[
  {
    "path": ".ci/.gitattributes",
    "content": "# Declare shell files to have LF endings on checkout\n# On Windows, the default git setting for `core.autocrlf`\n# means that when checking out code, LF endings get converted\n# to CRLF. This causes problems for shell scripts, as bash\n# gets choked up on the extra `\\r` character.\n*.sh text eol=lf\n"
  },
  {
    "path": ".ci/create-docs.yml",
    "content": "# These steps are only run on Linux\n\nsteps:\n  - script: \"esy '@doc' install\"\n    displayName: \"Install doc dependency\"\n    condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))\n\n  - script: \"esy '@doc' build\"\n    displayName: \"Build docs\"\n    condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))\n\n  - script: echo '##vso[task.setvariable variable=docsPath]'$(esy '@doc' echo '#{self.target_dir}/default/_doc/_html')\n    displayName: \"Save docsPath in variable\"\n    condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))\n\n  - task: PublishBuildArtifacts@1\n    displayName: \"Publish Artifact: Docs\"\n    condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))\n    inputs:\n      PathtoPublish: $(docsPath)\n      ArtifactName: Docs\n"
  },
  {
    "path": ".ci/esy-bench.yml",
    "content": "# Run benchmarks\n\nsteps:\n  - script: esy @bench install\n    displayName: 'esy @bench install'\n  - script: esy @bench build\n    displayName: 'esy @bench build'\n  - script: esy @bench x ReveryBench\n    displayName: 'esy @bench x ReveryBench'\n"
  },
  {
    "path": ".ci/esy-build-steps.yml",
    "content": "# Cross-platform set of build steps for building esy projects\n\nsteps:\n  - script: npm install -g esy@0.6.7\n    displayName: 'npm install -g esy@0.6.7'\n  - script: esy install\n    displayName: 'esy install'\n  - script: esy build\n    displayName: 'esy build'\n  - script: esy @test install\n    displayName: 'esy @test install'\n  - script: esy @test run\n    displayName: 'esy @test run'\n  - script: esy @examples install\n    displayName: 'esy @examples install'\n  - script: esy @examples run-harfbuzz\n    displayName: 'esy @examples run-harfbuzz'\n  - script: esy @examples x SkiaCli\n    displayName: '@esy @examples x SkiaCli'\n  - script: esy @examples x SkiaCli.bc\n    displayName: '@esy @examples x SkiaCli.bc'\n  - script: esy @examples run-skia-fontmanager\n    displayName: 'esy @examples run-skia-fontmanager'\n  - script: esy @examples x ReveryTextWrapCli\n    displayName: 'esy @examples x ReveryTextWrapCli'\n  - script: esy @examples x ReveryTextWrapCli.bc\n    displayName: 'esy @examples x ReveryTextWrapCli.bc'\n"
  },
  {
    "path": ".ci/esy-check-hygiene.yml",
    "content": "# Cross-platform set of build steps for building esy projects\n\nsteps:\n  - script: npm install -g esy@0.6.7\n    displayName: 'npm install -g esy@0.6.7'\n  - script: esy install\n    displayName: 'esy install'\n  - script: git diff --exit-code\n    displayName: 'check that `esy.lock` is up-to-date. If this fails, commit `esy.lock` changes and re-submit PR.'\n  - script: esy @bench install\n    displayName: 'esy @bench install'\n  - script: git diff --exit-code\n    displayName: 'check that `bench.esy.lock` is up-to-date. If this fails, commit `bench.esy.lock` changes and re-submit PR.'\n  - script: esy @js install\n    displayName: 'esy @js install'\n  - script: git diff --exit-code\n    displayName: 'check that `js.esy.lock` is up-to-date. If this fails, commit `js.esy.lock` changes and re-submit PR.'\n  - script: esy @test install\n    displayName: 'esy @test install'\n  - script: git diff --exit-code\n    displayName: 'check that `test.esy.lock` is up-to-date. If this fails, commit `test.esy.lock` changes and re-submit PR.'\n  - script: esy @doc install\n    displayName: 'esy @doc install'\n  - script: git diff --exit-code\n    displayName: 'check that `doc.esy.lock` is up-to-date. If this fails, commit `doc.esy.lock` changes and re-submit PR.'\n  - script: esy build\n    displayName: 'esy build'\n  - script: esy format\n    displayName: esy format\n  - script: git diff --exit-code\n    displayName: 'check that formatting is correct. If this fails, run `esy format` and re-submit PR.'\n  - script: esy b dune build @check\n    displayName: 'esy b dune build @check'\n"
  },
  {
    "path": ".ci/format.sh",
    "content": "#!/usr/bin/env bash\nOS=$1\n\ndune build @fmt --auto-promote\n\ncaml_output=$?\n\nif [[ $OS == \"windows\" ]]\nthen\n    files=$(/usr/bin/find $(pwd) -type f \\( -iname \\*.c -o -iname \\*.h -o -iname \\*.cpp \\) -not -path \"*_esy/*\")\n    native_output=$(astyle -n -Q --style=java --s4 $(cygpath -w $files))\nelif [[ $OS == \"darwin\" ]]\nthen\n    native_output=$(find -E . -regex '.*\\.(c|h|cpp)' -type f -not -path \"*_esy/*\" -exec astyle -n -Q --style=java --s4 {} \\;)\nelif [[ $OS == \"linux\" ]]\nthen\n    native_output=$(find ./ -type f \\( -iname \\*.c -o -iname \\*.h -o -iname \\*.cpp \\) -not -path \"*_esy/*\" -exec astyle -n -Q --style=java --s4 {} \\;)\nfi\n\nif [[ $native_output ]]\nthen\n    printf \"\\nFormatted the following native stubs:\\n%s\\n\" \"$native_output\"\nfi\n\nif [[ $caml_output != 0 ]] || [[ $native_output != \"\" ]]\nthen\n    exit 1\nfi\n"
  },
  {
    "path": ".ci/publish-build-cache.yml",
    "content": "# Steps for publishing project cache\n\nsteps:\n  - bash: 'mkdir -p $(STAGING_DIRECTORY)'\n    condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master'))\n    displayName: '[Cache][Publish] Create cache directory'\n\n  - bash: |\n      cd $(ESY__CACHE_INSTALL_PATH)\n      pwd\n      STDIR=$STAGING_DIRECTORY\n      if [ \"$AGENT_OS\" == \"Windows_NT\" ]; then\n        STDIR=$( cygpath --unix --absolute \"$STAGING_DIRECTORY\")\n      fi\n      echo \"STDIR: $STDIR\"\n      tar -czf \"$STDIR/esy-cache.tar\" .\n    workingDirectory: ''\n    condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master'))\n    displayName: '[Cache][Publish] Tar esy cache directory'\n\n  # - bash: 'cd $(ESY__NPM_ROOT) && tar -czf $(STAGING_DIRECTORY)/npm-cache.tar .'\n  #   condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master'))\n  #   displayName: '[Cache][Publish] Tar npm cache directory'\n\n  - task: PublishBuildArtifacts@1\n    displayName: '[Cache][Publish] Upload tarball'\n    condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master'))\n    inputs:\n        pathToPublish: '$(STAGING_DIRECTORY)'\n        artifactName: 'cache-$(Agent.OS)-install'\n        parallel: true\n        parallelCount: 8\n"
  },
  {
    "path": ".ci/publish-release.yml",
    "content": "# Steps for publishing project cache\n\nsteps:\n  - task: PublishBuildArtifacts@1\n    displayName: '[Release]'\n    inputs:\n        pathToPublish: '_release'\n        artifactName: 'release-$(Agent.OS)'\n        parallel: true\n        parallelCount: 8\n"
  },
  {
    "path": ".ci/restore-build-cache.yml",
    "content": "# Steps for restoring project cache\n\nsteps:\n  - task: DownloadBuildArtifacts@0\n    condition: and(succeeded(), ne(variables['Build.SourceBranch'], 'refs/heads/master'))\n    displayName: '[Cache][Restore] Restore install'\n    inputs:\n        buildType: 'specific'\n        project: '$(System.TeamProject)'\n        pipeline: '$(Build.DefinitionName)'\n        branchName: 'refs/heads/master'\n        buildVersionToDownload: 'latestFromBranch'\n        downloadType: 'single'\n        artifactName: 'cache-$(Agent.OS)-install'\n        downloadPath: '$(STAGING_DIRECTORY)'\n    continueOnError: true\n\n  - bash: 'mkdir -p $(ESY__CACHE_INSTALL_PATH)'\n    condition: and(succeeded(), ne(variables['Build.SourceBranch'], 'refs/heads/master'))\n    displayName: '[Cache][Restore] Create cache directory'\n\n  # - bash: 'cd $(ESY__NPM_ROOT) && tar -xf $(STAGING_DIRECTORY)/cache-$(Agent.OS)-install/npm-cache.tar -C .'\n  #   continueOnError: true\n  #   condition: and(succeeded(), ne(variables['Build.SourceBranch'], 'refs/heads/master'))\n  #   displayName: '[Cache][Restore] Untar npm cache directory'\n\n  - bash: |\n      cd $(ESY__CACHE_INSTALL_PATH)\n      pwd\n      STDIR=$STAGING_DIRECTORY\n      if [ \"$AGENT_OS\" == \"Windows_NT\" ]; then\n        STDIR=$( cygpath --unix --absolute \"$STAGING_DIRECTORY\")\n      fi\n      echo \"STDIR: $STDIR\"\n      tar -xf \"$STDIR/cache-$(Agent.OS)-install/esy-cache.tar\" -C .\n    continueOnError: true\n    condition: and(succeeded(), ne(variables['Build.SourceBranch'], 'refs/heads/master'))\n    displayName: '[Cache][Restore] Untar esy cache directory'\n\n  - bash: 'rm -rf *'\n    continueOnError: true\n    workingDirectory: '$(STAGING_DIRECTORY)'\n    condition: and(succeeded(), ne(variables['Build.SourceBranch'], 'refs/heads/master'))\n    displayName: '[Cache][Restore] Clean up'\n"
  },
  {
    "path": ".ci/use-node.yml",
    "content": "steps:\n  - task: NodeTool@0\n    displayName: 'Use Node 12.x'\n    inputs:\n      versionSpec: 12.x\n"
  },
  {
    "path": ".gitattributes",
    "content": "# Declare shell files to have LF endings on checkout\n# On Windows, the default git setting for `core.autocrlf`\n# means that when checking out code, LF endings get converted\n# to CRLF. This causes problems for shell scripts, as bash\n# gets choked up on the extra `\\r` character.\n*.sh text eol=lf\n\n# Hide lockfile updates\n*esy.lock/* linguist-generated\n\n# Recognize all re files as Reason\n*.re linguist-language=Reason\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\nopen_collective: revery\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug Report\nabout: Create a report to help us improve\n---\n\n<!-- Please search existing issues to avoid creating duplicates. -->\n\n<!-- Also please test using the latest master build to make sure your issue has not already been fixed -->\n\n- Operating System:\n- Revery Version:\n- OCaml version:\n- Native, Bytecode, or JS build:\n\n<!-- \n    Please try to narrow down the code to a minimal repro.\n    This will speed up investigation time. In addition, including\n    a  link to the github repo, if available, will help!\n-->\n\n- Link to github repo:\n\n- Steps to reproduce:\n\n- Actual Result:\n- Expected Result:\n\n- Additional Information:\n\n- Search terms used:\n\n<!-- Please include the search terms you used when searching the issue tracker. This will make it easier for others to find! -->\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature Request\nabout: Suggest an idea for this project\n---\n\n<!-- Please search existing issues to avoid creating duplicates. -->\n\n<!-- Describe the feature you'd like. -->\n"
  },
  {
    "path": ".gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (https://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\n# missing trailing slash since it's a symlink with esy\nnode_modules\njspm_packages/\n\n# TypeScript v1 declaration files\ntypings/\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n\n# next.js build output\n.next\n\n# dune build folder\n_esy/\n\n# install files\n*.install\n\n**/.merlin\n\n#macOS\n.DS_Store\n\nskia-c-example.png\nskia-svg-example.png\nskia-font-manager-output.png\nArial.ttf\n\n.vscode\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as\ncontributors and maintainers pledge to making participation in our project and\nour community a harassment-free experience for everyone, regardless of age, body\nsize, disability, ethnicity, sex characteristics, gender identity and expression,\nlevel of experience, education, socio-economic status, nationality, personal\nappearance, race, religion, or sexual identity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment\ninclude:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or\n advances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic\n address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable\nbehavior and are expected to take appropriate and fair corrective action in\nresponse to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or\nreject comments, commits, code, wiki edits, issues, and other contributions\nthat are not aligned to this Code of Conduct, or to ban temporarily or\npermanently any contributor for other behaviors that they deem inappropriate,\nthreatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces\nwhen an individual is representing the project or its community. Examples of\nrepresenting a project or community include using an official project e-mail\naddress, posting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event. Representation of a project may be\nfurther defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported by contacting the project team at oss@outrunlabs.com. All\ncomplaints will be reviewed and investigated and will result in a response that\nis deemed necessary and appropriate to the circumstances. The project team is\nobligated to maintain confidentiality with regard to the reporter of an incident.\nFurther details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good\nfaith may face temporary or permanent repercussions as determined by other\nmembers of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,\navailable at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html\n\n[homepage]: https://www.contributor-covenant.org\n\nFor answers to common questions about this code of conduct, see\nhttps://www.contributor-covenant.org/faq\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2018 Bryan Phelps\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\">\n\t<a href=\"https://www.outrunlabs.com/revery\" title=\"Revery\">\n\t\t<img src=\"./assets/logo.png\" alt=\"Logo\">\n\t</a>\n</p>\n\n<p align=\"center\">\n  <span>Build <b>native</b>, <i>high-performance</i>, <b>cross-platform</b> desktop apps with <a href=\"https://reasonml.github.io\">reason!</a></span>\n</p>\n\n<p align=\"center\">\n  <a href=\"https://github.com/revery-ui/revery/actions\"><img src=\"https://github.com/revery-ui/revery/workflows/Validate%20Pull%20Request/badge.svg\" /></a>\n  <a href=\"https://dev.azure.com/revery-ui/revery/_build/latest?definitionId=2?branchName=master\">\n    <img src=\"https://dev.azure.com/revery-ui/revery/_apis/build/status/revery-ui.revery?branchName=master\" alt=\"Build Status\"/>\n  </a>\n  <a href=\"https://badge.fury.io/js/revery\">\n    <img src=\"https://badge.fury.io/js/revery.svg\" alt=\"npm version\"/>\n  </a>\n  <a href=\"https://discord.gg/4pxY5Cp\">\n    <img src=\"https://img.shields.io/discord/526111832478449695.svg\" alt=\"Join the chat on discord!\"/>\n  </a>\n  <a href=\"#backers\">\n    <img src=\"https://opencollective.com/revery/backers/badge.svg\" alt=\"Backers\"/>\n  </a>\n</p>\n\n---\n\n<p align=\"center\">\n\t<a href=\"https://www.outrunlabs.com/revery/playground\" title=\"Playground\">\n\t\t<img src=\"./assets/screenshot.png\" alt=\"Slider components\">\n\t</a>\n</p>\n\n:construction: __NOTE:__ Revery is a work-in-progress and in active development! :construction:\n\nTo get a taste of Revery, check out our JavaScript + WebGL build on the [playground](https://outrunlabs.com/revery/playground). For the best experience, though, you'll want to try a [native build](https://github.com/revery-ui/revery/wiki/Building-&-Installing).\n\n## Motivation\n\nToday, [Electron](https://electronjs.org/) is one of the most popular tools for building desktop apps - using an HTML, JS, CSS stack. However, it has a heavy footprint in terms of both RAM and CPU - __essentially packing an entire browser into the app.__ Even with that tradeoff, it has a lot of great aspects - it's the quickest way to build a cross-platform app & it provides a great development experience - as can be testified by its usage in popular apps like VSCode, Discord, and Slack.\n\nRevery is kind of like super-fast, _native code_ Electron - with bundled React-like/Redux-like libraries and a fast build system - all ready to go!\n\nRevery is built with [reasonml](https://reasonml.github.io), which is a javascript-like syntax on top of [OCaml](https://ocaml.org) This means that the language is accessible to JS developers.\n\nYour apps are compiled to native code with the Reason / OCaml toolchain - with __instant startup__ and __performance comparable to native C code.__ Revery features platform-accelerated, GPU-accelerated rendering. The compiler itself is fast, too!\n\nRevery is an experiment - can we provide a great developer experience and help teams be productive, without making sacrifices on performance?\n\n### Design Decisions\n\n- __Consistent cross-platform behavior__\n\nA major value prop of Electron is that you can build for all platforms at once. You have great confidence as a developer that your app will look and work the same across different platforms. Revery is the same - aside from platform-specific behavior, if your app looks or behaves differently on another platform, that's a bug! As a consequence, Revery is like [flutter](https://flutter.io) in that it __does not use native widgets__. This means more work for us, but also that we have more predictable functionality cross-platform!\n\n> __NOTE:__ If you're looking for something that does leverage native widgets, check out [briskml](https://github.com/briskml/brisk). Another alternative is the [cuite](https://github.com/let-def/cuite) OCaml binding for [Qt](https://github.com/let-def/cuite).\n\n- __High performance__\n\nPerformance should be at the forefront, and not a compromise - we need to develop and build benchmarks that help ensure top-notch performance and start-up time.\n\n- __Type-safe, functional code__\n\nWe might have some dirty mutable objects for performance - but our high-level API should be purely functional. You should be able to follow the React model of modelling your UI as a _pure function_ of application state -> UI.\n\n## Getting Started\n\n- Check out [revery-quick-start](https://github.com/revery-ui/revery-quick-start) to get up and running with your own Revery app!\n- Try out our [interactive playground](https://www.outrunlabs.com/revery/playground/)\n- Read through our [docs](https://www.outrunlabs.com/revery/api/revery/)\n\n## Contributing\n\nWe'd love your help, and welcome PRs and contributions.\n\nSome ideas for getting started:\n- [Build and run](https://github.com/revery-ui/revery/wiki/Building-&-Installing) Revery\n- View our [Roadmap](https://github.com/revery-ui/revery/wiki/Roadmap)\n- Help us improve our [documentation](https://github.com/revery-ui/revery/blob/master/src/index.mld)\n- Help us build [examples](https://github.com/revery-ui/revery/tree/master/examples)\n- Help us [fix bugs](https://github.com/revery-ui/revery/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22+label%3A%22bug%22) and [build features](https://github.com/revery-ui/revery/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22++-label%3Abug)\n- Help us [log bugs and open issues](https://github.com/bryphe/revery/issues/new)\n- Support the project on [OpenCollective](https://opencollective.com/revery)\n- Follow us on [Twitter](https://twitter.com/reveryui) or chat with us on [Discord](https://discord.gg/UvQ2cFn)!\n\n## License\n\nRevery is provided under the [MIT License](LICENSE).\n\nRevery bundles several dependencies under their own license terms - please refer to [ThirdPartyLicenses.txt](./ThirdPartyLicenses.txt).\n\n## Contributors\n\nThanks to everyone who has [contributed](https://github.com/revery-ui/revery/graphs/contributors) to Revery!\n\n## Backers\n\nThank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/revery#backer)]\n\n<a href=\"https://opencollective.com/revery#backers\" target=\"_blank\"><img src=\"https://opencollective.com/revery/backers.svg?width=890\"></a>\n\n## Built with Revery\n\n<p align=\"left\">\n  <a href=\"https://v2.onivim.io/\">\n    <img src=\"./assets/onivim-logo.png\" alt=\"Onivim 2\" />\n  </a>\n</p>\n\n\n## Special Thanks\n\n`revery` would not be possible without a bunch of cool tech:\n- [ocaml](https://ocaml.org) made these tools possible - thanks [Inria](http://gallium.inria.fr/) & [OCaml Labs](http://ocamllabs.io/)!\n- [reasonml](https://reasonml.github.io) made revery possible - thanks @jordwalke!\n- [flex](https://github.com/jordwalke/flex) by @jordwalke\n- [briskml](https://github.com/briskml)\n    - [brisk-reconciler](https://github.com/briskml/brisk-reconciler) - the \"native React\" implementation.\n- [reason-sdl2](https://github.com/revery-ui/reason-sdl2)\n    - [SDL2](https://www.libsdl.org)\n    - [stb-image](https://github.com/nothings/stb)\n- [reason-fontkit](https://github.com/bryphe/reason-fontkit)\n    - [freetype2](https://www.freetype.org)\n    - [harfbuzz](https://www.freedesktop.org/wiki/Software/HarfBuzz)\n- [reason-gl-matrix](https://github.com/bryphe/reason-gl-matrix)\n    - [gl-matrix](http://glmatrix.net)\n    - [glm](https://glm.g-truc.net/0.9.9/index.html)\n- [@reason-native/console](https://github.com/facebookexperimental/reason-native/tree/master/src/console)\n\n`revery` was inspired by some __awesome projects:__\n- [react-native](https://facebook.github.io/react-native/)\n- [ReactMini](https://github.com/reasonml/reason-react/tree/master/ReactMini)\n- [cuite](https://github.com/let-def/cuite)\n- [wall](https://github.com/let-def/wall)\n- [elm](https://elm-lang.org/)\n- [reprocessing](https://github.com/Schmavery/reprocessing)\n\n# Hot reload\n\nWe don't have a Hot Reload yet but it is on our roadmap. In the meantime, you can check branch [feat/hot-reload](https://github.com/revery-ui/revery/tree/feat/hot-reload) to see the progression.\n\nIn the meantime @mbernat has done a [script](https://gist.github.com/mbernat/abf651653c123374037c27377f41d0a0) that allow to relaunch the APP when the binary changed.\n"
  },
  {
    "path": "Revery.opam",
    "content": "opam-version: \"1.2\"\nversion: \"dev\"\nmaintainer: \"bryphe@outlook.com\"\nauthor: [\"Bryan Phelps\"]\nbuild: [\n\n]\n"
  },
  {
    "path": "ReveryBench.opam",
    "content": "opam-version: \"1.2\"\nversion: \"dev\"\nmaintainer: \"bryphe@outlook.com\"\nauthor: [\"Bryan Phelps\"]\nbuild: [\n\n]\n"
  },
  {
    "path": "ReveryExampleJs.opam",
    "content": "opam-version: \"1.2\"\nversion: \"dev\"\nmaintainer: \"bryphe@outlook.com\"\nauthor: [\"Bryan Phelps\"]\nbuild: [\n\n]\n"
  },
  {
    "path": "ReveryExamples.opam",
    "content": "opam-version: \"1.2\"\nversion: \"dev\"\nmaintainer: \"bryphe@outlook.com\"\nauthor: [\"Bryan Phelps\"]\nbuild: [\n\n]\n"
  },
  {
    "path": "ReveryTest.opam",
    "content": "opam-version: \"1.2\"\nversion: \"dev\"\nmaintainer: \"bryphe@outlook.com\"\nauthor: [\"Bryan Phelps\"]\nbuild: [\n\n]\n"
  },
  {
    "path": "ThirdPartyLicenses.txt",
    "content": "Revery\n\nTHIRD-PARTY SOFTWARE NOTICES AND INFORMATION\n\nThis project incorporates components from the projects listed below. The original copyright notices and the licenses under which Outrun Labs, LLC received such components are set forth below. Outrun Labs, LLC reserves all rights not expressly granted herein, whether by implication, estoppel or otherwise.\n\n1. MinCW Runtime License (https://sourceforge.net/p/mingw/mingw-org-wsl/ci/21762bb4a1bd0c88c38eead03f59e8d994349e83/tree/LICENSE)\n2. Roboto Font by Google (https://github.com/google/fonts/tree/master/apache/roboto)\n3. FontAwesome/Font-Awesome (https://github.com/FontAwesome/Font-Awesome)\n4. SDL2 (https://www.libsdl.org)\n5. Zed (https://github.com/ocaml-community/zed)\n\n%% MingW Runtime License NOTICES AND INFORMATION BEGIN HERE\n==============================================\nCopyright (c) 2012 MinGW.org project\n\nPermission is hereby granted, free of charge, to any person obtaining a\ncopy of this software and associated documentation files (the \"Software\"),\nto deal in the Software without restriction, including without limitation\nthe rights to use, copy, modify, merge, publish, distribute, sublicense,\nand/or sell copies of the Software, and to permit persons to whom the\nSoftware is furnished to do so, subject to the following conditions:\n\nThe above copyright notice, this permission notice and the below disclaimer\nshall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n==============================================\nEND of MingW Runtime License NOTICES AND INFORMATION\n\n%% Roboto Font NOTICES AND INFORMATION BEGIN HERE\n==============================================\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n==============================================\nEND of Roboto Font NOTICES AND INFORMATION\n\n%% FontAwesome NOTICES AND INFORMATION BEGIN HERE\n==============================================\n\nFont Awesome Free License\n-------------------------\n\nFont Awesome Free is free, open source, and GPL friendly. You can use it for\ncommercial projects, open source projects, or really almost whatever you want.\nFull Font Awesome Free license: https://fontawesome.com/license/free.\n\n# Icons: CC BY 4.0 License (https://creativecommons.org/licenses/by/4.0/)\nIn the Font Awesome Free download, the CC BY 4.0 license applies to all icons\npackaged as SVG and JS file types.\n\n# Fonts: SIL OFL 1.1 License (https://scripts.sil.org/OFL)\nIn the Font Awesome Free download, the SIL OLF license applies to all icons\npackaged as web and desktop font files.\n\n# Code: MIT License (https://opensource.org/licenses/MIT)\nIn the Font Awesome Free download, the MIT license applies to all non-font and\nnon-icon files.\n\n# Attribution\nAttribution is required by MIT, SIL OLF, and CC BY licenses. Downloaded Font\nAwesome Free files already contain embedded comments with sufficient\nattribution, so you shouldn't need to do anything additional when using these\nfiles normally.\n\nWe've kept attribution comments terse, so we ask that you do not actively work\nto remove them from files, especially code. They're a great way for folks to\nlearn about Font Awesome.\n\n# Brand Icons\nAll brand icons are trademarks of their respective owners. The use of these\ntrademarks does not indicate endorsement of the trademark holder by Font\nAwesome, nor vice versa. **Please do not use brand logos for any purpose except\nto represent the company, product, or service to which they refer.**\n\n==============================================\nEND of FontAwesome NOTICES AND INFORMATION\n\n%% SDL 2 NOTICES AND INFORMATION BEGIN HERE\n==============================================\nThis software is provided 'as-is', without any express or implied\nwarranty.  In no event will the authors be held liable for any damages\narising from the use of this software.\n\nPermission is granted to anyone to use this software for any purpose,\nincluding commercial applications, and to alter it and redistribute it\nfreely, subject to the following restrictions:\n\n1. The origin of this software must not be misrepresented; you must not\n   claim that you wrote the original software. If you use this software\n   in a product, an acknowledgment in the product documentation would be\n   appreciated but is not required.\n2. Altered source versions must be plainly marked as such, and must not be\n   misrepresented as being the original software.\n3. This notice may not be removed or altered from any source distribution.\n==============================================\nEND of SDL 2 NOTICES AND INFORMATION\n\n%% Zed NOTICES AND INFORMATION BEGIN HERE\n==============================================\nCopyright (c) 2011, Jeremie Dimino <jeremie@dimino.org>\nAll rights reserved.\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright\n      notice, this list of conditions and the following disclaimer.\n    * Redistributions in binary form must reproduce the above copyright\n      notice, this list of conditions and the following disclaimer in the\n      documentation and/or other materials provided with the distribution.\n    * Neither the name of Jeremie Dimino nor the names of his\n      contributors may be used to endorse or promote products derived\n      from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY\nEXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE AUTHOR AND CONTRIBUTORS BE LIABLE FOR ANY\nDIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\nON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n==============================================\nEND of Zed NOTICES AND INFORMATION\n\n%% <template> NOTICES AND INFORMATION BEGIN HERE\n==============================================\n\n==============================================\nEND of <template> NOTICES AND INFORMATION\n"
  },
  {
    "path": "azure-pipelines.yml",
    "content": "# Starter pipeline\n# Start with a minimal pipeline that you can customize to build and deploy your code.\n# Add steps that build, run tests, deploy, and more:\n# https://aka.ms/yaml\n\nname: $(Build.SourceVersion)\n\n# Master build triggers\ntrigger: \n- master\n\n# PR Triggers\npr:\n  autoCancel: true\n  branches:\n    include: \n    - master\n  paths:\n    exclude: \n    - README.md\n\njobs:\n- job: Linux\n  timeoutInMinutes: 0\n  pool:\n    vmImage: 'ubuntu-20.04'\n\n  variables:\n    STAGING_DIRECTORY: $(Build.StagingDirectory)\n    ESY__CACHE_INSTALL_PATH: /home/vsts/.esy/3_____________________________________________________________________/i\n    ESY__CACHE_SOURCE_TARBALL_PATH: /home/vsts/.esy/source/i\n    # ESY__NPM_ROOT: /opt/hostedtoolcache/node/8.14.0/x64/lib/node_modules/esy\n\n  steps:\n  - script: sudo apt-get update\n  - script: sudo apt-get install -y libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libgl1-mesa-dev libglu1-mesa-dev mesa-utils mesa-utils-extra ragel libgtk-3-dev nasm libxxf86vm-dev\n  - template: .ci/use-node.yml\n  - template: .ci/restore-build-cache.yml\n  - template: .ci/esy-build-steps.yml\n  - template: .ci/esy-bench.yml\n  - template: .ci/create-docs.yml\n  - template: .ci/publish-build-cache.yml\n\n- job: CentOS\n  displayName: 'Linux - CentOS - Docker Image'\n  timeoutInMinutes: 0\n  pool:\n    vmImage: 'ubuntu-20.04'\n\n  variables:\n    STAGING_DIRECTORY: $(Build.StagingDirectory)\n    ESY__CACHE_INSTALL_PATH: /home/vsts/.esy/3_____________________________________________________________________/i\n    ESY__CACHE_SOURCE_TARBALL_PATH: /home/vsts/.esy/source/i\n    # ESY__NPM_ROOT: /opt/hostedtoolcache/node/8.14.0/x64/lib/node_modules/esy\n\n  steps:\n  - script: docker build -t centos scripts/docker/centos\n    displayName: 'docker build'\n  - script: docker run --rm --mount src=`pwd`,target=/revery,type=bind centos /bin/bash -c 'cd revery && ls -a'\n  - script: docker run --cap-add SYS_ADMIN --device /dev/fuse --security-opt apparmor:unconfined --rm --mount src=`pwd`,target=/revery,type=bind centos /bin/bash -c 'cd revery && ./scripts/docker-build.sh'\n\n- job: Hygiene_Checks\n  displayName: 'Hygiene Checks'\n  timeoutInMinutes: 0\n  pool:\n    vmImage: 'macOS 10.14'\n\n  variables:\n    STAGING_DIRECTORY: $(Build.StagingDirectory)\n    ESY__CACHE_INSTALL_PATH: /Users/runner/.esy/3__________________________________________________________________/i\n    ESY__CACHE_SOURCE_TARBALL_PATH: /Users/runner/.esy/source/i\n    # ESY__NPM_ROOT: /usr/local/lib/node_modules/esy\n\n  steps:\n  - script: brew update-reset\n  - script: brew install libpng ragel\n  - script: node --version\n  - template: .ci/restore-build-cache.yml\n  - template: .ci/esy-check-hygiene.yml\n  - template: .ci/publish-build-cache.yml\n\n- job: MacOS\n  timeoutInMinutes: 0\n  pool:\n    vmImage: 'macOS 10.14'\n\n  variables:\n    STAGING_DIRECTORY: $(Build.StagingDirectory)\n    ESY__CACHE_INSTALL_PATH: /Users/runner/.esy/3__________________________________________________________________/i\n    ESY__CACHE_SOURCE_TARBALL_PATH: /Users/runner/.esy/source/i\n    # ESY__NPM_ROOT: /usr/local/lib/node_modules/esy\n\n  steps:\n  - script: brew update-reset\n  - script: brew install libpng ragel\n  - script: node --version\n  - template: .ci/restore-build-cache.yml\n  - template: .ci/esy-build-steps.yml\n  - template: .ci/esy-bench.yml\n  - template: .ci/publish-build-cache.yml\n\n- job: Windows\n  timeoutInMinutes: 0\n  pool:\n    vmImage: 'vs2017-win2016'\n\n  variables:\n    STAGING_DIRECTORY: $(Build.StagingDirectory)\n    ESY__CACHE_INSTALL_PATH: /C/Users/VssAdministrator/.esy/3______________________________________________________/i\n    ESY__CACHE_SOURCE_TARBALL_PATH: /C/Users/VssAdministrator/.esy/source/i\n    # ESY__NPM_ROOT: /C/npm/prefix/node_modules/esy\n\n  steps:\n  - template: .ci/use-node.yml\n  - template: .ci/restore-build-cache.yml\n  - template: .ci/esy-build-steps.yml\n  - template: .ci/esy-bench.yml\n  - template: .ci/publish-build-cache.yml\n"
  },
  {
    "path": "bench/exe/Bench.re",
    "content": "ReveryBench.BenchFramework.cli();\nSkia_Bench.BenchFramework.cli();\n"
  },
  {
    "path": "bench/exe/dune",
    "content": "(executable\n (name bench)\n (public_name ReveryBench)\n (libraries ReveryBench.lib Skia_Bench)\n (package ReveryBench))\n\n(install\n (section bin)\n (package ReveryBench)\n (files TestFont.ttf))\n"
  },
  {
    "path": "bench/lib/BenchFramework.re",
    "content": "include Reperf.Make({\n  let config = Reperf.Config.create(~snapshotDir=\"bench/__snapshots__\", ());\n});\n"
  },
  {
    "path": "bench/lib/DrawBench.re",
    "content": "open BenchFramework;\n\nopen Revery;\nopen Revery.Draw;\n\nlet options = Reperf.Options.create(~iterations=10000, ());\n\nlet setup = () => {\n  let surface = SurfaceUtility.makeSurface(800l, 600l) |> Option.get;\n  CanvasContext.createFromSurface(surface);\n};\n\nmodule Data = {\n  let testString = String.make(50, 'a') ++ String.make(50, 'X');\n  let testFallbackString = \"ABC鬼\";\n  let paint = {\n    let textPaint = Skia.Paint.make();\n    Skia.Paint.setTextEncoding(textPaint, GlyphId);\n    Skia.Paint.setLcdRenderText(textPaint, true);\n    Skia.Paint.setAntiAlias(textPaint, true);\n    Skia.Paint.setTextSize(textPaint, 20.);\n    textPaint;\n  };\n\n  let rectPaint = Skia.Paint.make();\n};\n\nlet drawText = canvasContext => {\n  let maybeSkia =\n    Revery.Font.Family.fromFile(\"TestFont.ttf\")\n    |> Revery.Font.Family.toSkia(Normal);\n  switch (Revery.Font.load(maybeSkia)) {\n  | Error(_) => failwith(\"Unable to load font!\")\n  | Ok(font) =>\n    Skia.Paint.setColor(Data.paint, Revery_Core.Color.toSkia(Colors.white));\n\n    let shapedText =\n      Data.testString\n      |> Revery.Font.shape(font)\n      |> Revery.Font.ShapeResult.getGlyphStrings;\n\n    shapedText\n    |> List.iter(((typeface, string)) => {\n         Skia.Paint.setTypeface(Data.paint, typeface);\n         CanvasContext.drawText(\n           ~x=1.,\n           ~y=1.,\n           ~paint=Data.paint,\n           ~text=string,\n           canvasContext,\n         );\n       });\n  };\n};\n\nlet drawFallbackText = canvasContext => {\n  let maybeSkia =\n    Revery.Font.Family.system(\"Arial\") |> Revery.Font.Family.toSkia(Normal);\n  switch (Revery.Font.load(maybeSkia)) {\n  | Error(_) => failwith(\"Unable to load font!\")\n  | Ok(font) =>\n    Skia.Paint.setColor(Data.paint, Revery_Core.Color.toSkia(Colors.white));\n\n    let shapedText =\n      Data.testFallbackString\n      |> Revery.Font.shape(font)\n      |> Revery.Font.ShapeResult.getGlyphStrings;\n\n    shapedText\n    |> List.iter(((typeface, string)) => {\n         Skia.Paint.setTypeface(Data.paint, typeface);\n         CanvasContext.drawText(\n           ~x=1.,\n           ~y=1.,\n           ~paint=Data.paint,\n           ~text=string,\n           canvasContext,\n         );\n       });\n  };\n};\n\nlet drawRect = canvasContext => {\n  Skia.Paint.setColor(Data.rectPaint, Revery.Color.toSkia(Colors.green));\n  CanvasContext.drawRectLtwh(\n    ~paint=Data.rectPaint,\n    ~left=1.,\n    ~top=1.,\n    ~width=10.,\n    ~height=20.,\n    canvasContext,\n  );\n};\n\nbench(~name=\"Draw: drawText\", ~options, ~setup, ~f=drawText, ());\nbench(\n  ~name=\"Draw: drawText (second iteration, cached)\",\n  ~options,\n  ~setup,\n  ~f=drawText,\n  (),\n);\nbench(\n  ~name=\"Draw: drawFallbackText\",\n  ~options,\n  ~setup,\n  ~f=drawFallbackText,\n  (),\n);\nbench(\n  ~name=\"Draw: drawFallbackText (second iteration, cached)\",\n  ~options,\n  ~setup,\n  ~f=drawFallbackText,\n  (),\n);\nbench(~name=\"Draw: drawRectLtwh\", ~options, ~setup, ~f=drawRect, ());\n"
  },
  {
    "path": "bench/lib/LayoutBench.re",
    "content": "open BenchFramework;\n\nopen Revery.UI;\n\nlet options = Reperf.Options.create(~iterations=10000, ());\n\nlet createNode = () => {\n  let _ = (new node)();\n  ();\n};\n\nlet layoutNode = (force: bool, n: node) => {\n  Layout.layout(~force, n);\n};\n\nbench(\n  ~name=\"Node: create single node\",\n  ~options,\n  ~setup=() => (),\n  ~f=createNode,\n  (),\n);\n\nbench(\n  ~name=\"Layout: layout single node (force re-layout)\",\n  ~options,\n  ~setup=\n    NodeUtility.setupNode(~style=Style.make(~width=100, ~height=100, ())),\n  ~f=layoutNode(true),\n  (),\n);\n\nbench(\n  ~name=\"Layout: layout node tree (4 deep, 4 wide) (force re-layout))\",\n  ~options,\n  ~setup=NodeUtility.setupNodeTree(~depth=4, ~breadth=4),\n  ~f=layoutNode(true),\n  (),\n);\n\nbench(\n  ~name=\"Layout: layout single node\",\n  ~options,\n  ~setup=\n    NodeUtility.setupNode(~style=Style.make(~width=100, ~height=100, ())),\n  ~f=layoutNode(false),\n  (),\n);\n\nbench(\n  ~name=\"Layout: layout node tree (4 deep, 4 wide)\",\n  ~options,\n  ~setup=NodeUtility.setupNodeTree(~depth=4, ~breadth=4),\n  ~f=layoutNode(false),\n  (),\n);\n"
  },
  {
    "path": "bench/lib/NodeUtility.re",
    "content": "open Revery.UI;\n\nlet setupNode = (~style, ()) => {\n  let n = (new node)();\n  n#setStyle(style);\n  n;\n};\n\nlet rec setupNodeTree =\n        (\n          ~depth: int,\n          ~breadth: int,\n          ~style=Style.make(~width=400, ~height=400, ()),\n          (),\n        ) => {\n  let i = ref(breadth);\n\n  let n = setupNode(~style, ());\n\n  while (i^ > 0 && depth > 0) {\n    let newNode = setupNodeTree(~depth=depth - 1, ~breadth, ());\n    n#addChild(newNode, 0);\n\n    decr(i);\n  };\n\n  n;\n};\n"
  },
  {
    "path": "bench/lib/PaintBench.re",
    "content": "open BenchFramework;\n\nopen Revery;\nopen Revery.Draw;\n\nlet options = Reperf.Options.create(~iterations=10000, ());\n\nmodule Data = {\n  let color = Colors.red;\n  let paint = Skia.Paint.make();\n};\n\nlet setPaintColor = () => {\n  let () = Skia.Paint.setColor(Data.paint, Revery.Color.toSkia(Data.color));\n  ();\n};\n\nbench(\n  ~name=\"Paint: Set color\",\n  ~options,\n  ~setup=() => (),\n  ~f=setPaintColor,\n  (),\n);\n"
  },
  {
    "path": "bench/lib/RecalculateBench.re",
    "content": "open BenchFramework;\n\nopen Revery.UI;\n\nlet options = Reperf.Options.create(~iterations=1000, ());\n\nlet recalculate = (n: node) => {\n  n#recalculate();\n};\n\nbench(\n  ~name=\"Recalculate: recalculate single node\",\n  ~options,\n  ~setup=\n    NodeUtility.setupNode(~style=Style.make(~width=100, ~height=100, ())),\n  ~f=recalculate,\n  (),\n);\n\nbench(\n  ~name=\"Recalculate: recalculate node tree (4 deep, 4 wide)\",\n  ~options,\n  ~setup=NodeUtility.setupNodeTree(~depth=4, ~breadth=4),\n  ~f=recalculate,\n  (),\n);\n"
  },
  {
    "path": "bench/lib/SurfaceUtility.re",
    "content": "open Skia;\n\nlet makeSurface = (width, height) => {\n  let imageInfo = Skia.ImageInfo.make(width, height, Rgba8888, Premul, None);\n  Surface.makeRaster(imageInfo, 0, None);\n};\n"
  },
  {
    "path": "bench/lib/ViewNodeBench.re",
    "content": "open BenchFramework;\n\nopen Revery.Draw;\nopen Revery.UI;\n\nlet options = Reperf.Options.create(~iterations=10000, ());\n\nlet createViewNode = () => {\n  let viewNode = (new viewNode)();\n  viewNode#recalculate();\n  viewNode;\n};\n\nlet setup = () => {\n  let surface = SurfaceUtility.makeSurface(800l, 600l) |> Option.get;\n  let canvas = CanvasContext.createFromSurface(surface);\n\n  NodeDrawContext.create(\n    ~dpi=1.0,\n    ~canvasScalingFactor=1.0,\n    ~debug=false,\n    ~canvas,\n    ~zIndex=0,\n    ~opacity=1.0,\n    (),\n  );\n};\n\nmodule Data = {\n  let viewNode = createViewNode();\n};\n\nlet draw = context => {\n  Data.viewNode#draw(context);\n};\n\nbench(~name=\"ViewNode: draw single node\", ~options, ~setup, ~f=draw, ());\n"
  },
  {
    "path": "bench/lib/dune",
    "content": "(library\n (name ReveryBench)\n (public_name ReveryBench.lib)\n (ocamlopt_flags -linkall)\n (libraries Revery reperf.lib))\n"
  },
  {
    "path": "bench.json",
    "content": "{\n  \"source\": \"./package.json\",\n  \"scripts\": {\n      \"run\": \"esy '@bench' x ReveryBench\"\n  },\n  \"override\": {\n      \"build\": [\"dune build -p reason-harfbuzz,reason-skia,reason-sdl2,Revery,ReveryBench -j4\"],\n      \"install\": [\n          \"esy-installer ReveryBench.install\"\n      ]\n  }\n}\n"
  },
  {
    "path": "doc.json",
    "content": "{\n  \"source\": \"./package.json\",\n  \"scripts\": {\n    \"run\": \"esy '@doc' x http-server #{self.target_dir}/default/_doc/_html\",\n    \"print\": \"esy '@doc' echo #{self.target_dir}/default/_doc/_html\"\n  },\n  \"override\": {\n      \"build\": [\"dune build @doc -p reason-harfbuzz,reason-skia,reason-sdl2,Revery -j4\"],\n      \"dependencies\": {\n\t  \"@opam/odoc\": \"*\",\n\t  \"http-server\": \"*\"\n\t}\n  }\n}\n"
  },
  {
    "path": "dune",
    "content": "(dirs src packages test examples bench)\n"
  },
  {
    "path": "dune-project",
    "content": "(lang dune 2.5)\n\n(formatting (enabled_for reason))\n"
  },
  {
    "path": "dune-workspace",
    "content": "(lang dune 2.0)\n(context (default\n (disable_dynamically_linked_foreign_archives true)))\n"
  },
  {
    "path": "examples/AnalogClock.re",
    "content": "open Revery;\nopen Revery.Math;\nopen Revery.UI;\n\nmodule AnalogClock = {\n  module DegreeUtils = {\n    type t = {\n      hourDegrees: float,\n      minuteDegrees: float,\n      secondDegrees: float,\n    };\n\n    let numberToPercentage = (~number, ~max) => number /. max *. 100.;\n    let percentageToDegrees = percentage => percentage *. 360. /. 100.;\n\n    let getDegreesFromTime = ({tm_hour, tm_min, tm_sec, _}: Unix.tm) => {\n      let hourDegrees =\n        numberToPercentage(~number=float_of_int(tm_hour), ~max=12.)\n        |> percentageToDegrees;\n\n      let minuteDegrees =\n        numberToPercentage(~number=float_of_int(tm_min), ~max=60.)\n        |> percentageToDegrees;\n\n      let secondDegrees =\n        numberToPercentage(~number=float_of_int(tm_sec), ~max=60.)\n        |> percentageToDegrees;\n\n      {hourDegrees, minuteDegrees, secondDegrees};\n    };\n  };\n\n  type action =\n    | UpdateTime;\n\n  type state = {currentTime: Unix.tm};\n\n  let reducer = (action, _state) => {\n    switch (action) {\n    | UpdateTime => {currentTime: Unix.time() |> Unix.localtime}\n    };\n  };\n\n  let%component make = () => {\n    let%hook (state, dispatch) =\n      Hooks.reducer(\n        ~initialState={currentTime: Unix.time() |> Unix.localtime},\n        reducer,\n      );\n\n    let {hourDegrees, minuteDegrees, secondDegrees}: DegreeUtils.t =\n      state.currentTime |> DegreeUtils.getDegreesFromTime;\n\n    let%hook () =\n      Hooks.effect(\n        OnMount,\n        () => {\n          let clear =\n            Tick.interval(\n              ~name=\"Clock Timer\",\n              _ => dispatch(UpdateTime),\n              Time.seconds(1),\n            );\n          Some(clear);\n        },\n      );\n\n    let containerStyle =\n      Style.[\n        alignItems(`Center),\n        bottom(0),\n        justifyContent(`Center),\n        left(0),\n        position(`Absolute),\n        right(0),\n        top(0),\n      ];\n\n    let clockContainer =\n      Style.[\n        border(~width=4, ~color=Colors.white),\n        height(300),\n        padding(15),\n        position(`Relative),\n        width(300),\n      ];\n\n    let clockPointer = Style.[position(`Absolute), left(150)];\n\n    let hourStyle =\n      Style.(\n        merge(\n          ~source=clockPointer,\n          ~target=[\n            backgroundColor(Colors.white),\n            height(90),\n            top(105),\n            transform([\n              Transform.Rotate(Angle.from_degrees(hourDegrees)),\n              Transform.TranslateY(-45.),\n            ]),\n            width(4),\n          ],\n        )\n      );\n\n    let minutesStyle =\n      Style.(\n        merge(\n          ~source=clockPointer,\n          ~target=[\n            backgroundColor(Colors.white),\n            height(120),\n            top(90),\n            transform([\n              Transform.Rotate(Angle.from_degrees(minuteDegrees)),\n              Transform.TranslateY(-60.),\n            ]),\n            width(4),\n          ],\n        )\n      );\n\n    let secondsStyle =\n      Style.(\n        merge(\n          ~source=clockPointer,\n          ~target=[\n            backgroundColor(Colors.red),\n            height(150),\n            top(75),\n            transform([\n              Transform.Rotate(Angle.from_degrees(secondDegrees)),\n              Transform.TranslateY(-75.),\n            ]),\n            width(2),\n          ],\n        )\n      );\n\n    <View style=containerStyle>\n      <View style=clockContainer>\n        <View style=secondsStyle />\n        <View style=minutesStyle />\n        <View style=hourStyle />\n      </View>\n    </View>;\n  };\n};\n\nlet render = () => <AnalogClock />;\n"
  },
  {
    "path": "examples/Border.re",
    "content": "open Revery;\nopen Revery.UI;\n\nlet defaultStyle =\n  Style.[\n    backgroundColor(Color.multiplyAlpha(0.2, Colors.white)),\n    position(`Relative),\n    left(100),\n    top(100),\n    width(200),\n    height(200),\n    border(~width=15, ~color=Colors.red),\n    borderHorizontal(~width=8, ~color=Colors.blue),\n    borderTop(~width=15, ~color=Colors.red),\n    borderLeft(~width=30, ~color=Colors.green),\n    borderRadius(16.0),\n  ];\n\nlet innerStyle =\n  Style.[\n    backgroundColor(Colors.yellow),\n    position(`Relative),\n    left(0),\n    top(0),\n    width(30),\n    height(30),\n    borderHorizontal(~color=Colors.black, ~width=3),\n  ];\n\nlet textStyle = Style.[color(Colors.black)];\n\nlet render = () =>\n  <View style=defaultStyle>\n    <View style=innerStyle />\n    <Text style=textStyle fontSize=20. text=\"Inner Text\" />\n  </View>;\n"
  },
  {
    "path": "examples/Boxshadow.re",
    "content": "open Revery;\nopen Revery.UI;\n\nlet parentStyles =\n  Style.[\n    position(`Relative),\n    flexGrow(1),\n    alignItems(`Center),\n    justifyContent(`Center),\n    flexDirection(`Column),\n  ];\n\nlet blackShadow =\n  Style.[\n    backgroundColor(Colors.blue),\n    position(`Relative),\n    width(100),\n    height(100),\n    boxShadow(\n      ~yOffset=-10.,\n      ~xOffset=0.,\n      ~blurRadius=15.,\n      ~color=Colors.black,\n      ~spreadRadius=10.,\n    ),\n    marginVertical(30),\n  ];\n\nlet subtleBlackShadow =\n  Style.[\n    backgroundColor(Colors.teal),\n    position(`Relative),\n    width(100),\n    height(100),\n    boxShadow(\n      ~yOffset=1.,\n      ~xOffset=1.,\n      ~blurRadius=5.,\n      ~color=Color.rgba(0., 0., 0., 0.2),\n      ~spreadRadius=0.,\n    ),\n    marginVertical(30),\n  ];\n\nlet greenShadow =\n  Style.[\n    backgroundColor(Colors.red),\n    position(`Relative),\n    width(100),\n    height(100),\n    boxShadow(\n      ~yOffset=10.,\n      ~xOffset=-30.,\n      ~blurRadius=20.,\n      ~color=Colors.green,\n      ~spreadRadius=0.,\n    ),\n    marginVertical(30),\n  ];\n\nlet render = () =>\n  <View style=parentStyles>\n    <View style=blackShadow />\n    <View style=subtleBlackShadow />\n    <View style=greenShadow />\n  </View>;\n"
  },
  {
    "path": "examples/Calculator.re",
    "content": "open Revery;\nopen Revery.UI;\nopen Revery.UI.Components;\n\nmodule Row = {\n  let make = (~children, ()) => {\n    let style =\n      Style.[\n        flexDirection(`Row),\n        alignItems(`Stretch),\n        justifyContent(`Center),\n        flexGrow(1),\n      ];\n    <View style> ...children </View>;\n  };\n};\n\nmodule Column = {\n  let make = (~children, ()) => {\n    let style =\n      Style.[\n        flexDirection(`Column),\n        alignItems(`Stretch),\n        justifyContent(`Center),\n        backgroundColor(Colors.darkGrey),\n        flexGrow(1),\n      ];\n    <View style> ...children </View>;\n  };\n};\n\nmodule Button = {\n  let make =\n      (~fontFamily=Font.Family.default, ~contents: string, ~onClick, ()) => {\n    let clickableStyle =\n      Style.[\n        position(`Relative),\n        backgroundColor(Colors.lightGrey),\n        justifyContent(`Center),\n        alignItems(`Center),\n        flexGrow(1),\n        /* Min width */\n        width(125),\n        margin(10),\n      ];\n    let viewStyle =\n      Style.[\n        position(`Relative),\n        justifyContent(`Center),\n        alignItems(`Center),\n      ];\n    let textStyle =\n      Style.[color(Colors.black), textWrap(TextWrapping.NoWrap)];\n\n    <Clickable style=clickableStyle onClick>\n      <View style=viewStyle>\n        <Text style=textStyle fontFamily fontSize=32. text=contents />\n      </View>\n    </Clickable>;\n  };\n};\n\nmodule Display = {\n  let make = (~display: string, ~curNum: string, ()) => {\n    let viewStyle =\n      Style.[\n        backgroundColor(Colors.white),\n        height(120),\n        flexDirection(`Column),\n        alignItems(`Stretch),\n        justifyContent(`FlexStart),\n        flexGrow(2),\n      ];\n    let displayStyle = Style.[color(Colors.black), margin(15)];\n    let numStyle = Style.[color(Colors.black), margin(15)];\n\n    <View style=viewStyle>\n      <Text style=displayStyle fontSize=20. text=display />\n      <Text style=numStyle fontSize=32. text=curNum />\n    </View>;\n  };\n};\n\ntype operator = [ | `Nop | `Add | `Sub | `Mul | `Div];\n\nlet showFloat = float => {\n  let string = string_of_float(float);\n  if (String.length(string) > 1 && string.[String.length(string) - 1] == '.') {\n    String.sub(string, 0, String.length(string) - 1);\n  } else {\n    string;\n  };\n};\ntype state = {\n  operator, /* Current operator being applied */\n  result: float, /* The actual numerical result */\n  display: string, /* The equation displayed */\n  number: string /* Current number being typed */\n};\n\ntype action =\n  | BackspaceKeyPressed\n  | ClearKeyPressed(bool) /* true = AC pressed */\n  | DotKeyPressed\n  | NumberKeyPressed(string)\n  | OperationKeyPressed(operator)\n  | PlusMinusKeyPressed\n  | ResultKeyPressed;\n\nlet eval = (state, newOp) => {\n  /* Figure out what the string for the next operation will be */\n  let newOpString =\n    switch (newOp) {\n    | `Nop => \"\"\n    | `Add => \"+\"\n    | `Sub => \"-\"\n    | `Mul => \"×\"\n    | `Div => \"÷\"\n    };\n  /* Split the current display on ! and get everything after (to clear errors) */\n  let partitionedDisplay = String.split_on_char('!', state.display);\n  let display =\n    List.nth(partitionedDisplay, List.length(partitionedDisplay) - 1);\n  let (newDisplay, newResult) =\n    switch (state.operator) {\n    | #operator when state.number == \"\" => (\n        \"Error: Can't evaluate binary operator without input!\",\n        state.result,\n      )\n    | `Nop => (state.number ++ newOpString, float_of_string(state.number))\n    | `Add => (\n        display ++ state.number ++ newOpString,\n        state.result +. float_of_string(state.number),\n      )\n    | `Sub => (\n        display ++ state.number ++ newOpString,\n        state.result -. float_of_string(state.number),\n      )\n    | `Mul => (\n        display ++ state.number ++ newOpString,\n        state.result *. float_of_string(state.number),\n      )\n    | `Div =>\n      if (float_of_string(state.number) != 0.) {\n        (\n          display ++ state.number ++ newOpString,\n          state.result /. float_of_string(state.number),\n        );\n      } else {\n        (\"Error: Divide by zero!\", state.result);\n      }\n    };\n  (newResult, newDisplay);\n};\n\nlet reducer = (action, state) =>\n  switch (action) {\n  | BackspaceKeyPressed =>\n    state.number == \"\"\n      ? state\n      : {\n        ...state,\n        number: String.sub(state.number, 0, String.length(state.number) - 1),\n      }\n  | ClearKeyPressed(ac) =>\n    ac\n      ? {operator: `Nop, result: 0., display: \"\", number: \"\"}\n      : {...state, number: \"\"}\n  | DotKeyPressed =>\n    String.contains(state.number, '.')\n      ? state : {...state, number: state.number ++ \".\"}\n  | NumberKeyPressed(n) => {...state, number: state.number ++ n}\n  | OperationKeyPressed(o) =>\n    let (result, display) = eval(state, o);\n    {operator: o, result, display, number: \"\"};\n  | PlusMinusKeyPressed =>\n    if (state.number != \"\" && state.number.[0] == '-') {\n      {\n        ...state,\n        number: String.sub(state.number, 1, String.length(state.number) - 1),\n      };\n    } else {\n      {...state, number: \"-\" ++ state.number};\n    }\n  | ResultKeyPressed =>\n    let (result, display) = eval(state, `Nop);\n    {operator: `Nop, result, display, number: showFloat(result)};\n  };\n\nmodule KeyboardInput = {\n  type state = {\n    ref: option(node),\n    hasFocus: bool,\n  };\n\n  type action =\n    | Focused(bool)\n    | SetRef(node);\n\n  let reducer = (action, state) =>\n    switch (action) {\n    | Focused(v) => {...state, hasFocus: v}\n    | SetRef(v) => {...state, ref: Some(v)}\n    };\n\n  let%component make = (~dispatch as parentDispatch, ()) => {\n    let%hook (v, dispatch) =\n      Hooks.reducer(~initialState={ref: None, hasFocus: false}, reducer);\n\n    let%hook () =\n      Hooks.effect(\n        Always,\n        () => {\n          if (!v.hasFocus) {\n            switch (v.ref) {\n            | Some(v) => Focus.focus(v)\n            | None => ()\n            };\n          };\n          None;\n        },\n      );\n\n    let onBlur = () => {\n      dispatch(Focused(false));\n    };\n\n    let onFocus = () => {\n      dispatch(Focused(true));\n    };\n\n    let respondToKeys = (event: NodeEvents.keyEventParams) => {\n      Key.Keycode.(\n        switch (event.keycode) {\n        | v when v == backspace => parentDispatch(BackspaceKeyPressed)\n        | v when v == c && event.ctrlKey =>\n          parentDispatch(ClearKeyPressed(true))\n        | v when v == c => parentDispatch(ClearKeyPressed(false))\n\n        /* + key */\n        | v when v == Key.Keycode.equals && event.shiftKey =>\n          parentDispatch(OperationKeyPressed(`Add))\n        | v when v == Key.Keycode.equals && event.ctrlKey =>\n          parentDispatch(PlusMinusKeyPressed)\n        | v when v == Key.Keycode.minus =>\n          parentDispatch(OperationKeyPressed(`Sub))\n        /* * key */\n        | v when v == digit8 && event.shiftKey =>\n          parentDispatch(OperationKeyPressed(`Mul))\n        | v when v == slash => parentDispatch(OperationKeyPressed(`Div))\n        | v when v == period => parentDispatch(DotKeyPressed)\n        | v when v == equals => parentDispatch(ResultKeyPressed)\n\n        | v when v == digit0 => parentDispatch(NumberKeyPressed(\"0\"))\n        | v when v == digit1 => parentDispatch(NumberKeyPressed(\"1\"))\n        | v when v == digit2 => parentDispatch(NumberKeyPressed(\"2\"))\n        | v when v == digit3 => parentDispatch(NumberKeyPressed(\"3\"))\n        | v when v == digit4 => parentDispatch(NumberKeyPressed(\"4\"))\n        | v when v == digit5 => parentDispatch(NumberKeyPressed(\"5\"))\n        | v when v == digit6 => parentDispatch(NumberKeyPressed(\"6\"))\n        | v when v == digit7 => parentDispatch(NumberKeyPressed(\"7\"))\n        | v when v == digit8 => parentDispatch(NumberKeyPressed(\"8\"))\n        | v when v == digit9 => parentDispatch(NumberKeyPressed(\"9\"))\n\n        | _ => ()\n        }\n      );\n    };\n\n    <View\n      ref={r => dispatch(SetRef(r))}\n      onBlur\n      onFocus\n      style=Style.[position(`Absolute), width(1), height(1)]\n      onKeyDown=respondToKeys\n    />;\n  };\n};\n\nmodule Calculator = {\n  let%component make = () => {\n    let%hook ({display, number, _}, dispatch) =\n      Hooks.reducer(\n        ~initialState={operator: `Nop, result: 0., display: \"\", number: \"\"},\n        reducer,\n      );\n\n    <Column>\n      <KeyboardInput dispatch />\n      <Display display curNum=number />\n      <Row>\n        <Button\n          contents=\"AC\"\n          onClick={_ => dispatch(ClearKeyPressed(true))}\n        />\n        <Button\n          contents=\"C\"\n          onClick={_ => dispatch(ClearKeyPressed(false))}\n        />\n        <Button contents=\"±\" onClick={_ => dispatch(PlusMinusKeyPressed)} />\n        /* TODO: Switch to a font with a backspace character */\n        <Button\n          fontFamily={Font.Family.fromFile(\"FontAwesome5FreeSolid.otf\")}\n          contents={||}\n          onClick={_ => dispatch(BackspaceKeyPressed)}\n        />\n      </Row>\n      <Row>\n        <Button\n          contents=\"7\"\n          onClick={_ => dispatch(NumberKeyPressed(\"7\"))}\n        />\n        <Button\n          contents=\"8\"\n          onClick={_ => dispatch(NumberKeyPressed(\"8\"))}\n        />\n        <Button\n          contents=\"9\"\n          onClick={_ => dispatch(NumberKeyPressed(\"9\"))}\n        />\n        <Button\n          contents=\"÷\"\n          onClick={_ => dispatch(OperationKeyPressed(`Div))}\n        />\n      </Row>\n      <Row>\n        <Button\n          contents=\"4\"\n          onClick={_ => dispatch(NumberKeyPressed(\"4\"))}\n        />\n        <Button\n          contents=\"5\"\n          onClick={_ => dispatch(NumberKeyPressed(\"5\"))}\n        />\n        <Button\n          contents=\"6\"\n          onClick={_ => dispatch(NumberKeyPressed(\"6\"))}\n        />\n        <Button\n          contents=\"×\"\n          onClick={_ => dispatch(OperationKeyPressed(`Mul))}\n        />\n      </Row>\n      <Row>\n        <Button\n          contents=\"1\"\n          onClick={_ => dispatch(NumberKeyPressed(\"1\"))}\n        />\n        <Button\n          contents=\"2\"\n          onClick={_ => dispatch(NumberKeyPressed(\"2\"))}\n        />\n        <Button\n          contents=\"3\"\n          onClick={_ => dispatch(NumberKeyPressed(\"3\"))}\n        />\n        <Button\n          contents=\"-\"\n          onClick={_ => dispatch(OperationKeyPressed(`Sub))}\n        />\n      </Row>\n      <Row>\n        <Button contents=\".\" onClick={_ => dispatch(DotKeyPressed)} />\n        <Button\n          contents=\"0\"\n          onClick={_ => dispatch(NumberKeyPressed(\"0\"))}\n        />\n        <Button contents=\"=\" onClick={_ => dispatch(ResultKeyPressed)} />\n        <Button\n          contents=\"+\"\n          onClick={_ => dispatch(OperationKeyPressed(`Add))}\n        />\n      </Row>\n    </Column>;\n  };\n};\n\nlet render = _ => <Calculator />;\n"
  },
  {
    "path": "examples/CanQuitExample.re",
    "content": "open Revery;\nopen Revery.UI;\nopen Revery.UI.Components;\n\nmodule CanQuit = {\n  type checkboxState = {canQuit: bool};\n\n  let%component make = () => {\n    let initialCheckboxState = {canQuit: true};\n    let%hook ({canQuit}, setCheckboxState) =\n      Hooks.state(initialCheckboxState);\n\n    <View\n      style=Style.[\n        width(500),\n        height(500),\n        justifyContent(`Center),\n        alignItems(`Center),\n      ]>\n      <Checkbox\n        checkedColor=Colors.green\n        onChange={() => {\n          let win = getActiveWindow();\n          switch (win) {\n          | Some(win) => Window.setCanQuitCallback(win, () => !canQuit)\n          | None => ()\n          };\n          setCheckboxState(({canQuit}) => {canQuit: !canQuit});\n        }}\n        style=Style.[border(~width=2, ~color=Colors.green)]\n        checked=canQuit\n      />\n      <Text\n        text={\"Can quit: \" ++ (canQuit ? \"Yes\" : \"No\")}\n        fontSize=20.\n        style=Style.[marginTop(10)]\n      />\n    </View>;\n  };\n};\n\nlet render = () => {\n  <CanQuit />;\n};\n"
  },
  {
    "path": "examples/CanvasExample.re",
    "content": "open Revery;\nopen Revery.Font;\nopen Revery.Draw;\nopen Revery.UI;\n\nlet containerStyle =\n  Style.[\n    position(`Absolute),\n    top(0),\n    bottom(0),\n    left(0),\n    right(0),\n    alignItems(`Center),\n    justifyContent(`Center),\n    flexDirection(`Column),\n  ];\n\nlet outerBox =\n  Style.[width(450), height(450), backgroundColor(Colors.black)];\n\nmodule Sample = {\n  let make = () => {\n    <View style=containerStyle>\n      <Canvas\n        style=outerBox\n        render={(canvasContext, _dimensions) => {\n          let transform = Skia.Matrix.make();\n          Skia.Matrix.setIdentity(transform);\n          CanvasContext.concat(transform, canvasContext);\n\n          let paint = Skia.Paint.make();\n          Skia.Paint.setColor(\n            paint,\n            Skia.Color.makeArgb(0xFFl, 0xFFl, 0x00l, 0x00l),\n          );\n\n          let rect = Skia.Rect.makeLtrb(1.0, 1.0, 101., 201.);\n          CanvasContext.drawRect(~rect, ~paint, canvasContext);\n\n          let stroke = Skia.Paint.make();\n          Skia.Paint.setColor(\n            stroke,\n            Skia.Color.makeArgb(0xFFl, 0xFFl, 0xFFl, 0x00l),\n          );\n          Skia.Paint.setAntiAlias(stroke, true);\n          Skia.Paint.setStyle(stroke, Stroke);\n          Skia.Paint.setStrokeWidth(stroke, 2.);\n\n          let innerPath = Skia.Path.make();\n          Skia.Path.lineTo(innerPath, 5., 5.);\n          Skia.Path.lineTo(innerPath, 15., -5.);\n          Skia.Path.lineTo(innerPath, 20., 0.);\n\n          let translate = Skia.Matrix.makeScale(20., 20., 20., 20.);\n          let pathEffect =\n            Skia.PathEffect.create2dPath(~matrix=translate, innerPath);\n\n          Skia.Paint.setPathEffect(stroke, pathEffect);\n\n          let path = Skia.Path.make();\n          Skia.Path.addCircle(path, 125., 125., ~radius=100., ());\n          CanvasContext.drawPath(~path, ~paint=stroke, canvasContext);\n\n          let maybeSkia =\n            Revery_Font.Family.fromFile(\"Roboto-Regular.ttf\")\n            |> Revery_Font.Family.toSkia(Revery_Font.Weight.Normal);\n          switch (Revery_Font.load(maybeSkia)) {\n          | Error(_) => ()\n          | Ok(font) =>\n            let textPaint = Skia.Paint.make();\n            Skia.Paint.setColor(textPaint, Color.toSkia(Colors.white));\n            Skia.Paint.setLcdRenderText(textPaint, true);\n            Skia.Paint.setAntiAlias(textPaint, true);\n            Skia.Paint.setTextSize(textPaint, 20.);\n\n            let shapedText =\n              \"Hello, World\"\n              |> FontCache.shape(font)\n              |> ShapeResult.getGlyphStrings;\n\n            Skia.Paint.setTextEncoding(textPaint, GlyphId);\n\n            shapedText\n            |> List.iter(((typeface, string)) => {\n                 Skia.Paint.setTypeface(textPaint, typeface);\n                 CanvasContext.drawText(\n                   ~paint=textPaint,\n                   ~x=10.0,\n                   ~y=20.0,\n                   ~text=string,\n                   canvasContext,\n                 );\n               });\n          };\n\n          let fill = Skia.Paint.make();\n          Skia.Paint.setColor(\n            fill,\n            Skia.Color.makeArgb(0xFFl, 0xFFl, 0x00l, 0x00l),\n          );\n          let rect = Skia.Rect.makeLtrb(200., 150., 250., 300.);\n          CanvasContext.drawOval(~rect, ~paint=fill, canvasContext);\n          let red = Skia.Color.makeArgb(0xFFl, 0xFFl, 0x00l, 0x00l);\n          let blue = Skia.Color.makeArgb(0xFFl, 0x00l, 0x00l, 0xFFl);\n          // Test out some layers...\n\n          let layer =\n            CanvasContext.createLayer(\n              ~width=128l,\n              ~height=128l,\n              canvasContext,\n            )\n            |> Option.get;\n\n          // Draw a circle onto our new layer...\n          let paint = Skia.Paint.make();\n          let linearGradient =\n            Skia.Shader.makeLinearGradient2(\n              ~startPoint=Skia.Point.make(16., 16.0),\n              ~stopPoint=Skia.Point.make(48.0, 48.0),\n              ~startColor=red,\n              ~stopColor=blue,\n              ~tileMode=`clamp,\n            );\n          Skia.Paint.setShader(paint, linearGradient);\n          Skia.Paint.setColor(\n            paint,\n            Skia.Color.makeArgb(0xFFl, 0x00l, 0xFFl, 0x00l),\n          );\n\n          CanvasContext.drawCircle(\n            ~x=32.,\n            ~y=32.,\n            ~radius=16.,\n            ~paint,\n            layer,\n          );\n\n          // And then draw the layer a bunch of places!\n          let paint = Skia.Paint.make();\n          CanvasContext.drawLayer(\n            ~paint,\n            ~layer,\n            ~x=300.,\n            ~y=300.,\n            canvasContext,\n          );\n          CanvasContext.drawLayer(\n            ~paint,\n            ~layer,\n            ~x=264.,\n            ~y=264.,\n            canvasContext,\n          );\n          CanvasContext.drawLayer(\n            ~paint,\n            ~layer,\n            ~x=264.,\n            ~y=300.,\n            canvasContext,\n          );\n          CanvasContext.drawLayer(\n            ~paint,\n            ~layer,\n            ~x=300.,\n            ~y=264.,\n            canvasContext,\n          );\n        }}\n      />\n    </View>;\n  };\n};\n\nlet render = () => <Sample />;\n"
  },
  {
    "path": "examples/CheckboxExample.re",
    "content": "open Revery;\nopen Revery.UI;\nopen Revery.UI.Components;\n\nmodule Check = {\n  type checkboxState = {\n    first: bool,\n    second: bool,\n  };\n\n  let getCheckboxText = checked => checked ? \"Checked!\" : \"Not Checked!\";\n\n  let%component make = () => {\n    let initialCheckboxState = {first: false, second: true};\n    let%hook ({first, second}, setCheckboxState) =\n      Hooks.state(initialCheckboxState);\n\n    <View\n      style=Style.[\n        width(500),\n        height(500),\n        justifyContent(`Center),\n        alignItems(`Center),\n      ]>\n      <Checkbox\n        checked=first\n        onChange={() => setCheckboxState(_ => {first: !first, second})}\n        style=Style.[marginBottom(10)]\n      />\n      <Text\n        text={getCheckboxText(first)}\n        fontSize=20.\n        style=Style.[marginBottom(10)]\n      />\n      <Checkbox\n        checkedColor=Colors.green\n        onChange={() => setCheckboxState(_ => {second: !second, first})}\n        style=Style.[border(~width=2, ~color=Colors.green)]\n        checked=second\n      />\n      <Text\n        text={\n          \"Default state: \" ++ getCheckboxText(initialCheckboxState.second)\n        }\n        fontSize=20.\n        style=Style.[marginTop(10)]\n      />\n    </View>;\n  };\n};\n\nlet render = () => {\n  <Check />;\n};\n"
  },
  {
    "path": "examples/DefaultButton.re",
    "content": "open Revery;\nopen Revery.UI;\nopen Revery.UI.Components;\n\nmodule DefaultButtonWithCounter = {\n  let%component make = () => {\n    let%hook (count, setCount) = Hooks.state(0);\n    let increment = () => setCount(_ => count + 1);\n\n    let containerStyle =\n      Style.[justifyContent(`Center), alignItems(`Center)];\n\n    let countContainer =\n      Style.[\n        width(300),\n        height(300),\n        alignItems(`Center),\n        justifyContent(`Center),\n      ];\n\n    let countStyle = Style.[margin(24), color(Colors.black)];\n\n    let countStr = string_of_int(count);\n\n    <View style=containerStyle>\n      <View style=countContainer>\n        <Text fontSize=50. style=countStyle text=countStr />\n      </View>\n      <Button title=\"click me!\" onClick=increment />\n      <Button disabled=true title=\"(disabled)\" onClick=increment />\n    </View>;\n  };\n};\n\nlet render = () => <View> <DefaultButtonWithCounter /> </View>;\n"
  },
  {
    "path": "examples/DropdownExample.re",
    "content": "open Revery.UI;\nopen Revery.UI.Components;\n\nlet containerStyle =\n  Style.[\n    position(`Absolute),\n    top(0),\n    bottom(0),\n    left(0),\n    right(0),\n    alignItems(`Center),\n    justifyContent(`Center),\n  ];\n\nlet textStyle = Style.[marginBottom(20)];\n\nmodule DropdownExample = {\n  let items: DropdownInt.items = [\n    {value: 1, label: \"First option\"},\n    {value: 2, label: \"Second option\"},\n    {value: 3, label: \"Third option\"},\n    {value: 4, label: \"Fourth option\"},\n    {value: 5, label: \"A really, really, really long option\"},\n  ];\n\n  let%component make = () => {\n    let%hook (selectedItem, setSelectedItem) =\n      Hooks.state(List.nth(items, 0));\n    <View style=containerStyle>\n      <Text\n        style=textStyle\n        fontSize=20.\n        text={\"Selected Item: \" ++ selectedItem.label}\n      />\n      <DropdownInt items onItemSelected={item => setSelectedItem(_ => item)} />\n    </View>;\n  };\n};\n\nlet render = () => <DropdownExample />;\n"
  },
  {
    "path": "examples/Examples.re",
    "content": "open Revery;\nopen ExampleStubs;\n\nmodule SliderExample = Slider;\nmodule ScrollViewExample = ScrollView;\n\nopen Revery.UI;\nopen Revery.UI.Components;\n\nlet bgColor = Color.hex(\"#212733\");\nlet activeBackgroundColor = Color.hex(\"#2E3440\");\nlet inactiveBackgroundColor = Color.hex(\"#272d39\");\nlet selectionHighlight = Color.hex(\"#90f7ff\");\n\ntype example = {\n  name: string,\n  render: Window.t => element,\n  source: string,\n};\n\nlet examples = [\n  {name: \"Animation\", render: _w => Hello.render(), source: \"Hello.re\"},\n  {\n    name: \"Spring\",\n    render: _w => SpringExample.render(),\n    source: \"SpringExample.re\",\n  },\n  {\n    name: \"CanQuit\",\n    render: _ => CanQuitExample.render(),\n    source: \"CanQuit.re\",\n  },\n  {\n    name: \"Button\",\n    render: _ => DefaultButton.render(),\n    source: \"DefaultButton.re\",\n  },\n  {\n    name: \"Checkbox\",\n    render: _ => CheckboxExample.render(),\n    source: \"CheckboxExample.re\",\n  },\n  {name: \"Slider\", render: _ => SliderExample.render(), source: \"Slider.re\"},\n  {name: \"Border\", render: _ => Border.render(), source: \"Border.re\"},\n  {\n    name: \"ScrollView\",\n    render: _ => ScrollViewExample.render(),\n    source: \"ScrollView.re\",\n  },\n  {\n    name: \"Calculator\",\n    render: w => Calculator.render(w),\n    source: \"Calculator.re\",\n  },\n  {name: \"Flexbox\", render: _ => Flexbox.render(), source: \"Flexbox.re\"},\n  {\n    name: \"Box Shadow\",\n    render: _ => Boxshadow.render(),\n    source: \"Boxshadow.re\",\n  },\n  {name: \"Focus\", render: _ => FocusExample.render(), source: \"Focus.re\"},\n  {name: \"Fonts\", render: _ => FontsExample.render(), source: \"Fonts.re\"},\n  {\n    name: \"Stopwatch\",\n    render: _ => Stopwatch.render(),\n    source: \"Stopwatch.re\",\n  },\n  {\n    name: \"Native: File(s)/Folders(s)\",\n    render: _ => NativeFileExample.render(),\n    source: \"NativeFileExample.re\",\n  },\n  {\n    name: \"Native: Notifications\",\n    render: _ => NativeNotificationExample.render(),\n    source: \"NativeNotificationExample.re\",\n  },\n  {\n    name: \"Native: Icon Features\",\n    render: w => NativeIconExample.render(w),\n    source: \"NativeIconExample.re\",\n  },\n  {\n    name: \"Native: OSX Menu\",\n    render: w => NativeMenuExample.render(w),\n    source: \"NativeMenuExample.re\",\n  },\n  {\n    name: \"Native: Inputs\",\n    render: _ => NativeInputExample.render(),\n    source: \"NativeInputExample.re\",\n  },\n  {\n    name: \"Input\",\n    render: _ => InputExample.render(),\n    source: \"InputExample.re\",\n  },\n  {\n    name: \"Radio Button\",\n    render: _ => RadioButtonExample.render(),\n    source: \"RadioButtonExample.re\",\n  },\n  {\n    name: \"Game Of Life\",\n    render: _ => GameOfLife.render(),\n    source: \"GameOfLife.re\",\n  },\n  {\n    name: \"Screen Capture\",\n    render: w => ScreenCapture.render(w),\n    source: \"ScreenCapture.re\",\n  },\n  {\n    name: \"Tree View\",\n    render: w => TreeView.render(w),\n    source: \"TreeView.re\",\n  },\n  {\n    name: \"Analog Clock\",\n    render: _ => AnalogClock.render(),\n    source: \"AnalogClock.re\",\n  },\n  {\n    name: \"TodoMVC\",\n    render: _ => TodoExample.render(),\n    source: \"TodoExample.re\",\n  },\n  {\n    name: \"Dropdown\",\n    render: _ => DropdownExample.render(),\n    source: \"DropdownExample.re\",\n  },\n  {\n    name: \"Text\",\n    render: _w => TextExample.render(),\n    source: \"TextExample.re\",\n  },\n  {\n    name: \"Hover Example\",\n    render: _ => HoverExample.render(),\n    source: \"HoverExample.re\",\n  },\n  {\n    name: \"Canvas Example\",\n    render: _ => CanvasExample.render(),\n    source: \"CanvasExample.re\",\n  },\n  {\n    name: \"SVG Example\",\n    render: _ => SVGExample.render(),\n    source: \"SVGExample.re\",\n  },\n  {\n    name: \"Zoom Example\",\n    render: _ => ZoomExample.render(),\n    source: \"ZoomExample.re\",\n  },\n  {\n    name: \"Nested Clickables\",\n    render: _ => NestedClickable.render(),\n    source: \"NestedClickable.re\",\n  },\n  {\n    name: \"File Drag & Drop\",\n    render: _ => FileDragAndDrop.render(),\n    source: \"FileDragAndDrop.re\",\n  },\n  {\n    name: \"Rich Text Example\",\n    render: _ => RichTextExample.render(),\n    source: \"RichTextExample.re\",\n  },\n  {\n    name: \"Shell: Open URL\",\n    render: _ => URLFileOpen.render(),\n    source: \"URLFileOpen.re\",\n  },\n  {\n    name: \"Window: Hit Tests/Zones\",\n    render: w => HitTests.render(w),\n    source: \"HitTests.re\",\n  },\n  {\n    name: \"Window: Control\",\n    render: w => WindowControl.render(w),\n    source: \"WindowControl.re\",\n  },\n  {\n    name: \"Markdown\",\n    render: _ => MarkdownExample.render(),\n    source: \"MarkdownExample.re\",\n  },\n  {\n    name: \"ImageQuality\",\n    render: _ => ImageQualityExample.render(),\n    source: \"ImageQualityExample.re\",\n  },\n  {\n    name: \"Layer\",\n    render: _ => LayerExample.render(),\n    source: \"LayerExample.re\",\n  },\n  {\n    name: \"WavFilePlayback\",\n    render: _ => WavFilePlaybackExample.render(),\n    source: \"WavFilePlaybackExample.re\",\n  },\n];\n\nlet getExampleByName = name =>\n  List.find(example => example.name == name, examples);\n\nlet noop = () => ();\n\nmodule ExampleButton = {\n  let make = (~isActive, ~name, ~onClick, ()) => {\n    let highlightColor =\n      isActive ? selectionHighlight : Colors.transparentWhite;\n\n    let buttonOpacity = 1.0;\n    let bgColor = isActive ? activeBackgroundColor : inactiveBackgroundColor;\n\n    let wrapperStyle =\n      Style.[\n        borderLeft(~width=4, ~color=highlightColor),\n        backgroundColor(bgColor),\n      ];\n\n    let textColor = isActive ? Colors.white : Colors.grey;\n    let textHeaderStyle = Style.[color(textColor), margin(16)];\n\n    <Opacity opacity=buttonOpacity>\n      <Clickable style=wrapperStyle onClick>\n        <Text style=textHeaderStyle text=name />\n      </Clickable>\n    </Opacity>;\n  };\n};\n\nmodule ExampleHost = {\n  let%component make = (~window, ~initialExample, ()) => {\n    let%hook (selectedExample, setSelectedExample) =\n      Hooks.state(getExampleByName(initialExample));\n\n    let renderButton = example => {\n      let isActive = example === selectedExample;\n      let onClick = _ => {\n        Window.setTitle(window, \"Revery Example - \" ++ example.name);\n\n        prerr_endline(\"SOURCE FILE: \" ++ example.source);\n        notifyExampleSwitched(example.source);\n        setSelectedExample(_ => example);\n      };\n\n      <ExampleButton isActive name={example.name} onClick />;\n    };\n\n    let buttons = List.map(renderButton, examples);\n\n    let exampleView = selectedExample.render(window);\n\n    <View\n      onMouseWheel={_evt => ()}\n      style=Style.[\n        position(`Absolute),\n        justifyContent(`Center),\n        alignItems(`Center),\n        backgroundColor(bgColor),\n        bottom(0),\n        top(0),\n        left(0),\n        right(0),\n        flexDirection(`Row),\n      ]>\n      <ScrollView\n        style=Style.[\n          position(`Absolute),\n          top(0),\n          left(0),\n          width(175),\n          bottom(0),\n          backgroundColor(bgColor),\n        ]>\n        <View> {buttons |> React.listToElement} </View>\n      </ScrollView>\n      <View\n        style=Style.[\n          position(`Absolute),\n          top(0),\n          left(175),\n          right(0),\n          bottom(0),\n          backgroundColor(activeBackgroundColor),\n        ]>\n        exampleView\n      </View>\n    </View>;\n  };\n};\n\nlet init = app => {\n  Revery.App.initConsole();\n\n  Timber.App.enable(Timber.Reporter.console());\n  Timber.App.setLevel(Timber.Level.perf);\n\n  App.onIdle(app, () => prerr_endline(\"Idle!\"))\n  |> (ignore: Revery.App.unsubscribe => unit);\n  App.onBeforeQuit(\n    app,\n    (_code: int) => {\n      prerr_endline(\"Quitting!\");\n      App.AllowQuit;\n    },\n  )\n  |> (ignore: Revery.App.unsubscribe => unit);\n\n  let initialExample = ref(\"Canvas Example\");\n  let decorated = ref(true);\n  let forceScaleFactor = ref(None);\n  let showFPSCounter = ref(false);\n  Arg.parse(\n    [\n      (\"--trace\", Unit(() => Timber.App.setLevel(Timber.Level.trace)), \"\"),\n      (\"--no-decoration\", Unit(() => decorated := false), \"\"),\n      (\"--example\", String(name => initialExample := name), \"\"),\n      (\n        \"--force-device-scale-factor\",\n        Float(scaleFactor => forceScaleFactor := Some(scaleFactor)),\n        \"\",\n      ),\n      (\"--show-fps\", Unit(() => showFPSCounter := true), \"\"),\n    ],\n    _ => (),\n    \"There is only --trace, --example, --no-decoration, --show-fps, and --force-device-scale-factor\",\n  );\n  let initialExample = initialExample^;\n\n  let maximized = Environment.webGL;\n\n  let windowWidth = 800;\n  let windowHeight = 480;\n\n  print_endline(\"Hello from example app\");\n\n  let window =\n    App.createWindow(\n      ~createOptions=\n        WindowCreateOptions.create(\n          ~width=windowWidth,\n          ~height=windowHeight,\n          ~maximized,\n          ~titlebarStyle=Transparent,\n          ~icon=Some(\"revery-icon.png\"),\n          ~decorated=decorated^,\n          ~forceScaleFactor=forceScaleFactor^,\n          ~acceleration=`Auto,\n          (),\n        ),\n      app,\n      \"Welcome to Revery!\",\n    );\n  if (showFPSCounter^) {\n    Window.showFPSCounter(window);\n  };\n\n  if (Environment.webGL) {\n    Window.maximize(window);\n  };\n\n  /* NOTE: If you want to use network-calls or other IO, uncomment this line */\n  // let _startEventLoop = Revery_Lwt.startEventLoop();\n\n  let _unsubscribe =\n    Window.onFocusGained(window, () => print_endline(\"Focus gained\"));\n  let _unsubscribe =\n    Window.onFocusLost(window, () => print_endline(\"Focus lost\"));\n  let _unsubscribe =\n    Window.onMaximized(window, () => print_endline(\"Maximized!\"));\n  let _unsubscribe =\n    Window.onFullscreen(window, () => print_endline(\"Fullscreen!\"));\n  let _unsubscribe =\n    Window.onMinimized(window, () => print_endline(\"Minimized!\"));\n  let _unsubscribe =\n    Window.onRestored(window, () => print_endline(\"Restored!\"));\n\n  let _unsubscribe =\n    Window.onSizeChanged(window, ({width, height}) =>\n      print_endline(Printf.sprintf(\"Size changed: %d x %d\", width, height))\n    );\n\n  let _unsubscribe =\n    Window.onMoved(window, ((x, y)) =>\n      print_endline(Printf.sprintf(\"Moved: %d x %d\", x, y))\n    );\n\n  print_endline(\n    Printf.sprintf(\"Operating system: %s\", Environment.osString),\n  );\n\n  let _renderFunction =\n    UI.start(window, <ExampleHost window initialExample />);\n  ();\n};\n\nPrintexc.record_backtrace(true);\nApp.start(init);\n"
  },
  {
    "path": "examples/FileDragAndDrop.re",
    "content": "open Revery;\nopen Revery.UI;\nopen Revery.UI.Components;\n\nmodule Styles = {\n  open Style;\n\n  let droppable = [\n    width(100),\n    height(100),\n    backgroundColor(Colors.blue),\n    justifyContent(`Center),\n    alignItems(`Center),\n  ];\n\n  let outer = [\n    position(`Absolute),\n    top(0),\n    bottom(0),\n    left(0),\n    right(0),\n    justifyContent(`Center),\n    alignItems(`Center),\n  ];\n\n  let text = Style.[color(Colors.white)];\n\n  let activeStyle = Style.[color(Colors.blue)];\n\n  let inactiveStyle = Style.[color(Colors.lightBlue)];\n};\n\nlet filesToText = files =>\n  List.map(\n    file =>\n      <ClickableText\n        onClick={_ => Native.Shell.openFile(file) |> ignore}\n        text=file\n        activeStyle=Styles.activeStyle\n        inactiveStyle=Styles.inactiveStyle\n      />,\n    files,\n  );\n\nlet%component dnd = () => {\n  let%hook (files: list(string), setFiles) = Hooks.state([]);\n\n  <View style=Styles.outer>\n    {React.listToElement(filesToText(files))}\n    <View style=Styles.droppable onFileDropped={e => setFiles(_ => e.paths)}>\n      <Text style=Styles.text text=\"Drop here!\" />\n    </View>\n  </View>;\n};\n\nlet render = () => <dnd />;\n"
  },
  {
    "path": "examples/Flexbox.re",
    "content": "open Revery;\nopen Revery.UI;\n\nlet parentWidth = 400;\nlet childWidth = 300;\n\nlet parentStyles =\n    (~alignment as a=`Auto, ~direction as d=`Row, ~justify as j=`Center, ()) =>\n  Style.[\n    backgroundColor(Colors.green),\n    position(`Relative),\n    width(parentWidth),\n    height(100),\n    alignItems(a),\n    justifyContent(j),\n    flexDirection(d),\n  ];\n\nlet childStyles =\n  Style.[\n    backgroundColor(Colors.blue),\n    position(`Relative),\n    width(childWidth),\n    height(40),\n  ];\n\nlet defaultTextStyles =\n  Style.[color(Colors.white), backgroundColor(Colors.blue)];\n\nlet parentColumnStyle = parentStyles(~direction=`Column);\nlet headerStyle =\n  Style.[marginTop(25), marginBottom(25), color(Colors.white)];\n\nlet horizontalStyles =\n  <View>\n    <Text style=headerStyle fontSize=30. text=\"Flex Direction Row\" />\n    <View style={parentColumnStyle(~alignment=`Auto, ())}>\n      <View style=childStyles>\n        <Text style=defaultTextStyles fontSize=20. text=\"Default Flex\" />\n      </View>\n    </View>\n    <View style={parentColumnStyle(~alignment=`Center, ())}>\n      <View style=childStyles>\n        <Text style=defaultTextStyles fontSize=20. text=\"Center\" />\n      </View>\n    </View>\n    <View style={parentColumnStyle(~alignment=`FlexStart, ())}>\n      <View style=childStyles>\n        <Text style=defaultTextStyles fontSize=20. text=\"Flex Start\" />\n      </View>\n    </View>\n    <View style={parentColumnStyle(~alignment=`FlexEnd, ())}>\n      <View style=childStyles>\n        <Text style=defaultTextStyles fontSize=20. text=\"Flex End\" />\n      </View>\n    </View>\n    <View style={parentColumnStyle(~alignment=`Stretch, ())}>\n      <View style=childStyles>\n        <Text style=defaultTextStyles fontSize=20. text=\"Flex Stretch\" />\n      </View>\n    </View>\n  </View>;\n\nlet verticalStyles =\n  <View>\n    <Text style=headerStyle fontSize=30. text=\"Flex Direction Column\" />\n    <View style={parentStyles(~direction=`Column, ~justify=`FlexStart, ())}>\n      <View style=childStyles>\n        <Text style=defaultTextStyles fontSize=20. text=\"Align Flex Start\" />\n      </View>\n    </View>\n    <View style={parentStyles(~direction=`Column, ~justify=`Center, ())}>\n      <View style=childStyles>\n        <Text style=defaultTextStyles fontSize=20. text=\"Align Flex Center\" />\n      </View>\n    </View>\n    <View style={parentStyles(~direction=`Column, ~justify=`FlexEnd, ())}>\n      <View style=childStyles>\n        <Text style=defaultTextStyles fontSize=20. text=\"Align Flex End\" />\n      </View>\n    </View>\n  </View>;\n\nlet render = () => <View> horizontalStyles verticalStyles </View>;\n"
  },
  {
    "path": "examples/FocusExample.re",
    "content": "open Revery;\nopen Revery.UI;\nopen Revery.UI.Components;\n\nmodule SimpleButton = {\n  let%component make = () => {\n    let%hook (count, setCount) = Hooks.state(0);\n    let%hook (focused, setFocus) = Hooks.state(false);\n\n    let increment = () => setCount(_ => count + 1);\n\n    let txt = focused ? \"Focused\" : \"Unfocused\";\n    let textContent = txt ++ \" me: \" ++ string_of_int(count);\n\n    <Clickable\n      onClick=increment\n      tabindex=0\n      onFocus={() => setFocus(_ => true)}\n      onBlur={() => setFocus(_ => false)}>\n      <View\n        style=Style.[\n          backgroundColor(Color.rgba(1., 1., 1., 0.1)),\n          border(~width=2, ~color=Colors.white),\n          margin(16),\n        ]>\n        <Text\n          fontSize=20.\n          style=Style.[color(Colors.white), margin(4)]\n          text=textContent\n        />\n      </View>\n    </Clickable>;\n  };\n};\n\nlet render = () =>\n  <View\n    style=Style.[\n      position(`Absolute),\n      justifyContent(`Center),\n      alignItems(`Center),\n      bottom(0),\n      top(0),\n      left(0),\n      right(0),\n    ]>\n    <SimpleButton />\n  </View>;\n"
  },
  {
    "path": "examples/FontsExample.re",
    "content": "open Revery;\nopen Revery.UI;\nopen Revery.UI.Components;\n\nmodule FontComponent = {\n  type state = {\n    family: string,\n    bold: bool,\n    italic: bool,\n  };\n\n  let initialState: state = {family: \"Arial\", bold: false, italic: false};\n\n  type actions =\n    | SetBold(bool)\n    | SetItalic(bool)\n    | SetFamily(string);\n\n  let reducer = (action: actions, state: state) => {\n    let state =\n      switch (action) {\n      | SetBold(v) => {...state, bold: v}\n      | SetItalic(v) => {...state, italic: v}\n      | SetFamily(v) => {...state, family: v}\n      };\n\n    state;\n  };\n\n  let%component make = () => {\n    let%hook (state, dispatch) = Hooks.reducer(~initialState, reducer);\n\n    let fontExample =\n      <Text\n        text=\"Lorem ipsum dolor sit amet\"\n        fontFamily={Font.Family.system(state.family)}\n        fontWeight={state.bold ? Font.Weight.Bold : Font.Weight.Normal}\n        italic={state.italic}\n        fontSize=24.\n      />;\n\n    <Container width=500 height=500>\n      <Center>\n        <Padding padding=10>\n          <Row>\n            <Input\n              placeholder=\"Type font name\"\n              onChange={(value, _) => dispatch(SetFamily(value))}\n              value={state.family}\n            />\n          </Row>\n        </Padding>\n        <Padding padding=10>\n          <Row>\n            <Padding padding=16>\n              <Checkbox\n                checked={state.bold}\n                onChange={() => dispatch(SetBold(!state.bold))}\n              />\n            </Padding>\n            <Center>\n              <Text text=\"Bold\" fontSize=20. style=Style.[width(150)] />\n            </Center>\n          </Row>\n        </Padding>\n        <Padding padding=10>\n          <Row>\n            <Padding padding=16>\n              <Checkbox\n                checked={state.italic}\n                onChange={() => dispatch(SetItalic(!state.italic))}\n              />\n            </Padding>\n            <Center>\n              <Text text=\"Italic\" fontSize=20. style=Style.[width(150)] />\n            </Center>\n          </Row>\n        </Padding>\n        <Padding padding=16> <Row> fontExample </Row> </Padding>\n      </Center>\n    </Container>;\n  };\n};\n\nlet render = () => {\n  <FontComponent />;\n};\n"
  },
  {
    "path": "examples/GameOfLife.re",
    "content": "open Revery;\nopen Revery.UI;\nopen Revery.UI.Components;\n\nmodule ViewPort = {\n  type t = {\n    xMin: int,\n    yMin: int,\n    xMax: int,\n    yMax: int,\n  };\n  type direction =\n    | North\n    | East\n    | South\n    | West;\n\n  let create = size => {xMin: 0, xMax: size, yMin: 0, yMax: size};\n\n  let changeDirection = (viewPort, direction) =>\n    switch (direction) {\n    | North => {...viewPort, yMin: viewPort.yMin - 1, yMax: viewPort.yMax - 1}\n    | East => {...viewPort, xMin: viewPort.xMin + 1, xMax: viewPort.xMax + 1}\n    | South => {...viewPort, yMin: viewPort.yMin + 1, yMax: viewPort.yMax + 1}\n    | West => {...viewPort, xMin: viewPort.xMin - 1, xMax: viewPort.xMax - 1}\n    };\n\n  let zoomOut = viewPort => {\n    xMin: viewPort.xMin - 1,\n    xMax: viewPort.xMax + 1,\n    yMin: viewPort.yMin - 1,\n    yMax: viewPort.yMax + 1,\n  };\n\n  let zoomIn = viewPort =>\n    if (viewPort.xMax > viewPort.xMin + 2) {\n      {\n        xMin: viewPort.xMin + 1,\n        xMax: viewPort.xMax - 1,\n        yMin: viewPort.yMin + 1,\n        yMax: viewPort.yMax - 1,\n      };\n    } else {\n      viewPort;\n    };\n};\n\nmodule Position = {\n  type t = (int, int);\n};\n\ntype cell =\n  | Alive\n  | Dead;\n\nmodule Universe = {\n  module T =\n    Map.Make({\n      type t = Position.t;\n      let compare = compare;\n    });\n  include T;\n  let universeFromList = positions =>\n    List.fold_left(\n      (acc, pos) => T.add(pos, Alive, acc),\n      T.empty,\n      positions,\n    );\n};\n\nmodule Examples = {\n  let blinker = Universe.universeFromList([(5, 4), (5, 5), (5, 6)]);\n  let pulsar =\n    Universe.universeFromList([\n      (4, 2),\n      (5, 2),\n      (6, 2),\n      (10, 2),\n      (11, 2),\n      (12, 2),\n      (2, 4),\n      (7, 4),\n      (9, 4),\n      (14, 4),\n      (2, 5),\n      (7, 5),\n      (9, 5),\n      (14, 5),\n      (2, 6),\n      (7, 6),\n      (9, 6),\n      (14, 6),\n      (4, 7),\n      (5, 7),\n      (6, 7),\n      (10, 7),\n      (11, 7),\n      (12, 7),\n      (4, 9),\n      (5, 9),\n      (6, 9),\n      (10, 9),\n      (11, 9),\n      (12, 9),\n      (2, 10),\n      (7, 10),\n      (9, 10),\n      (14, 10),\n      (2, 11),\n      (7, 11),\n      (9, 11),\n      (14, 11),\n      (2, 12),\n      (7, 12),\n      (9, 12),\n      (14, 12),\n      (4, 14),\n      (5, 14),\n      (6, 14),\n      (10, 14),\n      (11, 14),\n      (12, 14),\n    ]);\n};\n\nmodule GameOfLife = {\n  /* helper function for options */\n  let withDefault = (opt, ~default) =>\n    switch (opt) {\n    | Some(x) => x\n    | None => default\n    };\n\n  let findCell = (universe, position) =>\n    Universe.find_opt(position, universe)\n    |> withDefault(~default=Dead)\n    |> (cell => (position, cell));\n\n  let numberOfLive = neighbours =>\n    neighbours\n    |> List.filter(x =>\n         switch (x) {\n         | Alive => true\n         | _ => false\n         }\n       )\n    |> List.length;\n\n  type lifeCycle =\n    | Dies\n    | Revives\n    | Same;\n\n  let underPopulationRule = (cell, neighbours) =>\n    switch (cell) {\n    | Alive =>\n      if (numberOfLive(neighbours) < 2) {\n        Dies;\n      } else {\n        Same;\n      }\n\n    | Dead => Same\n    };\n\n  let livesOnRule = (cell, neighbours) =>\n    switch (cell) {\n    | Alive =>\n      let numberOfLiveNeighbours = numberOfLive(neighbours);\n\n      if (numberOfLiveNeighbours == 2 || numberOfLiveNeighbours == 3) {\n        Same;\n      } else {\n        Dies;\n      };\n\n    | Dead => Same\n    };\n\n  let overPopulationRule = (cell, neighbours) =>\n    switch (cell) {\n    | Alive =>\n      if (numberOfLive(neighbours) > 3) {\n        Dies;\n      } else {\n        Same;\n      }\n\n    | Dead => Same\n    };\n\n  let reproductionRule = (cell, neighbours) =>\n    switch (cell) {\n    | Alive => Same\n\n    | Dead =>\n      if (numberOfLive(neighbours) == 3) {\n        Revives;\n      } else {\n        Same;\n      }\n    };\n\n  let reduceLifeCycle = (cell, neighbours) => {\n    let actions = [\n      underPopulationRule(cell, neighbours),\n      livesOnRule(cell, neighbours),\n      overPopulationRule(cell, neighbours),\n      reproductionRule(cell, neighbours),\n    ];\n\n    let reducedLifeCycle =\n      actions |> List.filter((!=)(Same)) |> (l => List.nth_opt(l, 0));\n\n    withDefault(reducedLifeCycle, ~default=Same);\n  };\n\n  let applyRules = (cell, neighbours) => {\n    let action = reduceLifeCycle(cell, neighbours);\n    switch (action) {\n    | Dies => Dead\n    | Revives => Alive\n    | Same => cell\n    };\n  };\n\n  let neighbourPositions = ((x, y)) => [\n    (x - 1, y - 1),\n    (x, y - 1),\n    (x + 1, y - 1),\n    (x - 1, y),\n    (x + 1, y),\n    (x - 1, y + 1),\n    (x, y + 1),\n    (x + 1, y + 1),\n  ];\n  let evolveCell = (universe, (position, cell)) => {\n    let neighbours =\n      List.map(\n        position => findCell(universe, position) |> snd,\n        neighbourPositions(position),\n      );\n    let evolvedCell = applyRules(cell, neighbours);\n    (position, evolvedCell);\n  };\n\n  let evolve = universe => {\n    let find_relevant_cells = position =>\n      neighbourPositions(position) |> List.map(findCell(universe));\n    let keys = Universe.bindings(universe) |> List.map(fst);\n    keys\n    |> List.map(find_relevant_cells)\n    |> List.concat\n    |> List.sort_uniq(compare)\n    |> List.map(evolveCell(universe))\n    |> List.filter(x => snd(x) |> (==)(Alive))\n    |> List.map(fst)\n    |> Universe.universeFromList;\n  };\n};\n\nmodule Row = {\n  let style =\n    Style.[\n      flexDirection(`Row),\n      alignItems(`Stretch),\n      justifyContent(`Center),\n      backgroundColor(Colors.darkGrey),\n      flexGrow(1),\n    ];\n\n  let make = (~children, ()) => <View style> ...children </View>;\n};\n\nmodule Column = {\n  let style =\n    Style.[\n      flexDirection(`Column),\n      alignItems(`Stretch),\n      justifyContent(`Center),\n      flexGrow(1),\n    ];\n\n  let make = (~children, ()) => <View style> ...children </View>;\n};\n\nmodule Cell = {\n  let clickableStyle =\n    Style.[\n      position(`Relative),\n      backgroundColor(Colors.transparentWhite),\n      justifyContent(`Center),\n      alignItems(`Stretch),\n      flexGrow(1),\n      margin(0),\n    ];\n  let baseStyle =\n    Style.[\n      flexDirection(`Column),\n      alignItems(`Stretch),\n      justifyContent(`Center),\n      flexGrow(1),\n    ];\n  let aliveStyle =\n    Style.(merge(~source=baseStyle, ~target=[backgroundColor(Colors.red)]));\n  let deadStyle =\n    Style.(\n      merge(~source=baseStyle, ~target=[backgroundColor(Colors.black)])\n    );\n\n  let make = (~cell, ~onClick, ()) => {\n    let style =\n      switch (cell) {\n      | Alive => <View style=aliveStyle />\n      | Dead => <View style=deadStyle />\n      };\n    <Clickable style=clickableStyle onClick> style </Clickable>;\n  };\n};\n\nlet viewPortRender =\n    (viewPort: ViewPort.t, universe: Universe.t(cell), onClick) => {\n  let cell = pos =>\n    switch (Universe.find_opt(pos, universe)) {\n    | Some(_) => Alive\n    | None => Dead\n    };\n  let rec range = (min, max, result) =>\n    if (min == max) {\n      result;\n    } else {\n      range(min, max - 1, [max, ...result]);\n    };\n  let cols = range(viewPort.xMin, viewPort.xMax, []);\n  let rows = range(viewPort.yMin, viewPort.yMax, []);\n  List.map(\n    x =>\n      <Column>\n        {List.map(\n           y =>\n             <Row>\n               <Cell cell={cell((x, y))} onClick={() => onClick((x, y))} />\n             </Row>,\n           rows,\n         )\n         |> React.listToElement}\n      </Column>,\n    cols,\n  );\n};\n\ntype state = {\n  viewPort: ViewPort.t,\n  universe: Universe.t(cell),\n  isRunning: bool,\n  dispose,\n}\nand dispose = unit => unit;\n\ntype action =\n  | TimerTick(Time.t)\n  | StartTimer(dispose)\n  | StopTimer\n  | ToggleAlive(Position.t)\n  | MoveViewPort(ViewPort.direction)\n  | ZoomViewPort(zoom)\nand zoom =\n  | ZoomIn\n  | ZoomOut;\n\nlet noop = () => ();\nlet reducer = (action, state) =>\n  switch (action) {\n  | TimerTick(_t) => {...state, universe: GameOfLife.evolve(state.universe)}\n  | StartTimer(dispose) => {...state, dispose, isRunning: true}\n  | StopTimer =>\n    state.dispose();\n    {...state, dispose: noop, isRunning: false};\n  | ToggleAlive(position) => {\n      ...state,\n      universe:\n        Universe.update(\n          position,\n          cell =>\n            switch (cell) {\n            | Some(Alive) => None\n            | _ => Some(Alive)\n            },\n          state.universe,\n        ),\n    }\n  | MoveViewPort(direction) => {\n      ...state,\n      viewPort: ViewPort.changeDirection(state.viewPort, direction),\n    }\n  | ZoomViewPort(zoom) => {\n      ...state,\n      viewPort:\n        switch (zoom) {\n        | ZoomIn => ViewPort.zoomIn(state.viewPort)\n        | ZoomOut => ViewPort.zoomOut(state.viewPort)\n        },\n    }\n  };\n\nmodule GameOfLiveComponent = {\n  let controlsStyle = Style.[height(120), flexDirection(`Row)];\n\n  let%component make = (~state, ()) => {\n    let%hook (state, dispatch) = Hooks.reducer(~initialState=state, reducer);\n\n    let%hook () =\n      Hooks.effect(OnMount, () => Some(() => dispatch(StopTimer)));\n\n    let toggleAlive = pos => dispatch(ToggleAlive(pos));\n\n    let startStop = () =>\n      state.isRunning\n        ? dispatch(StopTimer)\n        : {\n          let dispose =\n            Tick.interval(\n              ~name=\"GameOfLife Timer\",\n              t => dispatch(TimerTick(t)),\n              Time.zero,\n            );\n          dispatch(StartTimer(dispose));\n        };\n\n    let iconFamily = Font.Family.fromFile(\"FontAwesome5FreeSolid.otf\");\n\n    <Column>\n      <Row>\n        {viewPortRender(state.viewPort, state.universe, toggleAlive)\n         |> React.listToElement}\n      </Row>\n      <View style=controlsStyle>\n        <Button\n          fontFamily=iconFamily\n          title={state.isRunning ? {||} : {||}}\n          onClick=startStop\n        />\n        <Button\n          fontFamily=iconFamily\n          title={||}\n          onClick={_ => dispatch(MoveViewPort(North))}\n        />\n        <Button\n          fontFamily=iconFamily\n          title={||}\n          onClick={_ => dispatch(MoveViewPort(South))}\n        />\n        <Button\n          fontFamily=iconFamily\n          title={||}\n          onClick={_ => dispatch(MoveViewPort(East))}\n        />\n        <Button\n          fontFamily=iconFamily\n          title={||}\n          onClick={_ => dispatch(MoveViewPort(West))}\n        />\n        <Button\n          fontFamily=iconFamily\n          title={||}\n          onClick={_ => dispatch(ZoomViewPort(ZoomIn))}\n        />\n        <Button\n          fontFamily=iconFamily\n          title={||}\n          onClick={_ => dispatch(ZoomViewPort(ZoomOut))}\n        />\n      </View>\n    </Column>;\n  };\n};\n\nlet render = () => {\n  let viewPort = ViewPort.create(15);\n  let state = {\n    viewPort,\n    universe: Examples.pulsar,\n    isRunning: false,\n    dispose: noop,\n  };\n  <GameOfLiveComponent state />;\n};\n"
  },
  {
    "path": "examples/Hello.re",
    "content": "open Revery;\nopen Revery.Math;\nopen Revery.UI;\nopen Revery.UI.Components;\n\nmodule RepoLink = {\n  let make = () => {\n    let activeStyle = Style.[color(Colors.blue)];\n\n    let inactiveStyle = Style.[color(Colors.lightBlue)];\n\n    <Link\n      text=\"View on GitHub\"\n      href=\"https://github.com/revery-ui/revery\"\n      activeStyle\n      inactiveStyle\n    />;\n  };\n};\n\nmodule Logo = {\n  let rotationAnimation =\n    Animation.(\n      zip((\n        animate(Time.seconds(9))\n        |> tween(0., 6.28)\n        |> repeat\n        |> delay(Time.seconds(1)),\n        animate(Time.seconds(4))\n        |> tween(0., 6.28)\n        |> repeat\n        |> delay(Time.ms(500)),\n      ))\n    );\n\n  let%component make = () => {\n    let%hook (shouldRotate, setShouldRotate) = Hooks.state(true);\n    let%hook ((rotationX, rotationY), _animationState, resetRotation) =\n      Hooks.animation(\n        ~name=\"Rotation Animation\",\n        rotationAnimation,\n        ~active=shouldRotate,\n      );\n    let%hook (opacity, setOpacity) = Hooks.state(1.0);\n    let%hook transitionedOpacity =\n      Hooks.transition(~name=\"Opacity Transition Animation\", opacity);\n\n    <View>\n      <Opacity opacity=transitionedOpacity>\n        <Image\n          src={`File(\"outrun-logo.png\")}\n          style=Style.[\n            transform([\n              Transform.RotateY(Angle.from_radians(rotationY)),\n              Transform.RotateX(Angle.from_radians(rotationX)),\n            ]),\n          ]\n        />\n        <View style=Style.[alignItems(`Center)]> <RepoLink /> </View>\n        <Row>\n          <Button\n            width=200\n            height=80\n            onClick={() => setShouldRotate((!))}\n            title={shouldRotate ? \"Pause\" : \"Resume\"}\n          />\n          <Button\n            width=200\n            height=80\n            onClick={() => {\n              setShouldRotate(_ => true);\n              resetRotation();\n            }}\n            title=\"Restart\"\n          />\n        </Row>\n      </Opacity>\n      <Row>\n        <Button\n          width=200\n          height=80\n          onClick={() => setOpacity(_ => 1.0)}\n          title=\"Show it\"\n        />\n        <Button\n          width=200\n          height=80\n          onClick={() => setOpacity(_ => 0.0)}\n          title=\"Hide it\"\n        />\n      </Row>\n    </View>;\n  };\n};\n\nmodule AnimatedText = {\n  let%component make = (~text: string, ~delay: Time.t, ()) => {\n    let%hook (animatedOpacity, _state, _reset) =\n      Hooks.animation(\n        ~name=\"Text Opacity Animation\",\n        Animation.(\n          animate(Time.seconds(1))\n          |> delay(Time.seconds(1))\n          |> ease(Easing.easeOut)\n          |> tween(0., 1.)\n        ),\n      );\n    let%hook (translate, _state, _reset) =\n      Hooks.animation(\n        ~name=\"Text Translate Animation\",\n        Animation.animate(Time.ms(500))\n        |> Animation.delay(delay)\n        |> Animation.ease(Easing.easeOut)\n        |> Animation.tween(50., 0.),\n      );\n\n    let textHeaderStyle =\n      Style.[\n        color(Colors.white),\n        marginHorizontal(8),\n        transform([Transform.TranslateY(translate)]),\n      ];\n\n    <Opacity opacity=animatedOpacity>\n      <Text style=textHeaderStyle fontSize=24. text />\n    </Opacity>;\n  };\n};\n\nlet render = () =>\n  <Center>\n    <Row>\n      <AnimatedText delay=Time.zero text=\"Welcome\" />\n      <AnimatedText delay={Time.ms(500)} text=\"to\" />\n      <AnimatedText delay={Time.seconds(1)} text=\"Revery 😃\" />\n    </Row>\n    <Logo />\n  </Center>;\n"
  },
  {
    "path": "examples/HitTests.re",
    "content": "open Revery;\nopen Revery.UI;\n\nmodule Styles = {\n  open Style;\n\n  let outer = [\n    position(`Absolute),\n    top(0),\n    bottom(0),\n    left(0),\n    right(0),\n    justifyContent(`Center),\n    alignItems(`Center),\n  ];\n\n  let draggable = [\n    width(100),\n    height(100),\n    backgroundColor(Colors.blue),\n    justifyContent(`Center),\n    alignItems(`Center),\n  ];\n\n  let text = Style.[color(Colors.white)];\n};\n\nlet hitTests = (~window, ()) =>\n  <View style=Styles.outer>\n    {Window.isDecorated(window)\n       ? <Text\n           text=\"Window decorations must not be enabled.\\nRestart with --no-decoration\"\n           style=Styles.text\n         />\n       : <View style=Styles.draggable mouseBehavior=Draggable>\n           <Text\n             text=\"Drag window from here!\"\n             style=Styles.text\n             mouseBehavior=Draggable\n           />\n         </View>}\n  </View>;\n\nlet render = window => <hitTests window />;\n"
  },
  {
    "path": "examples/HoverExample.re",
    "content": "open Revery;\nopen Revery.UI;\n\ntype state = {\n  parentBackground: Color.t,\n  childOneBackground: Color.t,\n  childTwoBackground: Color.t,\n  childThreeBackground: Color.t,\n  childFourBackground: Color.t,\n};\n\ntype action =\n  | SetParentBackground(Color.t)\n  | SetChildOneBackground(Color.t)\n  | SetChildTwoBackground(Color.t)\n  | SetChildThreeBackground(Color.t)\n  | SetChildFourBackground(Color.t);\n\nlet reducer = (action: action, state) => {\n  switch (action) {\n  | SetParentBackground(c) => {...state, parentBackground: c}\n  | SetChildOneBackground(c) => {...state, childOneBackground: c}\n  | SetChildTwoBackground(c) => {...state, childTwoBackground: c}\n  | SetChildThreeBackground(c) => {...state, childThreeBackground: c}\n  | SetChildFourBackground(c) => {...state, childFourBackground: c}\n  };\n};\nmodule HoverExample = {\n  let initialState = {\n    parentBackground: Colors.darkGray,\n    childOneBackground: Colors.blanchedAlmond,\n    childTwoBackground: Colors.blueViolet,\n    childThreeBackground: Colors.burlyWood,\n    childFourBackground: Colors.cornflowerBlue,\n  };\n\n  let%component make = () => {\n    let%hook (state, dispatch) = Hooks.reducer(~initialState, reducer);\n\n    <View\n      style=Style.[\n        flexDirection(`Row),\n        justifyContent(`Center),\n        alignItems(`Center),\n        width(500),\n        height(500),\n        backgroundColor(state.parentBackground),\n      ]\n      onMouseOver={_ => dispatch(SetParentBackground(Colors.lightGray))}\n      onMouseOut={_ =>\n        dispatch(SetParentBackground(initialState.parentBackground))\n      }>\n      <View\n        style=Style.[\n          flexDirection(`Row),\n          flexWrap(`Wrap),\n          justifyContent(`SpaceAround),\n          alignItems(`Center),\n        ]>\n        <View\n          style=Style.[\n            width(175),\n            height(175),\n            backgroundColor(state.childOneBackground),\n          ]\n          onMouseEnter={_ =>\n            dispatch(SetChildOneBackground(Colors.aquamarine))\n          }\n          onMouseLeave={_ =>\n            dispatch(SetChildOneBackground(initialState.childOneBackground))\n          }\n        />\n        <View\n          style=Style.[\n            width(175),\n            height(175),\n            backgroundColor(state.childTwoBackground),\n          ]\n          onMouseOver={_ =>\n            dispatch(SetChildTwoBackground(Colors.forestGreen))\n          }\n          onMouseOut={_ =>\n            dispatch(SetChildTwoBackground(initialState.childTwoBackground))\n          }\n        />\n        <View\n          style=Style.[\n            width(175),\n            height(175),\n            backgroundColor(state.childThreeBackground),\n          ]\n          onMouseOver={_ =>\n            dispatch(SetChildThreeBackground(Colors.darkSalmon))\n          }\n          onMouseOut={_ =>\n            dispatch(\n              SetChildThreeBackground(initialState.childThreeBackground),\n            )\n          }\n        />\n        <View\n          style=Style.[\n            width(175),\n            height(175),\n            backgroundColor(state.childFourBackground),\n          ]\n          onMouseEnter={_ => dispatch(SetChildFourBackground(Colors.tomato))}\n          onMouseLeave={_ =>\n            dispatch(\n              SetChildFourBackground(initialState.childFourBackground),\n            )\n          }\n        />\n      </View>\n    </View>;\n  };\n};\n\nlet render = () => {\n  <View\n    style=Style.[\n      position(`Absolute),\n      justifyContent(`Center),\n      alignItems(`Center),\n      bottom(0),\n      top(0),\n      left(0),\n      right(0),\n    ]>\n    <HoverExample />\n  </View>;\n};\n"
  },
  {
    "path": "examples/ImageQualityExample.re",
    "content": "open Revery.UI;\n\nmodule ImageSample = {\n  let make = (~quality, ()) => {\n    <View style=Style.[padding(8)]>\n      <Image height=50 width=50 src={`File(\"reason-logo.png\")} quality />\n    </View>;\n  };\n};\n\nmodule Images = {\n  let make = () => {\n    <View\n      style=Style.[\n        flexDirection(`Column),\n        flexGrow(1),\n        justifyContent(`Center),\n        alignItems(`Center),\n      ]>\n      <ImageSample quality=`none />\n      <ImageSample quality=`low />\n      <ImageSample quality=`medium />\n      <ImageSample quality=`high />\n    </View>;\n  };\n};\n\nlet render = () => <Images />;\n"
  },
  {
    "path": "examples/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleIdentifier</key>\n  <string>com.revery.examples</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "examples/InputExample.re",
    "content": "open Revery;\nopen Revery.UI;\nopen Revery.UI.Components;\n\nlet containerStyle =\n  Style.[\n    position(`Absolute),\n    top(0),\n    bottom(0),\n    left(0),\n    right(0),\n    alignItems(`Center),\n    justifyContent(`Center),\n    flexDirection(`Column),\n  ];\n\nlet controlsStyle =\n  Style.[\n    margin(10),\n    flexDirection(`Row),\n    justifyContent(`Center),\n    alignItems(`Center),\n  ];\n\nlet textStyle =\n  Style.[\n    color(Colors.white),\n    width(100),\n    margin(14),\n    textWrap(TextWrapping.NoWrap),\n  ];\n\nmodule Example = {\n  type inputFields = {\n    first: string,\n    second: string,\n    third: string,\n    isPassword: bool,\n  };\n\n  let%component make = () => {\n    let%hook ({first, isPassword, _}, setValue) =\n      Hooks.state({first: \"\", second: \"\", third: \"\", isPassword: false});\n\n    <View style=containerStyle>\n      <View\n        style=Style.[\n          flexDirection(`Row),\n          alignItems(`Center),\n          justifyContent(`Center),\n        ]>\n        <Input\n          placeholder=\"Insert text here\"\n          onChange={(value, _) =>\n            setValue(state => {...state, first: value})\n          }\n          value=first\n        />\n        <Button\n          height=50\n          width=100\n          fontSize=15.\n          title=\"Reset\"\n          onClick={() => setValue(state => {...state, first: \"\"})}\n        />\n        <Button\n          height=50\n          width=100\n          fontSize=15.\n          title=\"Set value\"\n          onClick={() => setValue(state => {...state, first: \"New value\"})}\n        />\n        <Button\n          height=50\n          width=100\n          fontSize=15.\n          title=\"Emoji\"\n          onClick={() => Native.Input.openEmojiPanel()}\n        />\n      </View>\n      <Padding padding=20>\n        <View\n          style=Style.[\n            flexDirection(`Row),\n            alignItems(`Center),\n            justifyContent(`Center),\n          ]>\n          <Input\n            placeholder=\"Insert text here\"\n            onChange={(value, _) =>\n              setValue(state => {...state, first: value})\n            }\n            value=first\n            isPassword\n          />\n          <View style=controlsStyle>\n            <Text fontSize=16. style=textStyle text=\"Obscure Input\" />\n            <Checkbox\n              checkedColor=Colors.green\n              onChange={() =>\n                setValue(state => {...state, isPassword: !state.isPassword})\n              }\n              style=Style.[border(~width=2, ~color=Colors.green)]\n              checked=isPassword\n            />\n          </View>\n        </View>\n      </Padding>\n      <Padding padding=20>\n        <BoxShadow\n          boxShadow={Style.BoxShadow.make(\n            ~xOffset=-5.,\n            ~yOffset=2.,\n            ~color=Colors.black,\n            ~blurRadius=20.,\n            ~spreadRadius=0.,\n            (),\n          )}>\n          <Input\n            placeholder=\"custom input\"\n            placeholderColor=Colors.plum\n            cursorColor=Colors.white\n            autofocus=true\n            onFocus={() => print_endline(\"Input example focused\")}\n            onBlur={() => print_endline(\"Input example blurred\")}\n            onChange={(value, _) =>\n              setValue(state => {...state, second: value})\n            }\n            onKeyDown={_ => print_endline(\"key event\")}\n            style=Style.[\n              backgroundColor(Colors.paleVioletRed),\n              color(Colors.white),\n              height(50),\n            ]\n          />\n        </BoxShadow>\n      </Padding>\n    </View>;\n  };\n};\n\nlet render = () => <Example />;\n"
  },
  {
    "path": "examples/LayerExample.re",
    "content": "open Revery.UI;\n\nmodule Layers = {\n  let make = () => {\n    <Layer\n      condition=Layer.Condition.always\n      style=Style.[flexGrow(1)]\n      backgroundColor=Revery.Colors.red>\n      <View\n        style=Style.[\n          flexDirection(`Column),\n          flexGrow(1),\n          justifyContent(`Center),\n          alignItems(`Center),\n        ]>\n        <Text text=\"Hello from inside a layer!\" />\n      </View>\n    </Layer>;\n  };\n};\n\nlet render = () => <Layers />;\n"
  },
  {
    "path": "examples/MarkdownExample.re",
    "content": "open Revery;\nopen Revery.UI;\nopen Revery.UI.Components;\n\nmodule Styles = {\n  open Style;\n\n  let outer = [\n    position(`Absolute),\n    top(0),\n    bottom(0),\n    left(0),\n    right(0),\n    margin(6),\n    justifyContent(`FlexStart),\n    alignItems(`FlexStart),\n  ];\n\n  let whiteText = [color(Colors.white)];\n  let linkActive = [color(Colors.blue)];\n  let linkInactive = [color(Colors.lightBlue)];\n};\n\nlet markdown =\n  Markdown.make(\n    ~paragraphStyle=Styles.whiteText,\n    ~h1Style=Styles.whiteText,\n    ~h2Style=Styles.whiteText,\n    ~h3Style=Styles.whiteText,\n    ~h4Style=Styles.whiteText,\n    ~h5Style=Styles.whiteText,\n    ~h6Style=Styles.whiteText,\n    ~activeLinkStyle=Styles.linkActive,\n    ~inactiveLinkStyle=Styles.linkInactive,\n  );\n\nlet example = () =>\n  <View style=Styles.outer>\n    <markdown\n      markdown={markdown|\nAn h2 header\n------------\n\nHere's a numbered list:\n\n 1. first item\n 2. second item\n 3. third item\n\nNote again how the actual text starts at 4 columns in (4 characters\nfrom the left side). Here's a code sample:\n\n    # Let me re-iterate ...\n    for i in 1 .. 10 { do-something(i) }\n\nAs you probably guessed, indented 4 spaces. By the way, instead of\nindenting the block, you can use delimited blocks, if you like:\n\n~~~\ndefine foobar() {\n    print \"Welcome to flavor country!\";\n}\n~~~\n\n(which makes copying & pasting easier). You can optionally mark the\ndelimited block for Pandoc to syntax highlight it:\n\n~~~python\nimport time\n# Quick, count to ten!\nfor i in range(10):\n    # (but not *too* quick)\n    time.sleep(0.5)\n    print(i)\n~~~\n    |markdown}\n    />\n  </View>;\n\nlet render = () => <example />;\n"
  },
  {
    "path": "examples/NativeFileExample.re",
    "content": "open Revery;\nopen Revery.UI;\nopen Revery.UI.Components;\n\nopen Revery.Native;\n\nmodule NativeFileExamples = {\n  let%component make = () => {\n    let%hook (fileListOpt, setFileListOpt) = Hooks.state(None);\n    let%hook (allowMultiple, setAllowMultiple) = Hooks.state(false);\n    let%hook (showHidden, setShowHidden) = Hooks.state(false);\n    let%hook (openFolders, setOpenFolders) = Hooks.state(false);\n\n    let openFile = () => {\n      let o =\n        Dialog.openFiles(\n          ~allowMultiple,\n          ~canChooseDirectories=openFolders,\n          ~showHidden,\n          ~title=\"Revery Open File Example\",\n          ~buttonText=\n            allowMultiple ? \"Open file(s) in Revery\" : \"Open file in Revery\",\n          (),\n        );\n      setFileListOpt(_ => o);\n    };\n\n    let optionStyle = Style.[color(Colors.white)];\n\n    let titleStyle = Style.[color(Colors.white)];\n\n    let renderFilePath = (path: string) =>\n      <Text fontSize=12. style=Style.[color(Colors.white)] text=path />;\n\n    let containerStyle =\n      Style.[\n        position(`Absolute),\n        justifyContent(`Center),\n        alignItems(`Center),\n        bottom(0),\n        top(0),\n        left(0),\n        right(0),\n      ];\n\n    <View style=containerStyle>\n      <Text style=titleStyle fontSize=20. text=\"Open Files and Folders\" />\n      <Row>\n        <Text style=optionStyle text=\"Allow multiple?\" />\n        <Checkbox\n          checked=allowMultiple\n          checkedColor=Colors.green\n          onChange={() => setAllowMultiple(am => !am)}\n        />\n      </Row>\n      <Row>\n        <Text style=optionStyle text=\"Show hidden?\" />\n        <Checkbox\n          checked=showHidden\n          checkedColor=Colors.green\n          onChange={() => setShowHidden(sh => !sh)}\n        />\n      </Row>\n      <Row>\n        <Text style=optionStyle text=\"Open folders?\" />\n        <Checkbox\n          checked=openFolders\n          checkedColor=Colors.green\n          onChange={() => setOpenFolders(ofv => !ofv)}\n        />\n      </Row>\n      <Button title=\"Open File\" onClick=openFile />\n      {switch (fileListOpt) {\n       | Some(fileList) =>\n         fileList\n         |> Array.map(renderFilePath)\n         |> Array.to_list\n         |> React.listToElement\n       | None => <View />\n       }}\n    </View>;\n  };\n};\n\nlet render = () => <NativeFileExamples />;\n"
  },
  {
    "path": "examples/NativeIconExample.re",
    "content": "open Revery;\nopen Revery.UI;\nopen Revery.UI.Components;\n\nopen Revery.Native;\n\nmodule Window = Revery.Window;\n\nmodule NativeFileExamples = {\n  let%component make = (~window as w, ()) => {\n    let%hook (maybeIcon, setMaybeIcon) = Hooks.state(None);\n    let%hook (progress, setIconProgress) =\n      Hooks.state(Icon.Determinate(0.));\n\n    let%hook () =\n      Hooks.effect(\n        OnMount,\n        () => {\n          let ih = Icon.get();\n          setMaybeIcon(_ => Some(ih));\n          Some(() => Icon.hideProgress(w |> Window.getSdlWindow, ih));\n        },\n      );\n\n    let%hook () =\n      Hooks.effect(\n        If((!=), progress),\n        () => {\n          maybeIcon\n          |> Option.iter(icon =>\n               Icon.setProgress(w |> Window.getSdlWindow, icon, progress)\n             );\n          None;\n        },\n      );\n\n    let optionStyle = Style.[color(Colors.white)];\n\n    let titleStyle = Style.[color(Colors.white)];\n\n    let containerStyle =\n      Style.[\n        position(`Absolute),\n        justifyContent(`Center),\n        alignItems(`Center),\n        bottom(0),\n        top(0),\n        left(0),\n        right(0),\n      ];\n\n    <View style=containerStyle>\n      <Text style=titleStyle fontSize=20. text=\"Icon Progress Bar\" />\n      <Slider\n        onValueChanged={x => setIconProgress(_ => Icon.Determinate(x))}\n        maximumValue=1.0\n      />\n      <Text\n        style=optionStyle\n        text={\n          \"Progress: \"\n          ++ (\n            switch (progress) {\n            | Indeterminate => \"Indeterminate\"\n            | Determinate(n) => string_of_float(n)\n            }\n          )\n        }\n      />\n      <Button\n        title=\"Set Indeterminate\"\n        height=24\n        width=120\n        fontSize=12.\n        onClick={() => setIconProgress(_ => Icon.Indeterminate)}\n      />\n    </View>;\n  };\n};\n\nlet render = window => <NativeFileExamples window />;\n"
  },
  {
    "path": "examples/NativeInputExample.re",
    "content": "open Revery;\nopen Revery.UI;\n\nmodule View = {\n  let noop = () => ();\n  let%component make = () => {\n    let%hook (isColumn, setIsColumn) = Hooks.state(true);\n\n    let containerStyle =\n      Style.[\n        position(`Absolute),\n        justifyContent(`Center),\n        alignItems(`Center),\n        flexDirection(isColumn ? `Column : `Row),\n        bottom(0),\n        top(0),\n        left(0),\n        right(0),\n      ];\n\n    <View style=containerStyle>\n      <NativeButton\n        title=\"Toggle Direction\"\n        onClick={() => setIsColumn(ic => !ic)}\n        style=Style.[flexGrow(1), color(Colors.teal)]\n        onMouseEnter={_ => print_endline(\"Mouse Entered!\")}\n      />\n      <NativeButton\n        title=\"Print to STDOUT\"\n        onClick={() => print_endline(\"You clicked a button!\")}\n      />\n      <NativeButton title=\"Custom Width\" style=Style.[width(200)] />\n      <NativeButton title=\"Custom Height\" style=Style.[height(50)] />\n      <NativeButton\n        title=\"Custom Both\"\n        style=Style.[width(200), height(50)]\n      />\n    </View>;\n  };\n};\n\nlet render = () => <View />;\n"
  },
  {
    "path": "examples/NativeMenuExample.re",
    "content": "open Revery;\nopen Revery.UI;\nopen Revery.UI.Components;\n\nopen Revery.Native;\n\nmodule Internal = {\n  let nonce = ref(0);\n};\n\nmodule View = {\n  let make = (~window, ()) => {\n    let onClickCreateMenu = () => {\n      incr(Internal.nonce);\n      let currentNonce = Internal.nonce^;\n      let startTime = Time.now();\n      let menuBar = Menu.getMenuBarHandle();\n      Menu.clear(menuBar);\n\n      let menu1 = Menu.create(\"Test 1\");\n      Menu.addSubmenu(~parent=menuBar, ~child=menu1);\n\n      let menuCallback = (str, ~fromKeyPress, ()) => {\n        print_endline(\n          Printf.sprintf(\n            \"%s clicked: %d (from key press: %b)\",\n            str,\n            currentNonce,\n            fromKeyPress,\n          ),\n        );\n      };\n\n      let item11 =\n        Menu.Item.create(\n          ~title=\"Item 1.1\",\n          ~onClick=menuCallback(\"Item 1.1\"),\n          ~keyEquivalent=Menu.KeyEquivalent.ofString(\"a\"),\n          (),\n        );\n      Menu.addItem(menu1, item11);\n\n      let menu2 = Menu.create(\"Test 2\");\n      Menu.addSubmenu(~parent=menuBar, ~child=menu2);\n\n      let item12 =\n        Menu.Item.create(\n          ~title=\"Item 1.2\",\n          ~onClick=menuCallback(\"Item 1.2\"),\n          ~keyEquivalent=\n            Menu.KeyEquivalent.ofString(\"b\")\n            |> (ke => Menu.KeyEquivalent.enableShift(ke, true)),\n          (),\n        );\n      let item13 =\n        Menu.Item.create(\n          ~title=\"Item 1.3\",\n          ~onClick=menuCallback(\"Item 1.3\"),\n          ~keyEquivalent=\n            Menu.KeyEquivalent.ofString(\"Space\")\n            |> (ke => Menu.KeyEquivalent.enableCtrl(ke, true)),\n          (),\n        );\n      let item14 =\n        Menu.Item.create(\n          ~title=\"Item 1.4\",\n          ~onClick=menuCallback(\"Item 1.4\"),\n          ~keyEquivalent=Menu.KeyEquivalent.ofString(\"ESC\"),\n          (),\n        );\n      let item15 =\n        Menu.Item.create(\n          ~title=\"Item 1.5\",\n          ~onClick=menuCallback(\"Item 1.5\"),\n          ~keyEquivalent=Menu.KeyEquivalent.ofString(\"Tab\"),\n          (),\n        );\n      Menu.addItem(menu2, item12);\n      Menu.addItem(menu2, item13);\n      Menu.addItem(menu2, item14);\n      Menu.addItem(menu2, item15);\n\n      let menu3 = Menu.create(\"Test 3\");\n      Menu.addSubmenu(~parent=menuBar, ~child=menu3);\n\n      let subMenu31 = Menu.create(\"Submenu 1\");\n      Menu.addSubmenu(~parent=menu3, ~child=subMenu31);\n\n      let item311 =\n        Menu.Item.create(\n          ~title=\"Item 3.1.1\",\n          ~onClick=menuCallback(\"Item 3.1.1\"),\n          ~keyEquivalent=\n            Menu.KeyEquivalent.ofString(\"c\")\n            |> (ke => Menu.KeyEquivalent.enableAlt(ke, true)),\n          (),\n        );\n\n      let separator = Menu.Item.createSeparator();\n\n      let item312 =\n        Menu.Item.create(\n          ~title=\"Item 3.1.2\",\n          ~onClick=menuCallback(\"Item 3.1.2\"),\n          (),\n        );\n\n      Menu.Item.setEnabled(item312, false);\n\n      let item313 =\n        Menu.Item.create(\n          ~title=\"Item 3.1.3\",\n          ~onClick=menuCallback(\"Item 3.1.3\"),\n          (),\n        );\n\n      Menu.Item.setVisible(item313, false); // Shouldn't show up\n\n      Menu.addItem(subMenu31, item311);\n      Menu.addItem(subMenu31, separator);\n      Menu.addItem(subMenu31, item312);\n      Menu.addItem(subMenu31, item313);\n\n      let endTime = Time.now();\n      let delta = Time.(endTime - startTime) |> Time.toFloatSeconds;\n      print_endline(\"Render time: \" ++ string_of_float(delta));\n    };\n\n    let onMouseUp = (evt: NodeEvents.mouseButtonEventParams) =>\n      if (evt.button == MouseButton.BUTTON_RIGHT) {\n        let menu = Menu.create(\"Right click\");\n        let item1 =\n          Menu.Item.create(\n            ~title=\"Click me 1\",\n            ~onClick=\n              (~fromKeyPress as _, ()) => print_endline(\"You clicked me!\"),\n            (),\n          );\n        let item2 =\n          Menu.Item.create(\n            ~title=\"Click me 2\",\n            ~onClick=\n              (~fromKeyPress as _, ()) => print_endline(\"You clicked me!\"),\n            (),\n          );\n        Menu.addItem(menu, item1);\n        Menu.addItem(menu, item2);\n        Menu.displayIn(\n          ~x=int_of_float(evt.mouseX),\n          ~y=int_of_float(evt.mouseY),\n          menu,\n          window |> Revery.Window.getSdlWindow,\n        );\n      };\n\n    let containerStyle =\n      Style.[\n        position(`Absolute),\n        justifyContent(`Center),\n        alignItems(`Center),\n        bottom(0),\n        top(0),\n        left(0),\n        right(0),\n      ];\n\n    <View style=containerStyle onMouseUp>\n      <Button title=\"Create Menu\" onClick=onClickCreateMenu />\n      <Text text=\"Or right click anywhere!\" italic=true />\n    </View>;\n  };\n};\n\nlet render = window => <View window />;\n"
  },
  {
    "path": "examples/NativeNotificationExample.re",
    "content": "open Revery.UI;\nopen Revery.UI.Components;\n\nopen Revery.Native;\n\nmodule Example = {\n  let make = () => {\n    let notification =\n      Notification.create(\n        ~title=\"Revery Test\",\n        ~body=\"This is a test!\",\n        ~mute=false,\n        ~onClick=() => print_endline(\"Notification clicked!\"),\n        (),\n      );\n    <View>\n      <Center>\n        <Button\n          title=\"Dispatch!\"\n          onClick={() => Notification.dispatch(notification)}\n        />\n        <Button\n          title=\"Schedule 5s From Now!\"\n          onClick={() => Notification.scheduleFromNow(5, notification)}\n        />\n      </Center>\n    </View>;\n  };\n};\n\nlet render = () => <Example />;\n"
  },
  {
    "path": "examples/NestedClickable.re",
    "content": "open Revery;\nopen Revery.UI;\nopen Revery.UI.Components;\n\nmodule Styles = {\n  open Style;\n\n  let outer = [\n    position(`Absolute),\n    top(0),\n    bottom(0),\n    left(0),\n    right(0),\n    justifyContent(`Center),\n    alignItems(`Center),\n    backgroundColor(Colors.yellow),\n  ];\n};\n\nlet%component clickies = () => {\n  let%hook (text, setText) = Hooks.state(\"Click something\");\n\n  <Clickable\n    style=Styles.outer onClick={() => setText(_ => \"Clicked outside\")}>\n    <Button title=text onClick={() => setText(_ => \"Clicked inside\")} />\n  </Clickable>;\n};\n\nlet render = () => <clickies />;\n"
  },
  {
    "path": "examples/RadioButtonExample.re",
    "content": "open Revery;\nopen Revery.UI;\nopen Revery.UI.Components;\n\nmodule RadioExample = {\n  let%component make = () => {\n    let%hook (radioVal, setRadioVal) = Hooks.state(\"Select a button!\");\n\n    <View\n      style=Style.[\n        width(500),\n        height(500),\n        justifyContent(`Center),\n        alignItems(`Center),\n        backgroundColor(Color.rgba(1., 1., 1., 0.1)),\n      ]>\n      <Text\n        text=\"Radio Button\"\n        fontSize=20.\n        style=Style.[marginBottom(20)]\n      />\n      <RadioButtonsInt\n        onChange={txt =>\n          setRadioVal(_ => \"Radio Button Value: \" ++ string_of_int(txt))\n        }\n        defaultSelected=0\n        iconSize=20.\n        buttons=[\n          {text: \"Button 1\", value: 1},\n          {text: \"Button 2\", value: 2},\n          {text: \"Button 3\", value: 3},\n          {text: \"Button 4\", value: 4},\n        ]\n      />\n      <Text text=radioVal fontSize=20. style=Style.[marginTop(20)] />\n    </View>;\n  };\n};\n\nlet render = () => <RadioExample />;\n"
  },
  {
    "path": "examples/RichTextExample.re",
    "content": "open Revery;\nopen Revery.UI;\nopen Revery.UI.Components;\nopen Revery.Font;\n\nlet containerStyle =\n  Style.[\n    position(`Absolute),\n    top(0),\n    bottom(0),\n    left(0),\n    right(0),\n    alignItems(`Center),\n    justifyContent(`Center),\n    flexDirection(`Column),\n  ];\n\nmodule SampleRichText = {\n  let make = () => {\n    let richtext =\n      RichText.(\n        text(\"Hello \", ~color=Colors.red, ~fontWeight=Weight.Bold)\n        ++ text(\"world\", ~color=Colors.green)\n        ++ text(\"!\", ~color=Colors.yellow)\n        |> fontSize(20.)\n        |> italic\n      );\n\n    let dimensions = RichText.measure(richtext);\n    let widthText = \"Width: \" ++ string_of_int(dimensions.width);\n    let heightText = \"Height: \" ++ string_of_int(dimensions.height);\n\n    <View style=containerStyle>\n      <RichTextView richtext />\n      <Text text=widthText />\n      <Text text=heightText />\n    </View>;\n  };\n};\nlet render = () => <SampleRichText />;\n"
  },
  {
    "path": "examples/SVGExample.re",
    "content": "open Revery;\nopen Revery.UI;\nopen Revery.UI.Components;\n\nlet examples = [\n  (\n    \"<polygon>\",\n    {|\n      <!-- Source: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/polygon -->\n\n      <svg viewBox=\"0 0 200 100\" xmlns=\"http://www.w3.org/2000/svg\">\n        <!-- Example of a polygon with the default fill -->\n        <polygon points=\"0,100 50,25 50,75 100,0\" />\n\n        <!-- Example of the same polygon shape with stroke and no fill -->\n        <polygon points=\"100,100 150,25 150,75 200,0\"\n                  fill=\"none\" stroke=\"black\" />\n      </svg>\n    |},\n  ),\n  (\n    \"<polyline>\",\n    {|\n      <!-- Source: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/polyline -->\n\n      <svg viewBox=\"0 0 200 100\" xmlns=\"http://www.w3.org/2000/svg\">\n        <!-- Example of a polyline with the default fill -->\n        <polyline points=\"0,100 50,25 50,75 100,0\" />\n\n        <!-- Example of the same polyline shape with stroke and no fill -->\n        <polyline points=\"100,100 150,25 150,75 200,0\"\n                  fill=\"none\" stroke=\"black\" />\n      </svg>\n    |},\n  ),\n  (\n    \"<rect>\",\n    {|\n      <!-- Source: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/rect -->\n\n      <svg viewBox=\"0 0 220 100\" xmlns=\"http://www.w3.org/2000/svg\">\n        <!-- Simple rectangle -->\n        <rect width=\"100\" height=\"100\" />\n\n        <!-- Rounded corner rectangle -->\n        <rect x=\"120\" width=\"100\" height=\"100\" rx=\"15\" />\n      </svg>\n    |},\n  ),\n  (\n    \"<path> - heart\",\n    {|\n      <!-- Source: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d -->\n\n      <svg viewBox=\"0 0 100 100\" xmlns=\"http://www.w3.org/2000/svg\">\n        <path fill=\"none\" stroke=\"red\"\n          d=\"M 10,30\n            A 20,20 0,0,1 50,30\n            A 20,20 0,0,1 90,30\n            Q 90,60 50,90\n            Q 10,60 10,30 z\" />\n      </svg>\n    |},\n  ),\n  (\n    \"<path> - moveTo\",\n    {|\n      <!-- Source: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d#Path_commands -->\n\n      <svg viewBox=\"0 0 100 100\" xmlns=\"http://www.w3.org/2000/svg\">\n        <path fill=\"none\" stroke=\"red\"\n          d=\"M 10,10 h 10\n            m  0,10 h 10\n            m  0,10 h 10\n            M 40,20 h 10\n            m  0,10 h 10\n            m  0,10 h 10\n            m  0,10 h 10\n            M 50,50 h 10\n            m-20,10 h 10\n            m-20,10 h 10\n            m-20,10 h 10\" />\n      </svg>\n    |},\n  ),\n  (\n    \"<path> - lineTo\",\n    {|\n      <!-- Source: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d#Path_commands -->\n\n      <svg viewBox=\"0 0 200 100\" xmlns=\"http://www.w3.org/2000/svg\">\n        <!-- LineTo commands with absolute coordinates -->\n        <path fill=\"none\" stroke=\"red\"\n              d=\"M 10,10\n                L 90,90\n                V 10\n                H 50\" />\n\n        <!-- LineTo commands with relative coordinates -->\n        <path fill=\"none\" stroke=\"red\"\n              d=\"M 110,10\n                l 80,80\n                v -80\n                h -40\" />\n      </svg>\n    |},\n  ),\n  (\n    \"<path> - cubic bezier\",\n    {|\n      <!-- Source: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d#Path_commands -->\n\n      <svg viewBox=\"0 0 200 100\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\n        <!-- Cubic Bézier curve with absolute coordinates -->\n        <path fill=\"none\" stroke=\"red\"\n              d=\"M 10,90\n                C 30,90 25,10 50,10\n                S 70,90 90,90\" />\n\n        <!-- Cubic Bézier curve with relative coordinates -->\n        <path fill=\"none\" stroke=\"red\"\n              d=\"M 110,90\n                c 20,0 15,-80 40,-80\n                s 20,80 40,80\" />\n\n        <!-- Highlight the curve vertex and control points -->\n        <g id=\"ControlPoints\">\n\n          <!-- First cubic command control points -->\n          <line x1=\"10\" y1=\"90\" x2=\"30\" y2=\"90\" stroke=\"lightgrey\" />\n          <circle cx=\"30\" cy=\"90\" r=\"1.5\"/>\n\n          <line x1=\"50\" y1=\"10\" x2=\"25\" y2=\"10\" stroke=\"lightgrey\" />\n          <circle cx=\"25\" cy=\"10\" r=\"1.5\"/>\n\n          <!-- Second smooth command control points (the first one is implicit) -->\n          <line x1=\"50\" y1=\"10\" x2=\"75\" y2=\"10\" stroke=\"lightgrey\" stroke-dasharray=\"2\" />\n          <circle cx=\"75\" cy=\"10\" r=\"1.5\" fill=\"lightgrey\"/>\n\n          <line x1=\"90\" y1=\"90\" x2=\"70\" y2=\"90\" stroke=\"lightgrey\" />\n          <circle cx=\"70\" cy=\"90\" r=\"1.5\" />\n\n          <!-- curve vertex points -->\n          <circle cx=\"10\" cy=\"90\" r=\"1.5\"/>\n          <circle cx=\"50\" cy=\"10\" r=\"1.5\"/>\n          <circle cx=\"90\" cy=\"90\" r=\"1.5\"/>\n        </g>\n        <use xlink:href=\"#ControlPoints\" x=\"100\" />\n      </svg>\n    |},\n  ),\n  (\n    \"<path> - quadratic bezier\",\n    {|\n      <!-- Source: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d#Path_commands -->\n\n      <svg viewBox=\"0 0 200 100\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\n        <!-- Quadratic Bézier curve with implicit repetition -->\n        <path fill=\"none\" stroke=\"red\"\n              d=\"M 10,50\n                Q 25,25 40,50\n                t 30,0 30,0 30,0 30,0 30,0\" />\n\n        <!-- Highlight the curve vertex and control points -->\n        <g>\n          <polyline points=\"10,50 25,25 40,50\" stroke=\"rgba(0,0,0,.2)\" fill=\"none\" />\n          <circle cx=\"25\" cy=\"25\" r=\"1.5\" />\n\n          <!-- Curve vertex points -->\n          <circle cx=\"10\" cy=\"50\" r=\"1.5\"/>\n          <circle cx=\"40\" cy=\"50\" r=\"1.5\"/>\n\n          <g id=\"SmoothQuadraticDown\">\n            <polyline points=\"40,50 55,75 70,50\" stroke=\"rgba(0,0,0,.2)\" stroke-dasharray=\"2\" fill=\"none\" />\n            <circle cx=\"55\" cy=\"75\" r=\"1.5\" fill=\"lightgrey\" />\n            <circle cx=\"70\" cy=\"50\" r=\"1.5\" />\n          </g>\n\n          <g id=\"SmoothQuadraticUp\">\n            <polyline points=\"70,50 85,25 100,50\" stroke=\"rgba(0,0,0,.2)\" stroke-dasharray=\"2\" fill=\"none\" />\n            <circle cx=\"85\" cy=\"25\" r=\"1.5\" fill=\"lightgrey\" />\n            <circle cx=\"100\" cy=\"50\" r=\"1.5\" />\n          </g>\n\n          <use xlink:href=\"#SmoothQuadraticDown\" x=\"60\" />\n          <use xlink:href=\"#SmoothQuadraticUp\"   x=\"60\" />\n          <use xlink:href=\"#SmoothQuadraticDown\" x=\"120\" />\n        </g>\n      </svg>\n    |},\n  ),\n  (\n    \"<path> - quadratic bezier*\",\n    {|\n      <svg viewBox=\"0 0 200 100\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\n        <!-- Quadratic Bézier curve with implicit repetition -->\n        <path fill=\"none\" stroke=\"red\"\n              d=\"M 10,50\n                Q 25,25 40,50\n                q 15,25 30,0\n                q 15,-25 30,0\n                q 15,25 30,0\n                q 15,-25 30,0\n                q 15,25 30,0\n                q 15,-25 30,0\" />\n\n        <!-- Highlight the curve vertex and control points -->\n        <g>\n          <polyline points=\"10,50 25,25 40,50\" stroke=\"#0003\" fill=\"none\" />\n          <circle cx=\"25\" cy=\"25\" r=\"1.5\" />\n\n          <!-- Curve vertex points -->\n          <circle cx=\"10\" cy=\"50\" r=\"1.5\"/>\n          <circle cx=\"40\" cy=\"50\" r=\"1.5\"/>\n\n          <g id=\"SmoothQuadraticDown\">\n            <polyline points=\"40,50 55,75 70,50\" stroke=\"#0003\" stroke-dasharray=\"2\" fill=\"none\" />\n            <circle cx=\"55\" cy=\"75\" r=\"1.5\" fill=\"lightgrey\" />\n            <circle cx=\"70\" cy=\"50\" r=\"1.5\" />\n          </g>\n\n          <g id=\"SmoothQuadraticUp\">\n            <polyline points=\"70,50 85,25 100,50\" stroke=\"#0003\" stroke-dasharray=\"2\" fill=\"none\" />\n            <circle cx=\"85\" cy=\"25\" r=\"1.5\" fill=\"lightgrey\" />\n            <circle cx=\"100\" cy=\"50\" r=\"1.5\" />\n          </g>\n\n          <use xlink:href=\"#SmoothQuadraticDown\" x=\"60\" />\n          <use xlink:href=\"#SmoothQuadraticUp\"   x=\"60\" />\n          <use xlink:href=\"#SmoothQuadraticDown\" x=\"120\" />\n        </g>\n      </svg>\n    |},\n  ),\n  (\n    \"<path> - quadratic bezier 2\",\n    {|\n      <svg width=\"12cm\" height=\"6cm\" viewBox=\"0 0 1200 600\"\n          xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">\n        <title>Example quad01 - quadratic Bézier commands in path data</title>\n        <desc>Picture showing a \"Q\" a \"T\" command,\n              along with annotations showing the control points\n              and end points</desc>\n        <rect x=\"1\" y=\"1\" width=\"1198\" height=\"598\"\n              fill=\"none\" stroke=\"blue\" stroke-width=\"1\" />\n\n        <path d=\"M200,300 Q400,50 600,300 T1000,300\"\n              fill=\"none\" stroke=\"red\" stroke-width=\"5\"  />\n        <!-- End points -->\n        <g fill=\"black\" >\n          <circle cx=\"200\" cy=\"300\" r=\"10\"/>\n          <circle cx=\"600\" cy=\"300\" r=\"10\"/>\n          <circle cx=\"1000\" cy=\"300\" r=\"10\"/>\n        </g>\n        <!-- Control points and lines from end points to control points -->\n        <g fill=\"#888888\" >\n          <circle cx=\"400\" cy=\"50\" r=\"10\"/>\n          <circle cx=\"800\" cy=\"550\" r=\"10\"/>\n        </g>\n        <path d=\"M200,300 L400,50 L600,300\n                L800,550 L1000,300\"\n              fill=\"none\" stroke=\"#888888\" stroke-width=\"2\" />\n      </svg>\n    |},\n  ),\n  (\n    \"<path> - elliptic arc\",\n    {|\n      <!-- Source: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d#Path_commands -->\n\n      <svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\">\n\n        <!-- The influence of the arc flags with which the arc is drawn -->\n        <path fill=\"none\" stroke=\"red\"\n              d=\"M 6,10\n                A 6 4 10 1 0 14,10\" />\n\n        <path fill=\"none\" stroke=\"lime\"\n              d=\"M 6,10\n                A 6 4 10 1 1 14,10\" />\n\n        <path fill=\"none\" stroke=\"purple\"\n              d=\"M 6,10\n                A 6 4 10 0 1 14,10\" />\n\n        <path fill=\"none\" stroke=\"pink\"\n              d=\"M 6,10\n                A 6 4 10 0 0 14,10\" />\n      </svg>\n    |},\n  ),\n  (\n    \"<path> - close path\",\n    {|\n      <!-- Source: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d#Path_commands -->\n\n      <svg viewBox=\"0 -1 30 11\" xmlns=\"http://www.w3.org/2000/svg\">\n\n        <!--\n        An open shape with the last point of\n        the path different to the first one\n        -->\n        <path stroke=\"red\"\n              d=\"M 5,1\n                l -4,8 8,0\" />\n\n        <!--\n        An open shape with the last point of\n        the path matching the first one\n        -->\n        <path stroke=\"red\"\n              d=\"M 15,1\n                l -4,8 8,0 -4,-8\" />\n\n        <!--\n        A closed shape with the last point of\n        the path different to the first one\n        -->\n        <path stroke=\"red\"\n              d=\"M 25,1\n                l -4,8 8,0\n                z\" />\n      </svg>\n    |},\n  ),\n  (\n    \"Material - disc\",\n    {|\n      <!-- Source: https://github.com/PKief/vscode-material-icon-theme/blob/master/icons/disc.svg?short_path=60ae136 -->\n\n      <svg version=\"1.1\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n        <path d=\"m12 14.5c-1.3875 0-2.5-1.125-2.5-2.5 0-1.3875 1.1125-2.5 2.5-2.5s2.5 1.1125 2.5 2.5a2.5 2.5 0 0 1-2.5 2.5m0-12.5a10 10 0 0 0-10 10 10 10 0 0 0 10 10 10 10 0 0 0 10-10 10 10 0 0 0-10-10z\" style=\"fill:#b0bec5;stroke-width:1.25\"/></svg>\n    |},\n  ),\n  (\n    \"Material - flash\",\n    {|\n      <!-- Source: https://github.com/PKief/vscode-material-icon-theme/blob/master/icons/flash.svg?short_path=987e419 -->\n\n      <svg version=\"1.1\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n        <defs>\n          <linearGradient id=\"linearGradient4150\" x1=\"2.373\" x2=\"21.86\" y1=\"12.027\" y2=\"12.027\" gradientTransform=\"translate(-.11669 -24.027)\" gradientUnits=\"userSpaceOnUse\">\n            <stop stop-color=\"#d92f3c\" offset=\"0\"/>\n            <stop stop-color=\"#791223\" offset=\"1\"/>\n          </linearGradient>\n        </defs>\n        <g>\n          <rect transform=\"rotate(90)\" x=\"2.2563\" y=\"-21.744\" width=\"19.487\" height=\"19.487\" ry=\"0\" fill=\"url(#linearGradient4150)\"/>\n          <path d=\"m16.56 5.9994c-.0046.000383-.0087.00127-.01324.00165-.42659.03629-.83051.09268-1.1813.19192-.55415.166-1.0532.40447-1.494.71803-.4282.32278-.81705.72086-1.1697 1.1912-.34004.47956-.6824 1.0323-1.0224 1.6594-.12109.22868-.23331.42505-.35074.63696v.0017c-1e-4.000183-.0032-.000183-.0033 0l-1.3401 2.394-.005-.0017c-.23782.4425-.46149.84702-.66509 1.1978-.2141.36889-.45189.6815-.71637.93972-.26448.25822-.56659.45561-.90663.59394-.071945.02731-.1614.04195-.24155.06287-.0005144.000134-.00114-.000134-.00165 0h-.9877v2.4138h.98935v-.0017c.42844-.03628.83244-.09229 1.1846-.19192.55415-.166 1.0532-.40447 1.494-.71803.4282-.32278.8187-.72086 1.1713-1.1912.34005-.47956.68074-1.0323 1.0208-1.6594.12084-.22819.23523-.42557.3524-.63696l.0066.0017.0033-.005.0364-.06618h2.5296v.0017h1.125v-2.4072h-.33089v-.0017h-1.9787c.21975-.40713.43079-.78837.62041-1.1151.2141-.36889.45189-.6815.71637-.93972.26448-.25822.56659-.45561.90663-.59394.07046-.026754.15931-.041111.23824-.061214h.99266v-.00331h-.0017v-2.4105h-.97777v-.00165z\" fill=\"#fff\"/>\n        </g>\n      </svg>\n    |},\n  ),\n  (\n    \"Onivim logo\",\n    {|\n      <!-- Source: https://raw.githubusercontent.com/onivim/oni2/master/assets/images/logo.svg -->\n\n      <svg id=\"Layer_1\" data-name=\"Layer 1\"\n        xmlns=\"http://www.w3.org/2000/svg\"\n        xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"1023.62\" height=\"990.06\" viewBox=\"0 0 1023.62 990.06\">\n        <defs>\n          <linearGradient id=\"linear-gradient\" x1=\"495.41\" y1=\"803.4\" x2=\"495.41\" y2=\"128.72\" gradientUnits=\"userSpaceOnUse\">\n            <stop offset=\"0\" stop-color=\"#e6537c\"/>\n            <stop offset=\"1\" stop-color=\"#ffe8c7\"/>\n          </linearGradient>\n        </defs>\n        <title>logo</title>\n        <g id=\"Icon\">\n          <g id=\"O_2\" data-name=\"O&amp;2\">\n            <path d=\"M755.74,378.89a4.49,4.49,0,0,1-4.48-4.49V222.33a4.48,4.48,0,0,1,4.48-4.48H974.65V202.6a47.15,47.15,0,0,0-47.1-47.1H755.74a4.49,4.49,0,0,1-4.48-4.49V110.63a4.48,4.48,0,0,1,4.48-4.48H927.55A96.56,96.56,0,0,1,1024,202.6v60.11a4.48,4.48,0,0,1-4.49,4.48H800.61v62.35h174a49.17,49.17,0,0,1,49.1,44.42,4.48,4.48,0,0,1-4.46,4.93ZM990.44,512a496.53,496.53,0,0,0-9.51-96.51,7.37,7.37,0,0,0-7.23-5.93H951.11a7.37,7.37,0,0,0-7.22,8.87A460.16,460.16,0,0,1,953.6,512c0,252.65-205.54,458.19-458.19,458.19S37.22,764.68,37.22,512,242.76,53.84,495.41,53.84A459.15,459.15,0,0,1,709.74,107.1a7.37,7.37,0,0,0,10.82-6.51V75.91a7.37,7.37,0,0,0-4.06-6.58A488.6,488.6,0,0,0,495.41,17c-273,0-495,222.07-495,495s222.07,495,495,495S990.44,785,990.44,512Z\" transform=\"translate(-0.38 -17)\" style=\"fill:#3af5e9\"/>\n          </g>\n          <path id=\"S\" d=\"M499.5,845.25c-88.24,0-177.54-29.33-258.23-84.78-6-4.08-9.24-14.07-7.89-23.93s7-16.93,13.46-16.93H752.17c6.5,0,12.11,7.06,13.46,16.93s-2,19.85-7.89,23.93C677.05,815.92,587.75,845.25,499.5,845.25ZM194,647.67c-2.73,0-5.27-3-6.75-7.9-4.36-14.57-8.58-29.78-12.55-45.19a34.89,34.89,0,0,1,.12-17c1.44-5.22,4.06-8.44,6.89-16.1H817.83c2.83,7.66,5.45,10.88,6.89,16.1a34.77,34.77,0,0,1,.12,17c-4,15.41-8.19,30.62-12.55,45.19-1.47,4.93-4,7.9-6.75,7.9ZM162.11,497.22a7.39,7.39,0,0,1-7.35-7.85c11.66-178.76,161.29-318.79,340.65-318.79A341.32,341.32,0,0,1,718,253.14a7.36,7.36,0,0,1,2.56,5.59V374.41a35.22,35.22,0,0,0,35.18,35.18h59.88a7.35,7.35,0,0,1,7.06,5.27,340.84,340.84,0,0,1,13.37,74.5,7.37,7.37,0,0,1-7.35,7.86Z\" transform=\"translate(-0.38 -17)\" style=\"fill:url(#linear-gradient)\"/>\n        </g>\n      </svg>\n    |},\n  ),\n];\n\nmodule SVGExample = {\n  module Styles = {\n    open Style;\n\n    let container = [\n      backgroundColor(Color.rgba(1., 1., 1., 0.1)),\n      flexDirection(`Row),\n    ];\n\n    let buttons = [width(200), color(Colors.white), marginLeft(10)];\n  };\n\n  let%component make = () => {\n    let%hook (currentExample, setExample) = {\n      let (_, data) = List.hd(examples);\n      Hooks.state(data);\n    };\n    let%hook (width, setWidth) = Hooks.state(300.);\n    let%hook (height, setHeight) = Hooks.state(300.);\n\n    let buttons =\n      List.map(\n        ((text, value)) => RadioButtonsString.{text, value},\n        examples,\n      );\n    let onChange = data => setExample(_ => data);\n\n    <View style=Styles.container>\n      <View>\n        <RadioButtonsString\n          style=Styles.buttons\n          onChange\n          defaultSelected=0\n          buttons\n        />\n        <Slider\n          onValueChanged={w => setWidth(_ => w)}\n          maximumValue=300.\n          minimumValue=50.\n          initialValue=width\n        />\n        <Slider\n          onValueChanged={h => setHeight(_ => h)}\n          maximumValue=500.\n          minimumValue=50.\n          initialValue=height\n        />\n      </View>\n      <SVG width height src={`Str(currentExample)} scaleMode=`Fit />\n    </View>;\n  };\n};\n\nlet render = () => <SVGExample />;\n"
  },
  {
    "path": "examples/ScreenCapture.re",
    "content": "open Revery;\nopen Revery.UI;\nopen Revery.UI.Components;\n\nlet backgroundColor = Color.hex(\"#212733\");\nlet activeBackgroundColor = Color.hex(\"#2E3440\");\nlet inactiveBackgroundColor = Color.hex(\"#272d39\");\nlet selectionHighlight = Color.hex(\"#90f7ff\");\n\nmodule ActionButton = {\n  let make = (~name, ~onClick, ()) => {\n    let wrapperStyle =\n      Style.[\n        backgroundColor(selectionHighlight),\n        border(~width=4, ~color=activeBackgroundColor),\n      ];\n    let textHeaderStyle = Style.[color(Colors.black), margin(16)];\n\n    <Clickable style=wrapperStyle onClick>\n      <Text style=textHeaderStyle text=name />\n    </Clickable>;\n  };\n};\n\nmodule CaptureArea = {\n  let%component make = (~w, ()) => {\n    let%hook (count, setCount) = Hooks.state(0);\n    let%hook (file, setFile) = Hooks.state(None);\n\n    let capture = () => {\n      let exed = Environment.getExecutingDirectory();\n      let filename = Printf.sprintf(\"Scrot_%d.tga\", count);\n      let fullname = exed ++ filename;\n      Window.takeScreenshot(w, fullname);\n      setCount(_ => count + 1);\n      setFile(_ => Some(filename));\n    };\n\n    let viewStyle =\n      Style.[\n        position(`Absolute),\n        left(0),\n        right(0),\n        top(0),\n        bottom(0),\n        flexDirection(`Column),\n      ];\n\n    let imageStyle = Style.[width(400), height(300)];\n\n    <View style=viewStyle>\n      <ActionButton name=\"Take a screenshot!\" onClick=capture />\n      {switch (file) {\n       | None => <View />\n       | Some(src) => <Image style=imageStyle src={`File(src)} />\n       }}\n    </View>;\n  };\n};\n\nlet render = w => <CaptureArea w />;\n"
  },
  {
    "path": "examples/ScrollView.re",
    "content": "open Revery;\nopen Revery.UI;\nopen Revery.UI.Components;\n\nlet containerStyle =\n  Style.[\n    position(`Absolute),\n    top(0),\n    bottom(0),\n    left(0),\n    right(0),\n    alignItems(`Center),\n    justifyContent(`Center),\n    flexDirection(`Column),\n  ];\n\nlet outerBox =\n  Style.[width(200), height(200), backgroundColor(Colors.black)];\n\nlet innerBox =\n  Style.[\n    width(450),\n    height(450),\n    backgroundColor(Color.rgba(0., 1., 0., 0.5)),\n  ];\n\nmodule Sample = {\n  let%component make = () => {\n    let%hook (bounce, setBounce) = Hooks.state(true);\n\n    <View style=containerStyle>\n      <Text text=\"Bounce\" fontSize=20. style=Style.[marginBottom(10)] />\n      <Checkbox\n        onChange={() => setBounce(isBounce => !isBounce)}\n        checked=bounce\n        style=Style.[marginBottom(10)]\n      />\n      <ScrollView style=outerBox bounce>\n        <Image\n          src={`File(\"outrun-logo.png\")}\n          /* Exercise the case in #579 */\n          style=Style.[overflow(`Hidden), width(512), height(256)]\n        />\n        <Image\n          src={`File(\"outrun-logo.png\")}\n          style=Style.[width(512), height(256)]\n        />\n        <Image\n          src={`File(\"outrun-logo.png\")}\n          style=Style.[width(512), height(256)]\n        />\n      </ScrollView>\n      <Text\n        text=\"To scroll horizontally use Mouse Wheel while holding Shift Key\"\n        fontSize=20.\n        style=Style.[marginTop(10)]\n      />\n    </View>;\n  };\n};\n\nlet render = () => <Sample />;\n"
  },
  {
    "path": "examples/Slider.re",
    "content": "open Revery;\nopen Revery.Math;\nopen Revery.UI;\nopen Revery.UI.Components;\n\nmodule AdjustableLogo = {\n  let%component make = () => {\n    let%hook (rotationX, setRotationX) =\n      Hooks.reducer(~initialState=0., (value, _) => value);\n    let%hook (rotationY, setRotationY) =\n      Hooks.reducer(~initialState=0., (value, _) => value);\n    let%hook (rotationZ, setRotationZ) =\n      Hooks.reducer(~initialState=0., (value, _) => value);\n\n    let containerStyle =\n      Style.[\n        flexGrow(1),\n        justifyContent(`Center),\n        alignItems(`Center),\n        flexDirection(`Column),\n      ];\n\n    let textStyle = Style.[color(Colors.white), width(100), margin(14)];\n\n    let controlsStyle =\n      Style.[\n        margin(10),\n        flexDirection(`Row),\n        justifyContent(`Center),\n        alignItems(`Center),\n      ];\n\n    let sliderContainerStyle =\n      Style.[\n        margin(10),\n        borderBottom(~width=1, ~color=Colors.darkGray),\n        flexDirection(`Row),\n        justifyContent(`Center),\n        alignItems(`Center),\n      ];\n\n    let verticalSliderContainerStyle =\n      Style.[\n        margin(10),\n        borderRight(~width=1, ~color=Colors.darkGray),\n        flexDirection(`Column),\n        justifyContent(`Center),\n        alignItems(`Center),\n      ];\n\n    let toDeg = r => 180. *. r /. pi;\n\n    let toDegString = r => r |> toDeg |> floor |> string_of_float;\n\n    let twoPi = 2. *. pi;\n\n    <View style=containerStyle>\n      <View>\n        <Image\n          src={`File(\"outrun-logo.png\")}\n          style=Style.[\n            width(512),\n            height(256),\n            transform([\n              Transform.RotateZ(Angle.from_radians(rotationZ)),\n              Transform.RotateY(Angle.from_radians(rotationY)),\n              Transform.RotateX(Angle.from_radians(rotationX)),\n            ]),\n          ]\n        />\n      </View>\n      <View style=controlsStyle>\n        <View style=verticalSliderContainerStyle>\n          <Text style=textStyle fontSize=16. text=\"Rotation Y: \" />\n          <Slider\n            vertical=true\n            onValueChanged=setRotationY\n            maximumValue=twoPi\n          />\n          <Text\n            style=textStyle\n            fontSize=16.\n            text={\"Value: \" ++ toDegString(rotationY)}\n          />\n        </View>\n        <View style=containerStyle>\n          <View style=sliderContainerStyle>\n            <Text style=textStyle fontSize=16. text=\"Rotation X: \" />\n            <Slider\n              onValueChanged=setRotationX\n              initialValue=twoPi\n              maximumValue=twoPi\n            />\n            <Text\n              style=textStyle\n              fontSize=16.\n              text={\"Value: \" ++ toDegString(rotationX)}\n            />\n          </View>\n          <View style=sliderContainerStyle>\n            <Text style=textStyle fontSize=16. text=\"Rotation Z: \" />\n            <Slider onValueChanged=setRotationZ maximumValue=twoPi />\n            <Text\n              style=textStyle\n              fontSize=16.\n              text={\"Value: \" ++ toDegString(rotationZ)}\n            />\n          </View>\n        </View>\n      </View>\n    </View>;\n  };\n};\n\nlet render = () => <AdjustableLogo />;\n"
  },
  {
    "path": "examples/SpringExample.re",
    "content": "open Revery;\nopen Revery.UI;\nopen Revery.UI.Components;\n\nmodule SpringyLogo = {\n  let make = (~width, ~onMouseDown, ~onMouseUp, ()) => {\n    let intWidth = int_of_float(width);\n\n    <View onMouseDown onMouseUp>\n      <Image\n        src={`File(\"outrun-logo.png\")}\n        style=Style.[\n          width(intWidth),\n          height(128),\n          cursor(MouseCursors.pointer),\n        ]\n      />\n    </View>;\n  };\n};\n\nmodule SliderControl = {\n  module Styles = {\n    let sliderContainerStyle =\n      Style.[\n        margin(10),\n        borderBottom(~width=1, ~color=Colors.darkGray),\n        flexDirection(`Row),\n        justifyContent(`Center),\n        alignItems(`Center),\n      ];\n\n    let textStyle = Style.[color(Colors.white), width(100), margin(14)];\n  };\n\n  let float_rounded_string = v => v |> int_of_float |> string_of_int;\n\n  let make =\n      (~text, ~minimumValue, ~maximumValue, ~value, ~onValueChanged, ()) => {\n    <View style=Styles.sliderContainerStyle>\n      <Text style=Styles.textStyle fontSize=16. text />\n      <Slider\n        onValueChanged={v => onValueChanged(v)}\n        value\n        minimumValue\n        maximumValue\n      />\n      <Text\n        style=Styles.textStyle\n        fontSize=16.\n        text={\"Value: \" ++ float_rounded_string(value)}\n      />\n    </View>;\n  };\n};\n\nmodule Example = {\n  module Styles = {\n    let containerStyle =\n      Style.[\n        flexGrow(1),\n        justifyContent(`Center),\n        alignItems(`Center),\n        flexDirection(`Column),\n      ];\n  };\n  let%component make = () => {\n    let%hook (stiffness, setStiffness) =\n      Hooks.reducer(~initialState=160., (value, _) => value);\n    let%hook (damping, setDamping) =\n      Hooks.reducer(~initialState=10., (value, _) => value);\n    let%hook (targetPosition, setTargetPosition) =\n      Hooks.reducer(~initialState=256.0, (value, _) => value);\n    let%hook (logoWidth, setImmediately) =\n      Hooks.spring(\n        ~name=\"Spring\",\n        ~target=targetPosition,\n        ~initialState=\n          Spring.{\n            value: 256.,\n            velocity: 100.,\n            acceleration: 0.,\n            time: Time.now(),\n          },\n        ~restThreshold=1.0,\n        Spring.Options.create(~damping, ~stiffness, ()),\n      );\n    let setImmediately = value => {\n      setTargetPosition(value);\n      setImmediately(value);\n    };\n\n    let onMouseDown = _ => {\n      setTargetPosition(512.0);\n    };\n    let onMouseUp = _ => setTargetPosition(256.0);\n\n    <Center>\n      <Center> <SpringyLogo width=logoWidth onMouseDown onMouseUp /> </Center>\n      <View style=Styles.containerStyle>\n        <SliderControl\n          text=\"Damping: \"\n          minimumValue=1.\n          maximumValue=100.\n          value=damping\n          onValueChanged={v => setDamping(v)}\n        />\n        <SliderControl\n          text=\"Stiffness: \"\n          minimumValue=1.\n          maximumValue=500.\n          value=stiffness\n          onValueChanged={v => setStiffness(v)}\n        />\n        <SliderControl\n          text=\"Width: \"\n          minimumValue=128.\n          maximumValue=640.\n          value=logoWidth\n          onValueChanged={v => setImmediately(v)}\n        />\n      </View>\n    </Center>;\n  };\n};\n\nlet render = () => <Example />;\n"
  },
  {
    "path": "examples/Stopwatch.re",
    "content": "open Revery;\nopen Revery.Math;\nopen Revery.UI;\nopen Revery.UI.Components;\n\nmodule Clock = {\n  type dispose = unit => unit;\n  let noop = () => ();\n\n  type state = {\n    dispose,\n    isRunning: bool,\n    elapsedTime: Time.t,\n  };\n\n  type action =\n    | Start(dispose)\n    | Stop\n    | TimerTick(Time.t);\n\n  let reducer = (a, s) =>\n    switch (a) {\n    | Start(f) => {dispose: f, isRunning: true, elapsedTime: Time.zero}\n    | Stop =>\n      s.dispose();\n      let ret = {dispose: noop, isRunning: false, elapsedTime: Time.zero};\n      ret;\n    | TimerTick(t) => {\n        ...s,\n        elapsedTime: s.isRunning ? Time.(s.elapsedTime + t) : s.elapsedTime,\n      }\n    };\n\n  let%component make = () => {\n    let%hook (state, dispatch) =\n      Hooks.reducer(\n        ~initialState={\n          isRunning: false,\n          dispose: noop,\n          elapsedTime: Time.zero,\n        },\n        reducer,\n      );\n\n    /*\n     * We'll make sure to dispatch the 'Stop' action when unmounting,\n     * so we don't have a runaway timer!\n     */\n    let%hook () = Hooks.effect(OnMount, () => Some(() => dispatch(Stop)));\n\n    let startStop = () =>\n      state.isRunning\n        ? dispatch(Stop)\n        /*\n         * If we're not already running, we'll start a timer job\n         * and use the delta time it passes to update our reducer.\n         */\n        : {\n          let dispose =\n            Tick.interval(\n              ~name=\"Stopwatch Interval\",\n              t => dispatch(TimerTick(t)),\n              Time.zero,\n            );\n\n          /* We'll also keep a handle on the dispose function so we can make sure its called on stop*/\n          dispatch(Start(dispose));\n        };\n\n    let buttonText = state.isRunning ? \"STOP\" : \"START\";\n\n    let marcherOpacity = state.isRunning ? 1.0 : 0.0;\n    let getMarcherPosition = t =>\n      sin(Time.toFloatSeconds(t) *. 2. *. pi) /. 2. +. 0.5;\n\n    <View\n      style=Style.[\n        position(`Absolute),\n        justifyContent(`Center),\n        alignItems(`Center),\n        bottom(0),\n        top(0),\n        left(0),\n        right(0),\n      ]>\n      <View\n        style=Style.[\n          margin(20),\n          width(150),\n          borderBottom(~color=Colors.gray, ~width=2),\n        ]>\n        <Text\n          style=Style.[color(Colors.white), marginVertical(20), width(200)]\n          fontSize=24.\n          text={Time.toString(state.elapsedTime)}\n        />\n        <Opacity opacity=marcherOpacity>\n          <View\n            style=Style.[\n              position(`Absolute),\n              bottom(0),\n              left(\n                int_of_float(getMarcherPosition(state.elapsedTime) *. 146.),\n              ),\n              width(4),\n              height(4),\n              backgroundColor(Color.hex(\"#90f7ff\")),\n            ]\n          />\n        </Opacity>\n      </View>\n      <Button title=buttonText onClick=startStop />\n    </View>;\n  };\n};\n\nlet render = () => <Clock />;\n"
  },
  {
    "path": "examples/TextExample.re",
    "content": "open Revery;\nopen Revery.UI;\nopen Revery.UI.Components;\n\nlet containerStyle =\n  Style.[\n    position(`Absolute),\n    top(0),\n    bottom(0),\n    left(0),\n    right(0),\n    alignItems(`Center),\n    justifyContent(`SpaceAround),\n    flexDirection(`Column),\n  ];\n\nlet slidersViewStyle = Style.[height(300)];\n\nlet textStyle =\n  Style.[\n    color(Colors.white),\n    width(100),\n    margin(14),\n    textWrap(TextWrapping.NoWrap),\n  ];\n\nlet controlsStyle =\n  Style.[\n    margin(10),\n    flexDirection(`Row),\n    justifyContent(`Center),\n    alignItems(`Center),\n  ];\n\nlet overflowStyles = (style, textWidth) =>\n  Style.[\n    color(Colors.white),\n    width(textWidth),\n    textOverflow(style),\n    lineHeight(1.5),\n    border(~color=Colors.blueViolet, ~width=1),\n    backgroundColor(Colors.black),\n  ];\n\nmodule SampleText = {\n  let%component make = () => {\n    let%hook (fontSizeSliderVal, setFontSize) =\n      Hooks.reducer(~initialState=20., (value, _) => value);\n    let%hook (widthSliderVal, setWidth) =\n      Hooks.reducer(~initialState=200., (value, _) => value);\n    let%hook (hyphenate, setHyphenate) = Hooks.state(false);\n\n    let textContent =\n      \"All work and no play makes Jack a dull boy. \"\n      ++ \"The quick brown fox jumps over the lazy dog.\";\n    let maxFontSize = 40.;\n    let maxWidth = 400.;\n    let textFontSize = fontSizeSliderVal;\n    let textWidth = int_of_float(widthSliderVal);\n    let wrapping =\n      if (hyphenate) {TextWrapping.WrapHyphenate} else {TextWrapping.Wrap};\n\n    <View style=containerStyle>\n      <View>\n        <View style=Style.[height(40), marginBottom(8)]>\n          <Text\n            fontSize=textFontSize\n            style={overflowStyles(`Ellipsis, textWidth)}\n            text={textContent ++ \" \" ++ textContent}\n          />\n        </View>\n        <View style=Style.[height(40), marginBottom(8)]>\n          <Text\n            fontSize=textFontSize\n            style={overflowStyles(`UserDefined(\"£\"), textWidth)}\n            text={textContent ++ \" \" ++ textContent}\n          />\n        </View>\n        <View style=Style.[height(80)]>\n          <Text\n            style=Style.[\n              color(Colors.white),\n              lineHeight(1.5),\n              textWrap(wrapping),\n              width(int_of_float(widthSliderVal)),\n              border(~color=Colors.blueViolet, ~width=1),\n              backgroundColor(Colors.black),\n            ]\n            fontSize=textFontSize\n            text=textContent\n          />\n        </View>\n      </View>\n      <View>\n        <View style=controlsStyle>\n          <Text style=textStyle fontSize=16. text=\"Font size: \" />\n          <Slider\n            onValueChanged={v => setFontSize(v)}\n            value=fontSizeSliderVal\n            maximumValue=maxFontSize\n          />\n          <Text\n            style=textStyle\n            fontSize=16.\n            text={\"Value: \" ++ (fontSizeSliderVal |> string_of_float)}\n          />\n        </View>\n        <View style=controlsStyle>\n          <Text style=textStyle fontSize=16. text=\"Width: \" />\n          <Slider\n            onValueChanged={w => setWidth(w)}\n            value=widthSliderVal\n            maximumValue=maxWidth\n          />\n          <Text\n            style=textStyle\n            fontSize=16.\n            text={\"Value: \" ++ (widthSliderVal |> string_of_float)}\n          />\n        </View>\n        <View style=controlsStyle>\n          <Text style=textStyle fontSize=16. text=\"Hyphenate?\" />\n          <Checkbox\n            checkedColor=Colors.green\n            onChange={() => setHyphenate(h => !h)}\n            style=Style.[border(~width=2, ~color=Colors.green)]\n            checked=hyphenate\n          />\n        </View>\n      </View>\n    </View>;\n  };\n};\n\nlet render = () => <SampleText />;\n"
  },
  {
    "path": "examples/TodoExample.re",
    "content": "open Revery;\nopen Revery.UI;\nopen Revery.UI.Components;\n\nmodule Constants = {\n  let fontSize = 12.;\n};\n\nmodule Theme = {\n  let fontSize = 16.;\n  let rem = factor => fontSize *. factor;\n  let remi = factor => rem(factor) |> int_of_float;\n\n  let appBackground = Color.hex(\"#f4edfe\");\n  let textColor = Color.hex(\"#513B70\");\n  let dimmedTextColor = Color.hex(\"#DAC5F7\");\n  let titleTextColor = Color.hex(\"#EADDFC\");\n\n  let panelBackground = Color.hex(\"#F9F5FF\");\n  let panelBorderColor = Color.hex(\"#EADDFC\");\n  let panelBorder = Style.border(~width=1, ~color=panelBorderColor);\n\n  let buttonColor = Color.hex(\"#9573C4\");\n  let hoveredButtonColor = Color.hex(\"#C9AEF0\");\n\n  let dangerColor = Color.hex(\"#f7c5c6\");\n};\n\nmodule Filter = {\n  type t =\n    | All\n    | Active\n    | Completed;\n\n  let toString = (v: t) =>\n    switch (v) {\n    | All => \"All\"\n    | Active => \"Active\"\n    | Completed => \"Completed\"\n    };\n};\n\nmodule Button = {\n  module Styles = {\n    let box = (~isSelected, ~isHovered) =>\n      Style.[\n        position(`Relative),\n        justifyContent(`Center),\n        alignItems(`Center),\n        paddingVertical(Theme.remi(0.15)),\n        paddingHorizontal(Theme.remi(0.5)),\n        marginHorizontal(Theme.remi(0.2)),\n        border(\n          ~width=1,\n          ~color=\n            switch (isSelected, isHovered) {\n            | (true, _) => Theme.buttonColor\n            | (false, true) => Theme.hoveredButtonColor\n            | (false, false) => Colors.transparentWhite\n            },\n        ),\n        borderRadius(2.),\n      ];\n\n    let text =\n      Style.[color(Theme.buttonColor), textWrap(TextWrapping.NoWrap)];\n  };\n\n  let%component make =\n                (\n                  ~label,\n                  ~onClick=?,\n                  ~isSelected=false,\n                  ~tabindex=?,\n                  ~onFocus=?,\n                  ~onBlur=?,\n                  (),\n                ) => {\n    let%hook (isHovered, onMouseOver, onMouseOut) = Hooks.hover();\n\n    <Clickable ?onClick ?onFocus ?onBlur ?tabindex>\n      <View\n        style={Styles.box(~isSelected, ~isHovered)} onMouseOver onMouseOut>\n        <Text style=Styles.text text=label fontSize={Theme.rem(0.8)} />\n      </View>\n    </Clickable>;\n  };\n};\n\nmodule Checkbox = {\n  module Styles = {\n    let box =\n      Style.[\n        width(Theme.remi(1.5)),\n        height(Theme.remi(1.5)),\n        justifyContent(`Center),\n        alignItems(`Center),\n        Theme.panelBorder,\n      ];\n\n    let checkmark =\n      Style.[\n        color(Theme.hoveredButtonColor),\n        textWrap(TextWrapping.NoWrap),\n        transform(Transform.[TranslateY(2.)]),\n      ];\n  };\n\n  let make = (~isChecked, ~onToggle, ()) => {\n    <Clickable onClick=onToggle>\n      <View style=Styles.box>\n        <Text\n          text={isChecked ? {||} : \"\"}\n          fontSize=Theme.fontSize\n          fontFamily={Font.Family.fromFile(\"FontAwesome5FreeSolid.otf\")}\n          style=Styles.checkmark\n        />\n      </View>\n    </Clickable>;\n  };\n};\n\nmodule AddTodo = {\n  module Styles = {\n    let container =\n      Style.[\n        flexDirection(`Row),\n        backgroundColor(Theme.panelBackground),\n        Theme.panelBorder,\n        margin(2),\n        alignItems(`Center),\n        overflow(`Hidden),\n      ];\n\n    let toggleAll = areAllCompleted =>\n      Style.[\n        color(areAllCompleted ? Theme.textColor : Theme.dimmedTextColor),\n        transform(Transform.[TranslateY(2.)]),\n        marginLeft(12),\n      ];\n\n    let input =\n      Style.[border(~width=0, ~color=Colors.transparentWhite), width(4000)]; // Not ideal, should be possible to use flexGrow(1) instead\n  };\n\n  let make = (~text, ~areAllCompleted, ~onInput, ~onSubmit, ~onToggleAll, ()) => {\n    let onKeyDown = (event: NodeEvents.keyEventParams) =>\n      if (event.keycode == 13) {\n        onSubmit();\n      };\n\n    <View style=Styles.container>\n      <Clickable onClick=onToggleAll>\n        <Text\n          text={||}\n          fontSize=Theme.fontSize\n          fontFamily={Font.Family.fromFile(\"FontAwesome5FreeSolid.otf\")}\n          style={Styles.toggleAll(areAllCompleted)}\n        />\n      </Clickable>\n      <Input\n        style=Styles.input\n        fontSize=Theme.fontSize\n        placeholder=\"Add your Todo here\"\n        value=text\n        onChange={(value, _) => onInput(value)}\n        onKeyDown\n      />\n    </View>;\n  };\n};\n\nmodule Todo = {\n  module Styles = {\n    let box =\n      Style.[\n        flexDirection(`Row),\n        margin(2),\n        paddingVertical(4),\n        paddingHorizontal(8),\n        alignItems(`Center),\n        backgroundColor(Theme.panelBackground),\n        Theme.panelBorder,\n      ];\n\n    let text = isChecked =>\n      Style.[\n        margin(6),\n        color(isChecked ? Theme.dimmedTextColor : Theme.textColor),\n        flexGrow(1),\n      ];\n\n    let removeButton = isHovered =>\n      Style.[\n        color(isHovered ? Theme.dangerColor : Colors.transparentWhite),\n        transform(Transform.[TranslateY(2.)]),\n        marginRight(6),\n      ];\n  };\n\n  type t = {\n    id: int,\n    task: string,\n    isDone: bool,\n  };\n\n  let%component make = (~task, ~onToggle, ~onRemove, ()) => {\n    let%hook (isHovered, setHovered) = Hooks.state(false);\n\n    <View\n      style=Styles.box\n      onMouseOver={_ => setHovered(_wasHovered => true)}\n      onMouseOut={_ => setHovered(_wasHovered => false)}>\n      <Checkbox isChecked={task.isDone} onToggle />\n      <Text\n        style={Styles.text(task.isDone)}\n        fontSize=Theme.fontSize\n        text={task.task}\n      />\n      <Clickable onClick=onRemove>\n        <Text\n          text={||}\n          fontFamily={Font.Family.fromFile(\"FontAwesome5FreeSolid.otf\")}\n          fontSize=Theme.fontSize\n          style={Styles.removeButton(isHovered)}\n        />\n      </Clickable>\n    </View>;\n  };\n};\n\nmodule Footer = {\n  module Styles = {\n    let container =\n      Style.[flexDirection(`Row), justifyContent(`SpaceBetween)];\n\n    let filterButtonsContainer =\n      Style.[\n        flexGrow(1),\n        width(0),\n        flexDirection(`Row),\n        alignItems(`Center),\n        justifyContent(`Center),\n        alignSelf(`Center),\n        transform(Transform.[TranslateY(-2.)]),\n      ];\n\n    let leftFlexContainer = Style.[flexGrow(1), width(0)];\n\n    let rightFlexContainer =\n      Style.[\n        flexGrow(1),\n        width(0),\n        flexDirection(`Row),\n        justifyContent(`FlexEnd),\n      ];\n\n    let itemsLeft =\n      Style.[color(Theme.buttonColor), textWrap(TextWrapping.NoWrap)];\n\n    let clearCompleted = isHovered =>\n      Style.[\n        color(isHovered ? Theme.hoveredButtonColor : Theme.buttonColor),\n        textWrap(TextWrapping.NoWrap),\n      ];\n  };\n\n  let make =\n      (\n        ~activeCount,\n        ~completedCount,\n        ~currentFilter,\n        ~onSelectFilter,\n        ~onClearCompleted,\n        (),\n      ) => {\n    let itemsLeft = {\n      let text =\n        switch (activeCount) {\n        | 1 => \"1 item left\"\n        | n => Printf.sprintf(\"%i items left\", n)\n        };\n\n      <Text text fontSize={Theme.rem(0.85)} style=Styles.itemsLeft />;\n    };\n\n    let filterButtonsView = {\n      let button = filter =>\n        <Button\n          label={Filter.toString(filter)}\n          isSelected={currentFilter == filter}\n          onClick={() => onSelectFilter(filter)}\n        />;\n\n      <View style=Styles.filterButtonsContainer>\n        {button(All)}\n        {button(Active)}\n        {button(Completed)}\n      </View>;\n    };\n\n    module ClearCompleted = {\n      let%component make = (~onClearCompleted, ~completedCount, ()) => {\n        let%hook (isHovered, setHovered) = Hooks.state(false);\n\n        let text =\n          switch (completedCount) {\n          | 0 => \"\"\n          | n => Printf.sprintf(\"Clear completed (%i)\", n)\n          };\n\n        <Clickable onClick=onClearCompleted>\n          <View\n            onMouseOver={_ => setHovered(_wasHovered => true)}\n            onMouseOut={_ => setHovered(_wasHovered => false)}>\n            <Text\n              text\n              fontSize={Theme.rem(0.85)}\n              style={Styles.clearCompleted(isHovered)}\n            />\n          </View>\n        </Clickable>;\n      };\n    };\n\n    <View style=Styles.container>\n      <View style=Styles.leftFlexContainer> itemsLeft </View>\n      filterButtonsView\n      <View style=Styles.rightFlexContainer>\n        <ClearCompleted onClearCompleted completedCount />\n      </View>\n    </View>;\n  };\n};\n\nmodule TodoMVC = {\n  module Styles = {\n    let appContainer =\n      Style.[\n        position(`Absolute),\n        top(0),\n        bottom(0),\n        left(0),\n        right(0),\n        alignItems(`Stretch),\n        justifyContent(`Center),\n        flexDirection(`Column),\n        backgroundColor(Theme.appBackground),\n        paddingVertical(2),\n        paddingHorizontal(6),\n        overflow(`Hidden),\n      ];\n\n    let title =\n      Style.[\n        color(Theme.titleTextColor),\n        alignSelf(`Center),\n        marginTop(Theme.remi(2.)),\n        textWrap(TextWrapping.NoWrap),\n      ];\n\n    let todoList = Style.[flexGrow(1)];\n  };\n\n  type state = {\n    todos: list(Todo.t),\n    filter: Filter.t,\n    inputValue: string,\n    nextId: int,\n  };\n\n  let initialState = {\n    todos:\n      Todo.[\n        {id: 0, task: \"Buy Milk\", isDone: false},\n        {id: 1, task: \"Wag the Dog\", isDone: true},\n      ],\n    filter: All,\n    inputValue: \"\",\n    nextId: 2,\n  };\n\n  type action =\n    | Add\n    | SetFilter(Filter.t)\n    | UpdateInput(string)\n    | Toggle(int)\n    | Remove(int)\n    | ToggleAll\n    | ClearCompleted;\n\n  let reducer = (action: action, state: state) =>\n    switch (action) {\n    | Add => {\n        ...state,\n        todos: [\n          {id: state.nextId, task: state.inputValue, isDone: false},\n          ...state.todos,\n        ],\n        inputValue: \"\",\n        nextId: state.nextId + 1,\n      }\n\n    | UpdateInput(text) => {...state, inputValue: text}\n\n    | Toggle(id) =>\n      let todos =\n        List.map(\n          (item: Todo.t) =>\n            item.id == id ? {...item, isDone: !item.isDone} : item,\n          state.todos,\n        );\n      {...state, todos};\n\n    | Remove(id) =>\n      let todos = List.filter((item: Todo.t) => item.id != id, state.todos);\n      {...state, todos};\n\n    | SetFilter(filter) => {...state, filter}\n\n    | ToggleAll =>\n      let areAllCompleted =\n        List.for_all((item: Todo.t) => item.isDone, state.todos);\n      let todos =\n        List.map(\n          (item: Todo.t) => {...item, isDone: !areAllCompleted},\n          state.todos,\n        );\n      {...state, todos};\n\n    | ClearCompleted =>\n      let todos = List.filter((item: Todo.t) => !item.isDone, state.todos);\n      {...state, todos};\n    };\n\n  let%component make = () => {\n    let%hook ({todos, inputValue, filter as currentFilter, _}, dispatch) =\n      Hooks.reducer(~initialState, reducer);\n\n    let header = {\n      <Text text=\"todoMVC\" fontSize={Theme.rem(4.)} style=Styles.title />;\n    };\n\n    let addTodoView = {\n      let onInput = value => dispatch(UpdateInput(value));\n      let onSubmit = () => dispatch(Add);\n      let onToggleAll = () => dispatch(ToggleAll);\n      let areAllCompleted =\n        List.for_all((item: Todo.t) => item.isDone, todos);\n\n      <AddTodo text=inputValue areAllCompleted onInput onSubmit onToggleAll />;\n    };\n\n    let todoListView = {\n      let onToggle = (id, ()) => dispatch(Toggle(id));\n      let onRemove = (id, ()) => dispatch(Remove(id));\n      let filteredTodos =\n        List.filter(\n          task =>\n            switch (filter) {\n            | All => true\n            | Active => !task.Todo.isDone\n            | Completed => task.Todo.isDone\n            },\n          todos,\n        );\n\n      <ScrollView style=Styles.todoList>\n        <View>\n          {List.map(\n             (task: Todo.t) =>\n               <Todo\n                 task\n                 onToggle={onToggle(task.id)}\n                 onRemove={onRemove(task.id)}\n               />,\n             filteredTodos,\n           )\n           |> React.listToElement}\n        </View>\n      </ScrollView>;\n    };\n\n    let footer = {\n      let onSelectFilter = filter => dispatch(SetFilter(filter));\n      let onClearCompleted = () => dispatch(ClearCompleted);\n      let activeCount =\n        todos |> List.filter((item: Todo.t) => !item.isDone) |> List.length;\n      let completedCount =\n        todos |> List.filter((item: Todo.t) => item.isDone) |> List.length;\n      <Footer\n        activeCount\n        completedCount\n        currentFilter\n        onSelectFilter\n        onClearCompleted\n      />;\n    };\n\n    <View style=Styles.appContainer>\n      header\n      addTodoView\n      todoListView\n      footer\n    </View>;\n  };\n};\n\nlet render = () => <TodoMVC />;\n"
  },
  {
    "path": "examples/TreeView.re",
    "content": "open Revery;\nopen Revery.UI;\nopen Revery.UI.Components;\n\nmodule TreeView = {\n  let stringTree =\n    Tree.(\n      Node(\n        {data: \"root\", id: 1, status: Open},\n        [\n          Node(\n            {data: \"subfolder 1\", id: 2, status: Open},\n            [\n              Node(\n                {data: \"subdirectory 1\", id: 3, status: Closed},\n                [Empty, Empty],\n              ),\n            ],\n          ),\n          Node(\n            {data: \"home\", id: 4, status: Open},\n            [\n              Node(\n                {status: Closed, id: 5, data: \"downloads\"},\n                [Empty, Empty],\n              ),\n              Node(\n                {data: \"desktop\", id: 6, status: Open},\n                [\n                  Node(\n                    {status: Open, id: 7, data: \"subfolder 2\"},\n                    [\n                      Node(\n                        {status: Open, id: 8, data: \"pictures\"},\n                        [\n                          Node({status: Closed, id: 12, data: \"Images\"}, []),\n                          Node(\n                            {status: Closed, id: 10, data: \"holiday 2018\"},\n                            [],\n                          ),\n                          Node(\n                            {status: Closed, id: 11, data: \"Graduation 2017\"},\n                            [],\n                          ),\n                        ],\n                      ),\n                      Empty,\n                    ],\n                  ),\n                  Node(\n                    {data: \"subfolder 3\", id: 9, status: Closed},\n                    [Empty, Empty],\n                  ),\n                ],\n              ),\n            ],\n          ),\n        ],\n      )\n    );\n\n  type heirarchy = {\n    name: string,\n    level: string,\n  };\n\n  let animalKingdom =\n    Tree.(\n      Node(\n        {\n          id: 1,\n          status: Open,\n          data: {\n            name: \"Animalia\",\n            level: \"Kingdom\",\n          },\n        },\n        [\n          Node(\n            {\n              id: 2,\n              data: {\n                name: \"chordate\",\n                level: \"phylum\",\n              },\n              status: Open,\n            },\n            [\n              Node(\n                {\n                  id: 3,\n                  status: Open,\n                  data: {\n                    name: \"mammal\",\n                    level: \"class\",\n                  },\n                },\n                [\n                  Node(\n                    {\n                      id: 4,\n                      status: Open,\n                      data: {\n                        name: \"carnivora\",\n                        level: \"order\",\n                      },\n                    },\n                    [Empty, Empty],\n                  ),\n                  Empty,\n                ],\n              ),\n              Empty,\n            ],\n          ),\n          Node(\n            {\n              id: 5,\n              status: Open,\n              data: {\n                name: \"anthropoda\",\n                level: \"phylum\",\n              },\n            },\n            [\n              Node(\n                {\n                  id: 6,\n                  status: Open,\n                  data: {\n                    name: \"insect\",\n                    level: \"class\",\n                  },\n                },\n                [\n                  Node(\n                    {\n                      id: 7,\n                      status: Open,\n                      data: {\n                        name: \"dipthera\",\n                        level: \"order\",\n                      },\n                    },\n                    [Empty, Empty],\n                  ),\n                  Empty,\n                ],\n              ),\n              Empty,\n            ],\n          ),\n        ],\n      )\n    );\n\n  let emptyRenderer =\n    Some(\n      indent =>\n        <Text\n          text={\"There is no file present\" ++ \"\\n\"}\n          fontSize=10.\n          style=Style.[marginLeft(indent * 20), color(Colors.rebeccaPurple)]\n        />,\n    );\n\n  let make = (~renderer=?, ()) => {\n    switch (renderer) {\n    | Some(fn) => <Tree tree=animalKingdom nodeRenderer=fn />\n    | None => <Tree tree=stringTree nodeRenderer=Tree.default emptyRenderer />\n    };\n  };\n\n  let customRenderer = (~indent, content) => {\n    open Tree;\n    let {data, _} = content;\n    let textStyles = Style.[color(Colors.black)];\n    <Padding padding=5>\n      <View\n        style=Style.[\n          justifyContent(`Center),\n          alignItems(`Center),\n          marginLeft(indent * 30),\n          backgroundColor(Colors.white),\n          width(80),\n          height(40),\n        ]>\n        <Text text={data.name} fontSize=10. style=textStyles />\n        <Text text={data.level} fontSize=10. style=textStyles />\n      </View>\n    </Padding>;\n  };\n};\n\nlet titleStyles = Style.[color(Colors.white), marginVertical(10)];\n\nlet render = _w => {\n  let exampleContainer =\n    Style.[\n      flexDirection(`Column),\n      justifyContent(`Center),\n      alignItems(`Center),\n      width(200),\n    ];\n  <View\n    style=Style.[\n      paddingTop(10),\n      justifyContent(`Center),\n      alignItems(`FlexStart),\n      flexDirection(`Row),\n      position(`Absolute),\n      top(0),\n      right(0),\n      left(0),\n      bottom(0),\n    ]>\n    <View style=exampleContainer>\n      <Text style=titleStyles fontSize=15. text=\"Custom Renderer\" />\n      <TreeView renderer=TreeView.customRenderer />\n    </View>\n    <View style=exampleContainer>\n      <Text\n        style=titleStyles\n        fontSize=15.\n        text=\"Default Renderer (with emptyRenderer)\"\n      />\n      <TreeView />\n    </View>\n  </View>;\n};\n"
  },
  {
    "path": "examples/URLFileOpen.re",
    "content": "open Revery;\nopen Revery.UI;\nopen Revery.UI.Components;\n\nlet containerStyle =\n  Style.[\n    position(`Absolute),\n    top(0),\n    bottom(0),\n    left(0),\n    right(0),\n    alignItems(`Center),\n    justifyContent(`Center),\n    flexDirection(`Column),\n  ];\n\nmodule Example = {\n  let%component make = () => {\n    let%hook (url, setURL) =\n      Hooks.state(\"https://github.com/revery-ui/revery\");\n\n    <View style=containerStyle>\n      <Input\n        placeholder=\"URL to open\"\n        onChange={(value, _) => setURL(_ => value)}\n        value=url\n      />\n      <Button\n        onClick={_ => Native.Shell.openURL(url) |> ignore}\n        title=\"Open!\"\n      />\n    </View>;\n  };\n};\n\nlet render = () => <Example />;\n"
  },
  {
    "path": "examples/WavFilePlaybackExample.re",
    "content": "open Revery;\nopen Revery.UI;\nopen Revery.UI.Components;\nopen Revery.Native;\n\nmodule AudioHelpers = {\n  let getBitsPerSample =\n    fun\n    | Sdl2.Audio.Format.U8\n    | S8 => 8\n    | S16LSB\n    | S16MSB\n    | U16LSB\n    | U16MSB => 16\n    | S32LSB\n    | S32MSB\n    | F32LSB\n    | F32MSB => 32;\n\n  let getAudioDuration = (spec: Sdl2.Audio.Spec.t, bytes) => {\n    bytes / (spec.freq * spec.channels * (getBitsPerSample(spec.format) / 8));\n  };\n};\n\nmodule Styles = {\n  let container =\n    Style.[\n      position(`Absolute),\n      top(0),\n      bottom(0),\n      left(0),\n      right(0),\n      alignItems(`Center),\n      justifyContent(`Center),\n      flexDirection(`Column),\n    ];\n  let text = Style.[color(Colors.white), margin(14)];\n  let audioPlayerTrack =\n    Style.[\n      width(200),\n      marginBottom(14),\n      padding(2),\n      borderRadius(8.),\n      backgroundColor(Colors.dimGrey),\n      alignItems(`Center),\n      flexDirection(`Row),\n      justifyContent(`Center),\n    ];\n};\n\nmodule WavFilePlaybackExamples = {\n  let%component make = () => {\n    let%hook (filename, setFilename) = Hooks.state(\"\");\n    let%hook (audioData, setAudioData) = Hooks.state(None);\n    let%hook (audioDevice, setAudioDevice) = Hooks.state(None);\n    let%hook (audioDeviceStatus, setAudioDeviceStatus) =\n      Hooks.state(Sdl2.Audio.Device.Status.Stopped);\n    let%hook (audioQueuedBytes, setAudioQueuedBytes) = Hooks.state(0);\n    let%hook () =\n      Hooks.tick(~name=\"\", ~tickRate=Time.milliseconds(50), _ => {\n        switch (audioDevice) {\n        | Some(device) =>\n          let status = Sdl2.Audio.Device.getStatus(device);\n          setAudioDeviceStatus(_ => status);\n          let bytes = Sdl2.Audio.getQueuedSize(device);\n          setAudioQueuedBytes(_ => bytes);\n        | _ => ()\n        }\n      });\n\n    let audioQueuedBytes = float(audioQueuedBytes);\n    let audioTotalBytes =\n      switch (audioData) {\n      | Some((_, _, len)) => float(len)\n      | _ => 0.\n      };\n    let audioPlayedBytes =\n      audioQueuedBytes > 0. ? audioTotalBytes -. audioQueuedBytes : 0.;\n    let audioPlaybackProgress = audioPlayedBytes /. audioTotalBytes;\n    let audioDuration =\n      switch (audioData) {\n      | Some((spec: Sdl2.Audio.Spec.t, _, bytes)) =>\n        AudioHelpers.getAudioDuration(spec, bytes)\n      | _ => 0\n      };\n    let audioElapsed =\n      int_of_float(float(audioDuration) *. audioPlaybackProgress);\n\n    let openAudioDevice = desiredSpec => {\n      switch (audioDevice) {\n      | Some(device) => Sdl2.Audio.Device.close(device)\n      | _ => ()\n      };\n      switch (\n        Sdl2.Audio.Device.open_(\n          None,\n          false,\n          desiredSpec,\n          Sdl2.Audio.Device.AllowedChanges.none,\n        )\n      ) {\n      | Error(err) => prerr_endline(err)\n      | Ok((device, _obstainedSpec)) => setAudioDevice(_ => Some(device))\n      };\n    };\n\n    let loadWavFile = filename => {\n      switch (Sdl2.Audio.Wav.load(filename)) {\n      | Error(err) => prerr_endline(err)\n      | Ok(data) =>\n        setAudioData(_ => Some(data));\n        let (spec, _, _) = data;\n        openAudioDevice(spec);\n      };\n    };\n\n    let openFile = () => {\n      let files =\n        Dialog.openFiles(\n          ~fileTypes=[|\"wav\", \"wave\"|],\n          ~allowMultiple=false,\n          ~canChooseDirectories=false,\n          ~showHidden=false,\n          ~title=\"Revery Open File Example\",\n          ~buttonText=\"Open file in Revery\",\n          (),\n        );\n      switch (files) {\n      | Some([|path|]) =>\n        setFilename(_ => path |> Filename.basename);\n        loadWavFile(path);\n      | _ => ()\n      };\n    };\n\n    let play = () => {\n      switch (audioData, audioDevice) {\n      | (Some((_spec, buf, len)), Some(device)) =>\n        if (audioQueuedBytes > 0.) {\n          Sdl2.Audio.Device.pause(device, false);\n        } else {\n          switch (Sdl2.Audio.queue(device, buf, len)) {\n          | Error(err) => prerr_endline(err)\n          | Ok(_) => Sdl2.Audio.Device.pause(device, false)\n          };\n        }\n      | _ => ()\n      };\n    };\n\n    let pause = () => {\n      switch (audioDevice) {\n      | Some(device) => Sdl2.Audio.Device.pause(device, true)\n      | _ => ()\n      };\n    };\n\n    if (filename == \"\") {\n      <View style=Styles.container>\n        <Text style=Styles.text fontSize=20. text=\"WAV File Playback\" />\n        <Button\n          title=\"Open File\"\n          fontSize=12.\n          height=50\n          width=100\n          onClick=openFile\n        />\n      </View>;\n    } else {\n      <View style=Styles.container>\n        <Row>\n          <Text style=Styles.text fontSize=20. text=\"Audio Loaded\" />\n        </Row>\n        <Row> <Text fontSize=12. style=Styles.text text=filename /> </Row>\n        <Row>\n          <View style=Styles.audioPlayerTrack>\n            <Text\n              fontSize=12.\n              style=Styles.text\n              text=Time.(seconds(audioElapsed) |> toString)\n            />\n            <Slider\n              value=audioPlayedBytes\n              minimumValue=0.\n              maximumValue=audioTotalBytes\n              thumbThickness=0\n              thumbLength=0\n              thumbColor={Color.hex(\"#90f7ff\")}\n            />\n            <Text\n              fontSize=12.\n              style=Styles.text\n              text=Time.(seconds(audioDuration) |> toString)\n            />\n          </View>\n        </Row>\n        <Row>\n          {switch (audioDeviceStatus) {\n           | Stopped\n           | Paused =>\n             <Button\n               title=\"Play\"\n               fontSize=12.\n               height=50\n               width=100\n               onClick=play\n             />\n           | Playing =>\n             audioQueuedBytes == 0.\n               ? <Button\n                   title=\"Play\"\n                   fontSize=12.\n                   height=50\n                   width=100\n                   onClick=play\n                 />\n               : <Button\n                   title=\"Pause\"\n                   fontSize=12.\n                   height=50\n                   width=100\n                   onClick=pause\n                 />\n           }}\n          <Button\n            title=\"Open File\"\n            fontSize=12.\n            height=50\n            width=100\n            onClick=openFile\n          />\n        </Row>\n      </View>;\n    };\n  };\n};\n\nlet render = () => <WavFilePlaybackExamples />;\n"
  },
  {
    "path": "examples/WindowControl.re",
    "content": "open Revery;\nopen Revery.UI;\nopen Revery.UI.Components;\n\nmodule Styles = {\n  open Style;\n\n  let outer = [\n    position(`Absolute),\n    top(0),\n    bottom(0),\n    left(0),\n    right(0),\n    justifyContent(`Center),\n    alignItems(`Center),\n  ];\n\n  let text = Style.[color(Colors.white)];\n};\n\nlet winControl = (~window as w, ()) =>\n  <View style=Styles.outer>\n    <Row>\n      <Button title=\"Maximize!\" onClick={_ => Window.maximize(w)} />\n      <Button title=\"Minimize!\" onClick={_ => Window.minimize(w)} />\n    </Row>\n    <Button title=\"Restore!\" onClick={_ => Window.restore(w)} />\n  </View>;\n\nlet render = window => <winControl window />;\n"
  },
  {
    "path": "examples/ZoomExample.re",
    "content": "open Revery;\nopen Revery.UI;\nopen Revery.UI.Components;\n\nmodule ZoomButton = {\n  let make = (~zoom, ~onClick, ()) => {\n    <Button\n      height=50\n      width=100\n      fontSize=15.\n      title={string_of_float(zoom)}\n      onClick={() => onClick(zoom)}\n    />;\n  };\n};\n\nmodule Zoom = {\n  let%component make = () => {\n    let window = UI.getActiveWindow();\n    let zoomV =\n      switch (window) {\n      | Some(v) => Window.getZoom(v)\n      | None => (-1.0)\n      };\n\n    let%hook (currentZoom, setCurrentZoom) = Hooks.state(zoomV);\n\n    let textStyle = Style.[color(Colors.white), width(100), margin(14)];\n\n    let setZoom = zoom => {\n      switch (window) {\n      | Some(v) =>\n        Window.setZoom(v, zoom);\n        setCurrentZoom(_ => zoom);\n      | None => ()\n      };\n    };\n\n    print_endline(\"Zoomv: \" ++ string_of_float(currentZoom));\n\n    <Center>\n      <Column>\n        <Text\n          style=textStyle\n          fontSize=16.\n          text={\"Zoom: \" ++ string_of_float(currentZoom)}\n        />\n        <Row>\n          <ZoomButton zoom=0.5 onClick=setZoom />\n          <ZoomButton zoom=1.0 onClick=setZoom />\n          <ZoomButton zoom=1.25 onClick=setZoom />\n          <ZoomButton zoom=1.5 onClick=setZoom />\n          <ZoomButton zoom=2.0 onClick=setZoom />\n        </Row>\n      </Column>\n    </Center>;\n  };\n};\n\nlet render = () => <Zoom />;\n"
  },
  {
    "path": "examples/dune",
    "content": "(executables\n (names Examples)\n (preprocess\n  (pps brisk-reconciler.ppx lwt_ppx))\n (package ReveryExamples)\n (public_names Examples)\n (libraries ExampleStubs skia str Revery Revery_Lwt flex timber))\n\n(install\n (section bin)\n (package Revery)\n (files Info.plist Roboto-Regular.ttf Roboto-RegularItalic.ttf\n   Roboto-Bold.ttf FontAwesome5FreeSolid.otf binary.dat outrun-logo.png\n   reason-logo.png outrun-icon.png revery-icon.png))\n"
  },
  {
    "path": "examples/gl-matrix-min.js",
    "content": "/*!\n@fileoverview gl-matrix - High performance matrix and vector operations\n@author Brandon Jones\n@author Colin MacKenzie IV\n@version 2.7.0\n\nCopyright (c) 2015-2018, Brandon Jones, Colin MacKenzie IV.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n\n*/\n!function(t,n){if(\"object\"==typeof exports&&\"object\"==typeof module)module.exports=n();else if(\"function\"==typeof define&&define.amd)define([],n);else{var r=n();for(var a in r)(\"object\"==typeof exports?exports:t)[a]=r[a]}}(\"undefined\"!=typeof self?self:this,function(){return function(t){var n={};function r(a){if(n[a])return n[a].exports;var e=n[a]={i:a,l:!1,exports:{}};return t[a].call(e.exports,e,e.exports,r),e.l=!0,e.exports}return r.m=t,r.c=n,r.d=function(t,n,a){r.o(t,n)||Object.defineProperty(t,n,{enumerable:!0,get:a})},r.r=function(t){\"undefined\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:\"Module\"}),Object.defineProperty(t,\"__esModule\",{value:!0})},r.t=function(t,n){if(1&n&&(t=r(t)),8&n)return t;if(4&n&&\"object\"==typeof t&&t&&t.__esModule)return t;var a=Object.create(null);if(r.r(a),Object.defineProperty(a,\"default\",{enumerable:!0,value:t}),2&n&&\"string\"!=typeof t)for(var e in t)r.d(a,e,function(n){return t[n]}.bind(null,e));return a},r.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(n,\"a\",n),n},r.o=function(t,n){return Object.prototype.hasOwnProperty.call(t,n)},r.p=\"\",r(r.s=10)}([function(t,n,r){\"use strict\";Object.defineProperty(n,\"__esModule\",{value:!0}),n.setMatrixArrayType=function(t){n.ARRAY_TYPE=t},n.toRadian=function(t){return t*e},n.equals=function(t,n){return Math.abs(t-n)<=a*Math.max(1,Math.abs(t),Math.abs(n))};var a=n.EPSILON=1e-6;n.ARRAY_TYPE=\"undefined\"!=typeof Float32Array?Float32Array:Array,n.RANDOM=Math.random;var e=Math.PI/180},function(t,n,r){\"use strict\";Object.defineProperty(n,\"__esModule\",{value:!0}),n.forEach=n.sqrLen=n.len=n.sqrDist=n.dist=n.div=n.mul=n.sub=void 0,n.create=e,n.clone=function(t){var n=new a.ARRAY_TYPE(4);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n},n.fromValues=function(t,n,r,e){var u=new a.ARRAY_TYPE(4);return u[0]=t,u[1]=n,u[2]=r,u[3]=e,u},n.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t},n.set=function(t,n,r,a,e){return t[0]=n,t[1]=r,t[2]=a,t[3]=e,t},n.add=function(t,n,r){return t[0]=n[0]+r[0],t[1]=n[1]+r[1],t[2]=n[2]+r[2],t[3]=n[3]+r[3],t},n.subtract=u,n.multiply=o,n.divide=i,n.ceil=function(t,n){return t[0]=Math.ceil(n[0]),t[1]=Math.ceil(n[1]),t[2]=Math.ceil(n[2]),t[3]=Math.ceil(n[3]),t},n.floor=function(t,n){return t[0]=Math.floor(n[0]),t[1]=Math.floor(n[1]),t[2]=Math.floor(n[2]),t[3]=Math.floor(n[3]),t},n.min=function(t,n,r){return t[0]=Math.min(n[0],r[0]),t[1]=Math.min(n[1],r[1]),t[2]=Math.min(n[2],r[2]),t[3]=Math.min(n[3],r[3]),t},n.max=function(t,n,r){return t[0]=Math.max(n[0],r[0]),t[1]=Math.max(n[1],r[1]),t[2]=Math.max(n[2],r[2]),t[3]=Math.max(n[3],r[3]),t},n.round=function(t,n){return t[0]=Math.round(n[0]),t[1]=Math.round(n[1]),t[2]=Math.round(n[2]),t[3]=Math.round(n[3]),t},n.scale=function(t,n,r){return t[0]=n[0]*r,t[1]=n[1]*r,t[2]=n[2]*r,t[3]=n[3]*r,t},n.scaleAndAdd=function(t,n,r,a){return t[0]=n[0]+r[0]*a,t[1]=n[1]+r[1]*a,t[2]=n[2]+r[2]*a,t[3]=n[3]+r[3]*a,t},n.distance=s,n.squaredDistance=c,n.length=f,n.squaredLength=M,n.negate=function(t,n){return t[0]=-n[0],t[1]=-n[1],t[2]=-n[2],t[3]=-n[3],t},n.inverse=function(t,n){return t[0]=1/n[0],t[1]=1/n[1],t[2]=1/n[2],t[3]=1/n[3],t},n.normalize=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=r*r+a*a+e*e+u*u;o>0&&(o=1/Math.sqrt(o),t[0]=r*o,t[1]=a*o,t[2]=e*o,t[3]=u*o);return t},n.dot=function(t,n){return t[0]*n[0]+t[1]*n[1]+t[2]*n[2]+t[3]*n[3]},n.lerp=function(t,n,r,a){var e=n[0],u=n[1],o=n[2],i=n[3];return t[0]=e+a*(r[0]-e),t[1]=u+a*(r[1]-u),t[2]=o+a*(r[2]-o),t[3]=i+a*(r[3]-i),t},n.random=function(t,n){var r,e,u,o,i,s;n=n||1;do{r=2*a.RANDOM()-1,e=2*a.RANDOM()-1,i=r*r+e*e}while(i>=1);do{u=2*a.RANDOM()-1,o=2*a.RANDOM()-1,s=u*u+o*o}while(s>=1);var c=Math.sqrt((1-i)/s);return t[0]=n*r,t[1]=n*e,t[2]=n*u*c,t[3]=n*o*c,t},n.transformMat4=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3];return t[0]=r[0]*a+r[4]*e+r[8]*u+r[12]*o,t[1]=r[1]*a+r[5]*e+r[9]*u+r[13]*o,t[2]=r[2]*a+r[6]*e+r[10]*u+r[14]*o,t[3]=r[3]*a+r[7]*e+r[11]*u+r[15]*o,t},n.transformQuat=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=r[0],i=r[1],s=r[2],c=r[3],f=c*a+i*u-s*e,M=c*e+s*a-o*u,h=c*u+o*e-i*a,l=-o*a-i*e-s*u;return t[0]=f*c+l*-o+M*-s-h*-i,t[1]=M*c+l*-i+h*-o-f*-s,t[2]=h*c+l*-s+f*-i-M*-o,t[3]=n[3],t},n.str=function(t){return\"vec4(\"+t[0]+\", \"+t[1]+\", \"+t[2]+\", \"+t[3]+\")\"},n.exactEquals=function(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]&&t[3]===n[3]},n.equals=function(t,n){var r=t[0],e=t[1],u=t[2],o=t[3],i=n[0],s=n[1],c=n[2],f=n[3];return Math.abs(r-i)<=a.EPSILON*Math.max(1,Math.abs(r),Math.abs(i))&&Math.abs(e-s)<=a.EPSILON*Math.max(1,Math.abs(e),Math.abs(s))&&Math.abs(u-c)<=a.EPSILON*Math.max(1,Math.abs(u),Math.abs(c))&&Math.abs(o-f)<=a.EPSILON*Math.max(1,Math.abs(o),Math.abs(f))};var a=function(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}(r(0));function e(){var t=new a.ARRAY_TYPE(4);return a.ARRAY_TYPE!=Float32Array&&(t[0]=0,t[1]=0,t[2]=0,t[3]=0),t}function u(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t[2]=n[2]-r[2],t[3]=n[3]-r[3],t}function o(t,n,r){return t[0]=n[0]*r[0],t[1]=n[1]*r[1],t[2]=n[2]*r[2],t[3]=n[3]*r[3],t}function i(t,n,r){return t[0]=n[0]/r[0],t[1]=n[1]/r[1],t[2]=n[2]/r[2],t[3]=n[3]/r[3],t}function s(t,n){var r=n[0]-t[0],a=n[1]-t[1],e=n[2]-t[2],u=n[3]-t[3];return Math.sqrt(r*r+a*a+e*e+u*u)}function c(t,n){var r=n[0]-t[0],a=n[1]-t[1],e=n[2]-t[2],u=n[3]-t[3];return r*r+a*a+e*e+u*u}function f(t){var n=t[0],r=t[1],a=t[2],e=t[3];return Math.sqrt(n*n+r*r+a*a+e*e)}function M(t){var n=t[0],r=t[1],a=t[2],e=t[3];return n*n+r*r+a*a+e*e}n.sub=u,n.mul=o,n.div=i,n.dist=s,n.sqrDist=c,n.len=f,n.sqrLen=M,n.forEach=function(){var t=e();return function(n,r,a,e,u,o){var i=void 0,s=void 0;for(r||(r=4),a||(a=0),s=e?Math.min(e*r+a,n.length):n.length,i=a;i<s;i+=r)t[0]=n[i],t[1]=n[i+1],t[2]=n[i+2],t[3]=n[i+3],u(t,t,o),n[i]=t[0],n[i+1]=t[1],n[i+2]=t[2],n[i+3]=t[3];return n}}()},function(t,n,r){\"use strict\";Object.defineProperty(n,\"__esModule\",{value:!0}),n.forEach=n.sqrLen=n.len=n.sqrDist=n.dist=n.div=n.mul=n.sub=void 0,n.create=e,n.clone=function(t){var n=new a.ARRAY_TYPE(3);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n},n.length=u,n.fromValues=o,n.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t},n.set=function(t,n,r,a){return t[0]=n,t[1]=r,t[2]=a,t},n.add=function(t,n,r){return t[0]=n[0]+r[0],t[1]=n[1]+r[1],t[2]=n[2]+r[2],t},n.subtract=i,n.multiply=s,n.divide=c,n.ceil=function(t,n){return t[0]=Math.ceil(n[0]),t[1]=Math.ceil(n[1]),t[2]=Math.ceil(n[2]),t},n.floor=function(t,n){return t[0]=Math.floor(n[0]),t[1]=Math.floor(n[1]),t[2]=Math.floor(n[2]),t},n.min=function(t,n,r){return t[0]=Math.min(n[0],r[0]),t[1]=Math.min(n[1],r[1]),t[2]=Math.min(n[2],r[2]),t},n.max=function(t,n,r){return t[0]=Math.max(n[0],r[0]),t[1]=Math.max(n[1],r[1]),t[2]=Math.max(n[2],r[2]),t},n.round=function(t,n){return t[0]=Math.round(n[0]),t[1]=Math.round(n[1]),t[2]=Math.round(n[2]),t},n.scale=function(t,n,r){return t[0]=n[0]*r,t[1]=n[1]*r,t[2]=n[2]*r,t},n.scaleAndAdd=function(t,n,r,a){return t[0]=n[0]+r[0]*a,t[1]=n[1]+r[1]*a,t[2]=n[2]+r[2]*a,t},n.distance=f,n.squaredDistance=M,n.squaredLength=h,n.negate=function(t,n){return t[0]=-n[0],t[1]=-n[1],t[2]=-n[2],t},n.inverse=function(t,n){return t[0]=1/n[0],t[1]=1/n[1],t[2]=1/n[2],t},n.normalize=l,n.dot=v,n.cross=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=r[0],i=r[1],s=r[2];return t[0]=e*s-u*i,t[1]=u*o-a*s,t[2]=a*i-e*o,t},n.lerp=function(t,n,r,a){var e=n[0],u=n[1],o=n[2];return t[0]=e+a*(r[0]-e),t[1]=u+a*(r[1]-u),t[2]=o+a*(r[2]-o),t},n.hermite=function(t,n,r,a,e,u){var o=u*u,i=o*(2*u-3)+1,s=o*(u-2)+u,c=o*(u-1),f=o*(3-2*u);return t[0]=n[0]*i+r[0]*s+a[0]*c+e[0]*f,t[1]=n[1]*i+r[1]*s+a[1]*c+e[1]*f,t[2]=n[2]*i+r[2]*s+a[2]*c+e[2]*f,t},n.bezier=function(t,n,r,a,e,u){var o=1-u,i=o*o,s=u*u,c=i*o,f=3*u*i,M=3*s*o,h=s*u;return t[0]=n[0]*c+r[0]*f+a[0]*M+e[0]*h,t[1]=n[1]*c+r[1]*f+a[1]*M+e[1]*h,t[2]=n[2]*c+r[2]*f+a[2]*M+e[2]*h,t},n.random=function(t,n){n=n||1;var r=2*a.RANDOM()*Math.PI,e=2*a.RANDOM()-1,u=Math.sqrt(1-e*e)*n;return t[0]=Math.cos(r)*u,t[1]=Math.sin(r)*u,t[2]=e*n,t},n.transformMat4=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=r[3]*a+r[7]*e+r[11]*u+r[15];return o=o||1,t[0]=(r[0]*a+r[4]*e+r[8]*u+r[12])/o,t[1]=(r[1]*a+r[5]*e+r[9]*u+r[13])/o,t[2]=(r[2]*a+r[6]*e+r[10]*u+r[14])/o,t},n.transformMat3=function(t,n,r){var a=n[0],e=n[1],u=n[2];return t[0]=a*r[0]+e*r[3]+u*r[6],t[1]=a*r[1]+e*r[4]+u*r[7],t[2]=a*r[2]+e*r[5]+u*r[8],t},n.transformQuat=function(t,n,r){var a=r[0],e=r[1],u=r[2],o=r[3],i=n[0],s=n[1],c=n[2],f=e*c-u*s,M=u*i-a*c,h=a*s-e*i,l=e*h-u*M,v=u*f-a*h,d=a*M-e*f,b=2*o;return f*=b,M*=b,h*=b,l*=2,v*=2,d*=2,t[0]=i+f+l,t[1]=s+M+v,t[2]=c+h+d,t},n.rotateX=function(t,n,r,a){var e=[],u=[];return e[0]=n[0]-r[0],e[1]=n[1]-r[1],e[2]=n[2]-r[2],u[0]=e[0],u[1]=e[1]*Math.cos(a)-e[2]*Math.sin(a),u[2]=e[1]*Math.sin(a)+e[2]*Math.cos(a),t[0]=u[0]+r[0],t[1]=u[1]+r[1],t[2]=u[2]+r[2],t},n.rotateY=function(t,n,r,a){var e=[],u=[];return e[0]=n[0]-r[0],e[1]=n[1]-r[1],e[2]=n[2]-r[2],u[0]=e[2]*Math.sin(a)+e[0]*Math.cos(a),u[1]=e[1],u[2]=e[2]*Math.cos(a)-e[0]*Math.sin(a),t[0]=u[0]+r[0],t[1]=u[1]+r[1],t[2]=u[2]+r[2],t},n.rotateZ=function(t,n,r,a){var e=[],u=[];return e[0]=n[0]-r[0],e[1]=n[1]-r[1],e[2]=n[2]-r[2],u[0]=e[0]*Math.cos(a)-e[1]*Math.sin(a),u[1]=e[0]*Math.sin(a)+e[1]*Math.cos(a),u[2]=e[2],t[0]=u[0]+r[0],t[1]=u[1]+r[1],t[2]=u[2]+r[2],t},n.angle=function(t,n){var r=o(t[0],t[1],t[2]),a=o(n[0],n[1],n[2]);l(r,r),l(a,a);var e=v(r,a);return e>1?0:e<-1?Math.PI:Math.acos(e)},n.str=function(t){return\"vec3(\"+t[0]+\", \"+t[1]+\", \"+t[2]+\")\"},n.exactEquals=function(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]},n.equals=function(t,n){var r=t[0],e=t[1],u=t[2],o=n[0],i=n[1],s=n[2];return Math.abs(r-o)<=a.EPSILON*Math.max(1,Math.abs(r),Math.abs(o))&&Math.abs(e-i)<=a.EPSILON*Math.max(1,Math.abs(e),Math.abs(i))&&Math.abs(u-s)<=a.EPSILON*Math.max(1,Math.abs(u),Math.abs(s))};var a=function(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}(r(0));function e(){var t=new a.ARRAY_TYPE(3);return a.ARRAY_TYPE!=Float32Array&&(t[0]=0,t[1]=0,t[2]=0),t}function u(t){var n=t[0],r=t[1],a=t[2];return Math.sqrt(n*n+r*r+a*a)}function o(t,n,r){var e=new a.ARRAY_TYPE(3);return e[0]=t,e[1]=n,e[2]=r,e}function i(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t[2]=n[2]-r[2],t}function s(t,n,r){return t[0]=n[0]*r[0],t[1]=n[1]*r[1],t[2]=n[2]*r[2],t}function c(t,n,r){return t[0]=n[0]/r[0],t[1]=n[1]/r[1],t[2]=n[2]/r[2],t}function f(t,n){var r=n[0]-t[0],a=n[1]-t[1],e=n[2]-t[2];return Math.sqrt(r*r+a*a+e*e)}function M(t,n){var r=n[0]-t[0],a=n[1]-t[1],e=n[2]-t[2];return r*r+a*a+e*e}function h(t){var n=t[0],r=t[1],a=t[2];return n*n+r*r+a*a}function l(t,n){var r=n[0],a=n[1],e=n[2],u=r*r+a*a+e*e;return u>0&&(u=1/Math.sqrt(u),t[0]=n[0]*u,t[1]=n[1]*u,t[2]=n[2]*u),t}function v(t,n){return t[0]*n[0]+t[1]*n[1]+t[2]*n[2]}n.sub=i,n.mul=s,n.div=c,n.dist=f,n.sqrDist=M,n.len=u,n.sqrLen=h,n.forEach=function(){var t=e();return function(n,r,a,e,u,o){var i=void 0,s=void 0;for(r||(r=3),a||(a=0),s=e?Math.min(e*r+a,n.length):n.length,i=a;i<s;i+=r)t[0]=n[i],t[1]=n[i+1],t[2]=n[i+2],u(t,t,o),n[i]=t[0],n[i+1]=t[1],n[i+2]=t[2];return n}}()},function(t,n,r){\"use strict\";Object.defineProperty(n,\"__esModule\",{value:!0}),n.setAxes=n.sqlerp=n.rotationTo=n.equals=n.exactEquals=n.normalize=n.sqrLen=n.squaredLength=n.len=n.length=n.lerp=n.dot=n.scale=n.mul=n.add=n.set=n.copy=n.fromValues=n.clone=void 0,n.create=s,n.identity=function(t){return t[0]=0,t[1]=0,t[2]=0,t[3]=1,t},n.setAxisAngle=c,n.getAxisAngle=function(t,n){var r=2*Math.acos(n[3]),e=Math.sin(r/2);e>a.EPSILON?(t[0]=n[0]/e,t[1]=n[1]/e,t[2]=n[2]/e):(t[0]=1,t[1]=0,t[2]=0);return r},n.multiply=f,n.rotateX=function(t,n,r){r*=.5;var a=n[0],e=n[1],u=n[2],o=n[3],i=Math.sin(r),s=Math.cos(r);return t[0]=a*s+o*i,t[1]=e*s+u*i,t[2]=u*s-e*i,t[3]=o*s-a*i,t},n.rotateY=function(t,n,r){r*=.5;var a=n[0],e=n[1],u=n[2],o=n[3],i=Math.sin(r),s=Math.cos(r);return t[0]=a*s-u*i,t[1]=e*s+o*i,t[2]=u*s+a*i,t[3]=o*s-e*i,t},n.rotateZ=function(t,n,r){r*=.5;var a=n[0],e=n[1],u=n[2],o=n[3],i=Math.sin(r),s=Math.cos(r);return t[0]=a*s+e*i,t[1]=e*s-a*i,t[2]=u*s+o*i,t[3]=o*s-u*i,t},n.calculateW=function(t,n){var r=n[0],a=n[1],e=n[2];return t[0]=r,t[1]=a,t[2]=e,t[3]=Math.sqrt(Math.abs(1-r*r-a*a-e*e)),t},n.slerp=M,n.random=function(t){var n=a.RANDOM(),r=a.RANDOM(),e=a.RANDOM(),u=Math.sqrt(1-n),o=Math.sqrt(n);return t[0]=u*Math.sin(2*Math.PI*r),t[1]=u*Math.cos(2*Math.PI*r),t[2]=o*Math.sin(2*Math.PI*e),t[3]=o*Math.cos(2*Math.PI*e),t},n.invert=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=r*r+a*a+e*e+u*u,i=o?1/o:0;return t[0]=-r*i,t[1]=-a*i,t[2]=-e*i,t[3]=u*i,t},n.conjugate=function(t,n){return t[0]=-n[0],t[1]=-n[1],t[2]=-n[2],t[3]=n[3],t},n.fromMat3=h,n.fromEuler=function(t,n,r,a){var e=.5*Math.PI/180;n*=e,r*=e,a*=e;var u=Math.sin(n),o=Math.cos(n),i=Math.sin(r),s=Math.cos(r),c=Math.sin(a),f=Math.cos(a);return t[0]=u*s*f-o*i*c,t[1]=o*i*f+u*s*c,t[2]=o*s*c-u*i*f,t[3]=o*s*f+u*i*c,t},n.str=function(t){return\"quat(\"+t[0]+\", \"+t[1]+\", \"+t[2]+\", \"+t[3]+\")\"};var a=i(r(0)),e=i(r(5)),u=i(r(2)),o=i(r(1));function i(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}function s(){var t=new a.ARRAY_TYPE(4);return a.ARRAY_TYPE!=Float32Array&&(t[0]=0,t[1]=0,t[2]=0),t[3]=1,t}function c(t,n,r){r*=.5;var a=Math.sin(r);return t[0]=a*n[0],t[1]=a*n[1],t[2]=a*n[2],t[3]=Math.cos(r),t}function f(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=r[0],s=r[1],c=r[2],f=r[3];return t[0]=a*f+o*i+e*c-u*s,t[1]=e*f+o*s+u*i-a*c,t[2]=u*f+o*c+a*s-e*i,t[3]=o*f-a*i-e*s-u*c,t}function M(t,n,r,e){var u=n[0],o=n[1],i=n[2],s=n[3],c=r[0],f=r[1],M=r[2],h=r[3],l=void 0,v=void 0,d=void 0,b=void 0,m=void 0;return(v=u*c+o*f+i*M+s*h)<0&&(v=-v,c=-c,f=-f,M=-M,h=-h),1-v>a.EPSILON?(l=Math.acos(v),d=Math.sin(l),b=Math.sin((1-e)*l)/d,m=Math.sin(e*l)/d):(b=1-e,m=e),t[0]=b*u+m*c,t[1]=b*o+m*f,t[2]=b*i+m*M,t[3]=b*s+m*h,t}function h(t,n){var r=n[0]+n[4]+n[8],a=void 0;if(r>0)a=Math.sqrt(r+1),t[3]=.5*a,a=.5/a,t[0]=(n[5]-n[7])*a,t[1]=(n[6]-n[2])*a,t[2]=(n[1]-n[3])*a;else{var e=0;n[4]>n[0]&&(e=1),n[8]>n[3*e+e]&&(e=2);var u=(e+1)%3,o=(e+2)%3;a=Math.sqrt(n[3*e+e]-n[3*u+u]-n[3*o+o]+1),t[e]=.5*a,a=.5/a,t[3]=(n[3*u+o]-n[3*o+u])*a,t[u]=(n[3*u+e]+n[3*e+u])*a,t[o]=(n[3*o+e]+n[3*e+o])*a}return t}n.clone=o.clone,n.fromValues=o.fromValues,n.copy=o.copy,n.set=o.set,n.add=o.add,n.mul=f,n.scale=o.scale,n.dot=o.dot,n.lerp=o.lerp;var l=n.length=o.length,v=(n.len=l,n.squaredLength=o.squaredLength),d=(n.sqrLen=v,n.normalize=o.normalize);n.exactEquals=o.exactEquals,n.equals=o.equals,n.rotationTo=function(){var t=u.create(),n=u.fromValues(1,0,0),r=u.fromValues(0,1,0);return function(a,e,o){var i=u.dot(e,o);return i<-.999999?(u.cross(t,n,e),u.len(t)<1e-6&&u.cross(t,r,e),u.normalize(t,t),c(a,t,Math.PI),a):i>.999999?(a[0]=0,a[1]=0,a[2]=0,a[3]=1,a):(u.cross(t,e,o),a[0]=t[0],a[1]=t[1],a[2]=t[2],a[3]=1+i,d(a,a))}}(),n.sqlerp=function(){var t=s(),n=s();return function(r,a,e,u,o,i){return M(t,a,o,i),M(n,e,u,i),M(r,t,n,2*i*(1-i)),r}}(),n.setAxes=function(){var t=e.create();return function(n,r,a,e){return t[0]=a[0],t[3]=a[1],t[6]=a[2],t[1]=e[0],t[4]=e[1],t[7]=e[2],t[2]=-r[0],t[5]=-r[1],t[8]=-r[2],d(n,h(n,t))}}()},function(t,n,r){\"use strict\";Object.defineProperty(n,\"__esModule\",{value:!0}),n.sub=n.mul=void 0,n.create=function(){var t=new a.ARRAY_TYPE(16);a.ARRAY_TYPE!=Float32Array&&(t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[11]=0,t[12]=0,t[13]=0,t[14]=0);return t[0]=1,t[5]=1,t[10]=1,t[15]=1,t},n.clone=function(t){var n=new a.ARRAY_TYPE(16);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n[4]=t[4],n[5]=t[5],n[6]=t[6],n[7]=t[7],n[8]=t[8],n[9]=t[9],n[10]=t[10],n[11]=t[11],n[12]=t[12],n[13]=t[13],n[14]=t[14],n[15]=t[15],n},n.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[4]=n[4],t[5]=n[5],t[6]=n[6],t[7]=n[7],t[8]=n[8],t[9]=n[9],t[10]=n[10],t[11]=n[11],t[12]=n[12],t[13]=n[13],t[14]=n[14],t[15]=n[15],t},n.fromValues=function(t,n,r,e,u,o,i,s,c,f,M,h,l,v,d,b){var m=new a.ARRAY_TYPE(16);return m[0]=t,m[1]=n,m[2]=r,m[3]=e,m[4]=u,m[5]=o,m[6]=i,m[7]=s,m[8]=c,m[9]=f,m[10]=M,m[11]=h,m[12]=l,m[13]=v,m[14]=d,m[15]=b,m},n.set=function(t,n,r,a,e,u,o,i,s,c,f,M,h,l,v,d,b){return t[0]=n,t[1]=r,t[2]=a,t[3]=e,t[4]=u,t[5]=o,t[6]=i,t[7]=s,t[8]=c,t[9]=f,t[10]=M,t[11]=h,t[12]=l,t[13]=v,t[14]=d,t[15]=b,t},n.identity=e,n.transpose=function(t,n){if(t===n){var r=n[1],a=n[2],e=n[3],u=n[6],o=n[7],i=n[11];t[1]=n[4],t[2]=n[8],t[3]=n[12],t[4]=r,t[6]=n[9],t[7]=n[13],t[8]=a,t[9]=u,t[11]=n[14],t[12]=e,t[13]=o,t[14]=i}else t[0]=n[0],t[1]=n[4],t[2]=n[8],t[3]=n[12],t[4]=n[1],t[5]=n[5],t[6]=n[9],t[7]=n[13],t[8]=n[2],t[9]=n[6],t[10]=n[10],t[11]=n[14],t[12]=n[3],t[13]=n[7],t[14]=n[11],t[15]=n[15];return t},n.invert=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],s=n[6],c=n[7],f=n[8],M=n[9],h=n[10],l=n[11],v=n[12],d=n[13],b=n[14],m=n[15],p=r*i-a*o,P=r*s-e*o,A=r*c-u*o,E=a*s-e*i,O=a*c-u*i,R=e*c-u*s,y=f*d-M*v,q=f*b-h*v,x=f*m-l*v,_=M*b-h*d,Y=M*m-l*d,L=h*m-l*b,S=p*L-P*Y+A*_+E*x-O*q+R*y;if(!S)return null;return S=1/S,t[0]=(i*L-s*Y+c*_)*S,t[1]=(e*Y-a*L-u*_)*S,t[2]=(d*R-b*O+m*E)*S,t[3]=(h*O-M*R-l*E)*S,t[4]=(s*x-o*L-c*q)*S,t[5]=(r*L-e*x+u*q)*S,t[6]=(b*A-v*R-m*P)*S,t[7]=(f*R-h*A+l*P)*S,t[8]=(o*Y-i*x+c*y)*S,t[9]=(a*x-r*Y-u*y)*S,t[10]=(v*O-d*A+m*p)*S,t[11]=(M*A-f*O-l*p)*S,t[12]=(i*q-o*_-s*y)*S,t[13]=(r*_-a*q+e*y)*S,t[14]=(d*P-v*E-b*p)*S,t[15]=(f*E-M*P+h*p)*S,t},n.adjoint=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],s=n[6],c=n[7],f=n[8],M=n[9],h=n[10],l=n[11],v=n[12],d=n[13],b=n[14],m=n[15];return t[0]=i*(h*m-l*b)-M*(s*m-c*b)+d*(s*l-c*h),t[1]=-(a*(h*m-l*b)-M*(e*m-u*b)+d*(e*l-u*h)),t[2]=a*(s*m-c*b)-i*(e*m-u*b)+d*(e*c-u*s),t[3]=-(a*(s*l-c*h)-i*(e*l-u*h)+M*(e*c-u*s)),t[4]=-(o*(h*m-l*b)-f*(s*m-c*b)+v*(s*l-c*h)),t[5]=r*(h*m-l*b)-f*(e*m-u*b)+v*(e*l-u*h),t[6]=-(r*(s*m-c*b)-o*(e*m-u*b)+v*(e*c-u*s)),t[7]=r*(s*l-c*h)-o*(e*l-u*h)+f*(e*c-u*s),t[8]=o*(M*m-l*d)-f*(i*m-c*d)+v*(i*l-c*M),t[9]=-(r*(M*m-l*d)-f*(a*m-u*d)+v*(a*l-u*M)),t[10]=r*(i*m-c*d)-o*(a*m-u*d)+v*(a*c-u*i),t[11]=-(r*(i*l-c*M)-o*(a*l-u*M)+f*(a*c-u*i)),t[12]=-(o*(M*b-h*d)-f*(i*b-s*d)+v*(i*h-s*M)),t[13]=r*(M*b-h*d)-f*(a*b-e*d)+v*(a*h-e*M),t[14]=-(r*(i*b-s*d)-o*(a*b-e*d)+v*(a*s-e*i)),t[15]=r*(i*h-s*M)-o*(a*h-e*M)+f*(a*s-e*i),t},n.determinant=function(t){var n=t[0],r=t[1],a=t[2],e=t[3],u=t[4],o=t[5],i=t[6],s=t[7],c=t[8],f=t[9],M=t[10],h=t[11],l=t[12],v=t[13],d=t[14],b=t[15];return(n*o-r*u)*(M*b-h*d)-(n*i-a*u)*(f*b-h*v)+(n*s-e*u)*(f*d-M*v)+(r*i-a*o)*(c*b-h*l)-(r*s-e*o)*(c*d-M*l)+(a*s-e*i)*(c*v-f*l)},n.multiply=u,n.translate=function(t,n,r){var a=r[0],e=r[1],u=r[2],o=void 0,i=void 0,s=void 0,c=void 0,f=void 0,M=void 0,h=void 0,l=void 0,v=void 0,d=void 0,b=void 0,m=void 0;n===t?(t[12]=n[0]*a+n[4]*e+n[8]*u+n[12],t[13]=n[1]*a+n[5]*e+n[9]*u+n[13],t[14]=n[2]*a+n[6]*e+n[10]*u+n[14],t[15]=n[3]*a+n[7]*e+n[11]*u+n[15]):(o=n[0],i=n[1],s=n[2],c=n[3],f=n[4],M=n[5],h=n[6],l=n[7],v=n[8],d=n[9],b=n[10],m=n[11],t[0]=o,t[1]=i,t[2]=s,t[3]=c,t[4]=f,t[5]=M,t[6]=h,t[7]=l,t[8]=v,t[9]=d,t[10]=b,t[11]=m,t[12]=o*a+f*e+v*u+n[12],t[13]=i*a+M*e+d*u+n[13],t[14]=s*a+h*e+b*u+n[14],t[15]=c*a+l*e+m*u+n[15]);return t},n.scale=function(t,n,r){var a=r[0],e=r[1],u=r[2];return t[0]=n[0]*a,t[1]=n[1]*a,t[2]=n[2]*a,t[3]=n[3]*a,t[4]=n[4]*e,t[5]=n[5]*e,t[6]=n[6]*e,t[7]=n[7]*e,t[8]=n[8]*u,t[9]=n[9]*u,t[10]=n[10]*u,t[11]=n[11]*u,t[12]=n[12],t[13]=n[13],t[14]=n[14],t[15]=n[15],t},n.rotate=function(t,n,r,e){var u=e[0],o=e[1],i=e[2],s=Math.sqrt(u*u+o*o+i*i),c=void 0,f=void 0,M=void 0,h=void 0,l=void 0,v=void 0,d=void 0,b=void 0,m=void 0,p=void 0,P=void 0,A=void 0,E=void 0,O=void 0,R=void 0,y=void 0,q=void 0,x=void 0,_=void 0,Y=void 0,L=void 0,S=void 0,w=void 0,I=void 0;if(s<a.EPSILON)return null;u*=s=1/s,o*=s,i*=s,c=Math.sin(r),f=Math.cos(r),M=1-f,h=n[0],l=n[1],v=n[2],d=n[3],b=n[4],m=n[5],p=n[6],P=n[7],A=n[8],E=n[9],O=n[10],R=n[11],y=u*u*M+f,q=o*u*M+i*c,x=i*u*M-o*c,_=u*o*M-i*c,Y=o*o*M+f,L=i*o*M+u*c,S=u*i*M+o*c,w=o*i*M-u*c,I=i*i*M+f,t[0]=h*y+b*q+A*x,t[1]=l*y+m*q+E*x,t[2]=v*y+p*q+O*x,t[3]=d*y+P*q+R*x,t[4]=h*_+b*Y+A*L,t[5]=l*_+m*Y+E*L,t[6]=v*_+p*Y+O*L,t[7]=d*_+P*Y+R*L,t[8]=h*S+b*w+A*I,t[9]=l*S+m*w+E*I,t[10]=v*S+p*w+O*I,t[11]=d*S+P*w+R*I,n!==t&&(t[12]=n[12],t[13]=n[13],t[14]=n[14],t[15]=n[15]);return t},n.rotateX=function(t,n,r){var a=Math.sin(r),e=Math.cos(r),u=n[4],o=n[5],i=n[6],s=n[7],c=n[8],f=n[9],M=n[10],h=n[11];n!==t&&(t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[12]=n[12],t[13]=n[13],t[14]=n[14],t[15]=n[15]);return t[4]=u*e+c*a,t[5]=o*e+f*a,t[6]=i*e+M*a,t[7]=s*e+h*a,t[8]=c*e-u*a,t[9]=f*e-o*a,t[10]=M*e-i*a,t[11]=h*e-s*a,t},n.rotateY=function(t,n,r){var a=Math.sin(r),e=Math.cos(r),u=n[0],o=n[1],i=n[2],s=n[3],c=n[8],f=n[9],M=n[10],h=n[11];n!==t&&(t[4]=n[4],t[5]=n[5],t[6]=n[6],t[7]=n[7],t[12]=n[12],t[13]=n[13],t[14]=n[14],t[15]=n[15]);return t[0]=u*e-c*a,t[1]=o*e-f*a,t[2]=i*e-M*a,t[3]=s*e-h*a,t[8]=u*a+c*e,t[9]=o*a+f*e,t[10]=i*a+M*e,t[11]=s*a+h*e,t},n.rotateZ=function(t,n,r){var a=Math.sin(r),e=Math.cos(r),u=n[0],o=n[1],i=n[2],s=n[3],c=n[4],f=n[5],M=n[6],h=n[7];n!==t&&(t[8]=n[8],t[9]=n[9],t[10]=n[10],t[11]=n[11],t[12]=n[12],t[13]=n[13],t[14]=n[14],t[15]=n[15]);return t[0]=u*e+c*a,t[1]=o*e+f*a,t[2]=i*e+M*a,t[3]=s*e+h*a,t[4]=c*e-u*a,t[5]=f*e-o*a,t[6]=M*e-i*a,t[7]=h*e-s*a,t},n.fromTranslation=function(t,n){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=1,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=1,t[11]=0,t[12]=n[0],t[13]=n[1],t[14]=n[2],t[15]=1,t},n.fromScaling=function(t,n){return t[0]=n[0],t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=n[1],t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=n[2],t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},n.fromRotation=function(t,n,r){var e=r[0],u=r[1],o=r[2],i=Math.sqrt(e*e+u*u+o*o),s=void 0,c=void 0,f=void 0;if(i<a.EPSILON)return null;return e*=i=1/i,u*=i,o*=i,s=Math.sin(n),c=Math.cos(n),f=1-c,t[0]=e*e*f+c,t[1]=u*e*f+o*s,t[2]=o*e*f-u*s,t[3]=0,t[4]=e*u*f-o*s,t[5]=u*u*f+c,t[6]=o*u*f+e*s,t[7]=0,t[8]=e*o*f+u*s,t[9]=u*o*f-e*s,t[10]=o*o*f+c,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},n.fromXRotation=function(t,n){var r=Math.sin(n),a=Math.cos(n);return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=a,t[6]=r,t[7]=0,t[8]=0,t[9]=-r,t[10]=a,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},n.fromYRotation=function(t,n){var r=Math.sin(n),a=Math.cos(n);return t[0]=a,t[1]=0,t[2]=-r,t[3]=0,t[4]=0,t[5]=1,t[6]=0,t[7]=0,t[8]=r,t[9]=0,t[10]=a,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},n.fromZRotation=function(t,n){var r=Math.sin(n),a=Math.cos(n);return t[0]=a,t[1]=r,t[2]=0,t[3]=0,t[4]=-r,t[5]=a,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=1,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},n.fromRotationTranslation=o,n.fromQuat2=function(t,n){var r=new a.ARRAY_TYPE(3),e=-n[0],u=-n[1],i=-n[2],s=n[3],c=n[4],f=n[5],M=n[6],h=n[7],l=e*e+u*u+i*i+s*s;l>0?(r[0]=2*(c*s+h*e+f*i-M*u)/l,r[1]=2*(f*s+h*u+M*e-c*i)/l,r[2]=2*(M*s+h*i+c*u-f*e)/l):(r[0]=2*(c*s+h*e+f*i-M*u),r[1]=2*(f*s+h*u+M*e-c*i),r[2]=2*(M*s+h*i+c*u-f*e));return o(t,n,r),t},n.getTranslation=function(t,n){return t[0]=n[12],t[1]=n[13],t[2]=n[14],t},n.getScaling=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[4],o=n[5],i=n[6],s=n[8],c=n[9],f=n[10];return t[0]=Math.sqrt(r*r+a*a+e*e),t[1]=Math.sqrt(u*u+o*o+i*i),t[2]=Math.sqrt(s*s+c*c+f*f),t},n.getRotation=function(t,n){var r=n[0]+n[5]+n[10],a=0;r>0?(a=2*Math.sqrt(r+1),t[3]=.25*a,t[0]=(n[6]-n[9])/a,t[1]=(n[8]-n[2])/a,t[2]=(n[1]-n[4])/a):n[0]>n[5]&&n[0]>n[10]?(a=2*Math.sqrt(1+n[0]-n[5]-n[10]),t[3]=(n[6]-n[9])/a,t[0]=.25*a,t[1]=(n[1]+n[4])/a,t[2]=(n[8]+n[2])/a):n[5]>n[10]?(a=2*Math.sqrt(1+n[5]-n[0]-n[10]),t[3]=(n[8]-n[2])/a,t[0]=(n[1]+n[4])/a,t[1]=.25*a,t[2]=(n[6]+n[9])/a):(a=2*Math.sqrt(1+n[10]-n[0]-n[5]),t[3]=(n[1]-n[4])/a,t[0]=(n[8]+n[2])/a,t[1]=(n[6]+n[9])/a,t[2]=.25*a);return t},n.fromRotationTranslationScale=function(t,n,r,a){var e=n[0],u=n[1],o=n[2],i=n[3],s=e+e,c=u+u,f=o+o,M=e*s,h=e*c,l=e*f,v=u*c,d=u*f,b=o*f,m=i*s,p=i*c,P=i*f,A=a[0],E=a[1],O=a[2];return t[0]=(1-(v+b))*A,t[1]=(h+P)*A,t[2]=(l-p)*A,t[3]=0,t[4]=(h-P)*E,t[5]=(1-(M+b))*E,t[6]=(d+m)*E,t[7]=0,t[8]=(l+p)*O,t[9]=(d-m)*O,t[10]=(1-(M+v))*O,t[11]=0,t[12]=r[0],t[13]=r[1],t[14]=r[2],t[15]=1,t},n.fromRotationTranslationScaleOrigin=function(t,n,r,a,e){var u=n[0],o=n[1],i=n[2],s=n[3],c=u+u,f=o+o,M=i+i,h=u*c,l=u*f,v=u*M,d=o*f,b=o*M,m=i*M,p=s*c,P=s*f,A=s*M,E=a[0],O=a[1],R=a[2],y=e[0],q=e[1],x=e[2],_=(1-(d+m))*E,Y=(l+A)*E,L=(v-P)*E,S=(l-A)*O,w=(1-(h+m))*O,I=(b+p)*O,N=(v+P)*R,g=(b-p)*R,T=(1-(h+d))*R;return t[0]=_,t[1]=Y,t[2]=L,t[3]=0,t[4]=S,t[5]=w,t[6]=I,t[7]=0,t[8]=N,t[9]=g,t[10]=T,t[11]=0,t[12]=r[0]+y-(_*y+S*q+N*x),t[13]=r[1]+q-(Y*y+w*q+g*x),t[14]=r[2]+x-(L*y+I*q+T*x),t[15]=1,t},n.fromQuat=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=r+r,i=a+a,s=e+e,c=r*o,f=a*o,M=a*i,h=e*o,l=e*i,v=e*s,d=u*o,b=u*i,m=u*s;return t[0]=1-M-v,t[1]=f+m,t[2]=h-b,t[3]=0,t[4]=f-m,t[5]=1-c-v,t[6]=l+d,t[7]=0,t[8]=h+b,t[9]=l-d,t[10]=1-c-M,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},n.frustum=function(t,n,r,a,e,u,o){var i=1/(r-n),s=1/(e-a),c=1/(u-o);return t[0]=2*u*i,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=2*u*s,t[6]=0,t[7]=0,t[8]=(r+n)*i,t[9]=(e+a)*s,t[10]=(o+u)*c,t[11]=-1,t[12]=0,t[13]=0,t[14]=o*u*2*c,t[15]=0,t},n.perspective=function(t,n,r,a,e){var u=1/Math.tan(n/2),o=void 0;t[0]=u/r,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=u,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[11]=-1,t[12]=0,t[13]=0,t[15]=0,null!=e&&e!==1/0?(o=1/(a-e),t[10]=(e+a)*o,t[14]=2*e*a*o):(t[10]=-1,t[14]=-2*a);return t},n.perspectiveFromFieldOfView=function(t,n,r,a){var e=Math.tan(n.upDegrees*Math.PI/180),u=Math.tan(n.downDegrees*Math.PI/180),o=Math.tan(n.leftDegrees*Math.PI/180),i=Math.tan(n.rightDegrees*Math.PI/180),s=2/(o+i),c=2/(e+u);return t[0]=s,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=c,t[6]=0,t[7]=0,t[8]=-(o-i)*s*.5,t[9]=(e-u)*c*.5,t[10]=a/(r-a),t[11]=-1,t[12]=0,t[13]=0,t[14]=a*r/(r-a),t[15]=0,t},n.ortho=function(t,n,r,a,e,u,o){var i=1/(n-r),s=1/(a-e),c=1/(u-o);return t[0]=-2*i,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=-2*s,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=2*c,t[11]=0,t[12]=(n+r)*i,t[13]=(e+a)*s,t[14]=(o+u)*c,t[15]=1,t},n.lookAt=function(t,n,r,u){var o=void 0,i=void 0,s=void 0,c=void 0,f=void 0,M=void 0,h=void 0,l=void 0,v=void 0,d=void 0,b=n[0],m=n[1],p=n[2],P=u[0],A=u[1],E=u[2],O=r[0],R=r[1],y=r[2];if(Math.abs(b-O)<a.EPSILON&&Math.abs(m-R)<a.EPSILON&&Math.abs(p-y)<a.EPSILON)return e(t);h=b-O,l=m-R,v=p-y,d=1/Math.sqrt(h*h+l*l+v*v),o=A*(v*=d)-E*(l*=d),i=E*(h*=d)-P*v,s=P*l-A*h,(d=Math.sqrt(o*o+i*i+s*s))?(o*=d=1/d,i*=d,s*=d):(o=0,i=0,s=0);c=l*s-v*i,f=v*o-h*s,M=h*i-l*o,(d=Math.sqrt(c*c+f*f+M*M))?(c*=d=1/d,f*=d,M*=d):(c=0,f=0,M=0);return t[0]=o,t[1]=c,t[2]=h,t[3]=0,t[4]=i,t[5]=f,t[6]=l,t[7]=0,t[8]=s,t[9]=M,t[10]=v,t[11]=0,t[12]=-(o*b+i*m+s*p),t[13]=-(c*b+f*m+M*p),t[14]=-(h*b+l*m+v*p),t[15]=1,t},n.targetTo=function(t,n,r,a){var e=n[0],u=n[1],o=n[2],i=a[0],s=a[1],c=a[2],f=e-r[0],M=u-r[1],h=o-r[2],l=f*f+M*M+h*h;l>0&&(l=1/Math.sqrt(l),f*=l,M*=l,h*=l);var v=s*h-c*M,d=c*f-i*h,b=i*M-s*f;(l=v*v+d*d+b*b)>0&&(l=1/Math.sqrt(l),v*=l,d*=l,b*=l);return t[0]=v,t[1]=d,t[2]=b,t[3]=0,t[4]=M*b-h*d,t[5]=h*v-f*b,t[6]=f*d-M*v,t[7]=0,t[8]=f,t[9]=M,t[10]=h,t[11]=0,t[12]=e,t[13]=u,t[14]=o,t[15]=1,t},n.str=function(t){return\"mat4(\"+t[0]+\", \"+t[1]+\", \"+t[2]+\", \"+t[3]+\", \"+t[4]+\", \"+t[5]+\", \"+t[6]+\", \"+t[7]+\", \"+t[8]+\", \"+t[9]+\", \"+t[10]+\", \"+t[11]+\", \"+t[12]+\", \"+t[13]+\", \"+t[14]+\", \"+t[15]+\")\"},n.frob=function(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2)+Math.pow(t[4],2)+Math.pow(t[5],2)+Math.pow(t[6],2)+Math.pow(t[7],2)+Math.pow(t[8],2)+Math.pow(t[9],2)+Math.pow(t[10],2)+Math.pow(t[11],2)+Math.pow(t[12],2)+Math.pow(t[13],2)+Math.pow(t[14],2)+Math.pow(t[15],2))},n.add=function(t,n,r){return t[0]=n[0]+r[0],t[1]=n[1]+r[1],t[2]=n[2]+r[2],t[3]=n[3]+r[3],t[4]=n[4]+r[4],t[5]=n[5]+r[5],t[6]=n[6]+r[6],t[7]=n[7]+r[7],t[8]=n[8]+r[8],t[9]=n[9]+r[9],t[10]=n[10]+r[10],t[11]=n[11]+r[11],t[12]=n[12]+r[12],t[13]=n[13]+r[13],t[14]=n[14]+r[14],t[15]=n[15]+r[15],t},n.subtract=i,n.multiplyScalar=function(t,n,r){return t[0]=n[0]*r,t[1]=n[1]*r,t[2]=n[2]*r,t[3]=n[3]*r,t[4]=n[4]*r,t[5]=n[5]*r,t[6]=n[6]*r,t[7]=n[7]*r,t[8]=n[8]*r,t[9]=n[9]*r,t[10]=n[10]*r,t[11]=n[11]*r,t[12]=n[12]*r,t[13]=n[13]*r,t[14]=n[14]*r,t[15]=n[15]*r,t},n.multiplyScalarAndAdd=function(t,n,r,a){return t[0]=n[0]+r[0]*a,t[1]=n[1]+r[1]*a,t[2]=n[2]+r[2]*a,t[3]=n[3]+r[3]*a,t[4]=n[4]+r[4]*a,t[5]=n[5]+r[5]*a,t[6]=n[6]+r[6]*a,t[7]=n[7]+r[7]*a,t[8]=n[8]+r[8]*a,t[9]=n[9]+r[9]*a,t[10]=n[10]+r[10]*a,t[11]=n[11]+r[11]*a,t[12]=n[12]+r[12]*a,t[13]=n[13]+r[13]*a,t[14]=n[14]+r[14]*a,t[15]=n[15]+r[15]*a,t},n.exactEquals=function(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]&&t[3]===n[3]&&t[4]===n[4]&&t[5]===n[5]&&t[6]===n[6]&&t[7]===n[7]&&t[8]===n[8]&&t[9]===n[9]&&t[10]===n[10]&&t[11]===n[11]&&t[12]===n[12]&&t[13]===n[13]&&t[14]===n[14]&&t[15]===n[15]},n.equals=function(t,n){var r=t[0],e=t[1],u=t[2],o=t[3],i=t[4],s=t[5],c=t[6],f=t[7],M=t[8],h=t[9],l=t[10],v=t[11],d=t[12],b=t[13],m=t[14],p=t[15],P=n[0],A=n[1],E=n[2],O=n[3],R=n[4],y=n[5],q=n[6],x=n[7],_=n[8],Y=n[9],L=n[10],S=n[11],w=n[12],I=n[13],N=n[14],g=n[15];return Math.abs(r-P)<=a.EPSILON*Math.max(1,Math.abs(r),Math.abs(P))&&Math.abs(e-A)<=a.EPSILON*Math.max(1,Math.abs(e),Math.abs(A))&&Math.abs(u-E)<=a.EPSILON*Math.max(1,Math.abs(u),Math.abs(E))&&Math.abs(o-O)<=a.EPSILON*Math.max(1,Math.abs(o),Math.abs(O))&&Math.abs(i-R)<=a.EPSILON*Math.max(1,Math.abs(i),Math.abs(R))&&Math.abs(s-y)<=a.EPSILON*Math.max(1,Math.abs(s),Math.abs(y))&&Math.abs(c-q)<=a.EPSILON*Math.max(1,Math.abs(c),Math.abs(q))&&Math.abs(f-x)<=a.EPSILON*Math.max(1,Math.abs(f),Math.abs(x))&&Math.abs(M-_)<=a.EPSILON*Math.max(1,Math.abs(M),Math.abs(_))&&Math.abs(h-Y)<=a.EPSILON*Math.max(1,Math.abs(h),Math.abs(Y))&&Math.abs(l-L)<=a.EPSILON*Math.max(1,Math.abs(l),Math.abs(L))&&Math.abs(v-S)<=a.EPSILON*Math.max(1,Math.abs(v),Math.abs(S))&&Math.abs(d-w)<=a.EPSILON*Math.max(1,Math.abs(d),Math.abs(w))&&Math.abs(b-I)<=a.EPSILON*Math.max(1,Math.abs(b),Math.abs(I))&&Math.abs(m-N)<=a.EPSILON*Math.max(1,Math.abs(m),Math.abs(N))&&Math.abs(p-g)<=a.EPSILON*Math.max(1,Math.abs(p),Math.abs(g))};var a=function(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}(r(0));function e(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=1,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=1,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t}function u(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],s=n[5],c=n[6],f=n[7],M=n[8],h=n[9],l=n[10],v=n[11],d=n[12],b=n[13],m=n[14],p=n[15],P=r[0],A=r[1],E=r[2],O=r[3];return t[0]=P*a+A*i+E*M+O*d,t[1]=P*e+A*s+E*h+O*b,t[2]=P*u+A*c+E*l+O*m,t[3]=P*o+A*f+E*v+O*p,P=r[4],A=r[5],E=r[6],O=r[7],t[4]=P*a+A*i+E*M+O*d,t[5]=P*e+A*s+E*h+O*b,t[6]=P*u+A*c+E*l+O*m,t[7]=P*o+A*f+E*v+O*p,P=r[8],A=r[9],E=r[10],O=r[11],t[8]=P*a+A*i+E*M+O*d,t[9]=P*e+A*s+E*h+O*b,t[10]=P*u+A*c+E*l+O*m,t[11]=P*o+A*f+E*v+O*p,P=r[12],A=r[13],E=r[14],O=r[15],t[12]=P*a+A*i+E*M+O*d,t[13]=P*e+A*s+E*h+O*b,t[14]=P*u+A*c+E*l+O*m,t[15]=P*o+A*f+E*v+O*p,t}function o(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=a+a,s=e+e,c=u+u,f=a*i,M=a*s,h=a*c,l=e*s,v=e*c,d=u*c,b=o*i,m=o*s,p=o*c;return t[0]=1-(l+d),t[1]=M+p,t[2]=h-m,t[3]=0,t[4]=M-p,t[5]=1-(f+d),t[6]=v+b,t[7]=0,t[8]=h+m,t[9]=v-b,t[10]=1-(f+l),t[11]=0,t[12]=r[0],t[13]=r[1],t[14]=r[2],t[15]=1,t}function i(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t[2]=n[2]-r[2],t[3]=n[3]-r[3],t[4]=n[4]-r[4],t[5]=n[5]-r[5],t[6]=n[6]-r[6],t[7]=n[7]-r[7],t[8]=n[8]-r[8],t[9]=n[9]-r[9],t[10]=n[10]-r[10],t[11]=n[11]-r[11],t[12]=n[12]-r[12],t[13]=n[13]-r[13],t[14]=n[14]-r[14],t[15]=n[15]-r[15],t}n.mul=u,n.sub=i},function(t,n,r){\"use strict\";Object.defineProperty(n,\"__esModule\",{value:!0}),n.sub=n.mul=void 0,n.create=function(){var t=new a.ARRAY_TYPE(9);a.ARRAY_TYPE!=Float32Array&&(t[1]=0,t[2]=0,t[3]=0,t[5]=0,t[6]=0,t[7]=0);return t[0]=1,t[4]=1,t[8]=1,t},n.fromMat4=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[4],t[4]=n[5],t[5]=n[6],t[6]=n[8],t[7]=n[9],t[8]=n[10],t},n.clone=function(t){var n=new a.ARRAY_TYPE(9);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n[4]=t[4],n[5]=t[5],n[6]=t[6],n[7]=t[7],n[8]=t[8],n},n.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[4]=n[4],t[5]=n[5],t[6]=n[6],t[7]=n[7],t[8]=n[8],t},n.fromValues=function(t,n,r,e,u,o,i,s,c){var f=new a.ARRAY_TYPE(9);return f[0]=t,f[1]=n,f[2]=r,f[3]=e,f[4]=u,f[5]=o,f[6]=i,f[7]=s,f[8]=c,f},n.set=function(t,n,r,a,e,u,o,i,s,c){return t[0]=n,t[1]=r,t[2]=a,t[3]=e,t[4]=u,t[5]=o,t[6]=i,t[7]=s,t[8]=c,t},n.identity=function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=1,t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},n.transpose=function(t,n){if(t===n){var r=n[1],a=n[2],e=n[5];t[1]=n[3],t[2]=n[6],t[3]=r,t[5]=n[7],t[6]=a,t[7]=e}else t[0]=n[0],t[1]=n[3],t[2]=n[6],t[3]=n[1],t[4]=n[4],t[5]=n[7],t[6]=n[2],t[7]=n[5],t[8]=n[8];return t},n.invert=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],s=n[6],c=n[7],f=n[8],M=f*o-i*c,h=-f*u+i*s,l=c*u-o*s,v=r*M+a*h+e*l;if(!v)return null;return v=1/v,t[0]=M*v,t[1]=(-f*a+e*c)*v,t[2]=(i*a-e*o)*v,t[3]=h*v,t[4]=(f*r-e*s)*v,t[5]=(-i*r+e*u)*v,t[6]=l*v,t[7]=(-c*r+a*s)*v,t[8]=(o*r-a*u)*v,t},n.adjoint=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],s=n[6],c=n[7],f=n[8];return t[0]=o*f-i*c,t[1]=e*c-a*f,t[2]=a*i-e*o,t[3]=i*s-u*f,t[4]=r*f-e*s,t[5]=e*u-r*i,t[6]=u*c-o*s,t[7]=a*s-r*c,t[8]=r*o-a*u,t},n.determinant=function(t){var n=t[0],r=t[1],a=t[2],e=t[3],u=t[4],o=t[5],i=t[6],s=t[7],c=t[8];return n*(c*u-o*s)+r*(-c*e+o*i)+a*(s*e-u*i)},n.multiply=e,n.translate=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],s=n[5],c=n[6],f=n[7],M=n[8],h=r[0],l=r[1];return t[0]=a,t[1]=e,t[2]=u,t[3]=o,t[4]=i,t[5]=s,t[6]=h*a+l*o+c,t[7]=h*e+l*i+f,t[8]=h*u+l*s+M,t},n.rotate=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],s=n[5],c=n[6],f=n[7],M=n[8],h=Math.sin(r),l=Math.cos(r);return t[0]=l*a+h*o,t[1]=l*e+h*i,t[2]=l*u+h*s,t[3]=l*o-h*a,t[4]=l*i-h*e,t[5]=l*s-h*u,t[6]=c,t[7]=f,t[8]=M,t},n.scale=function(t,n,r){var a=r[0],e=r[1];return t[0]=a*n[0],t[1]=a*n[1],t[2]=a*n[2],t[3]=e*n[3],t[4]=e*n[4],t[5]=e*n[5],t[6]=n[6],t[7]=n[7],t[8]=n[8],t},n.fromTranslation=function(t,n){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=1,t[5]=0,t[6]=n[0],t[7]=n[1],t[8]=1,t},n.fromRotation=function(t,n){var r=Math.sin(n),a=Math.cos(n);return t[0]=a,t[1]=r,t[2]=0,t[3]=-r,t[4]=a,t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},n.fromScaling=function(t,n){return t[0]=n[0],t[1]=0,t[2]=0,t[3]=0,t[4]=n[1],t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},n.fromMat2d=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=0,t[3]=n[2],t[4]=n[3],t[5]=0,t[6]=n[4],t[7]=n[5],t[8]=1,t},n.fromQuat=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=r+r,i=a+a,s=e+e,c=r*o,f=a*o,M=a*i,h=e*o,l=e*i,v=e*s,d=u*o,b=u*i,m=u*s;return t[0]=1-M-v,t[3]=f-m,t[6]=h+b,t[1]=f+m,t[4]=1-c-v,t[7]=l-d,t[2]=h-b,t[5]=l+d,t[8]=1-c-M,t},n.normalFromMat4=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],s=n[6],c=n[7],f=n[8],M=n[9],h=n[10],l=n[11],v=n[12],d=n[13],b=n[14],m=n[15],p=r*i-a*o,P=r*s-e*o,A=r*c-u*o,E=a*s-e*i,O=a*c-u*i,R=e*c-u*s,y=f*d-M*v,q=f*b-h*v,x=f*m-l*v,_=M*b-h*d,Y=M*m-l*d,L=h*m-l*b,S=p*L-P*Y+A*_+E*x-O*q+R*y;if(!S)return null;return S=1/S,t[0]=(i*L-s*Y+c*_)*S,t[1]=(s*x-o*L-c*q)*S,t[2]=(o*Y-i*x+c*y)*S,t[3]=(e*Y-a*L-u*_)*S,t[4]=(r*L-e*x+u*q)*S,t[5]=(a*x-r*Y-u*y)*S,t[6]=(d*R-b*O+m*E)*S,t[7]=(b*A-v*R-m*P)*S,t[8]=(v*O-d*A+m*p)*S,t},n.projection=function(t,n,r){return t[0]=2/n,t[1]=0,t[2]=0,t[3]=0,t[4]=-2/r,t[5]=0,t[6]=-1,t[7]=1,t[8]=1,t},n.str=function(t){return\"mat3(\"+t[0]+\", \"+t[1]+\", \"+t[2]+\", \"+t[3]+\", \"+t[4]+\", \"+t[5]+\", \"+t[6]+\", \"+t[7]+\", \"+t[8]+\")\"},n.frob=function(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2)+Math.pow(t[4],2)+Math.pow(t[5],2)+Math.pow(t[6],2)+Math.pow(t[7],2)+Math.pow(t[8],2))},n.add=function(t,n,r){return t[0]=n[0]+r[0],t[1]=n[1]+r[1],t[2]=n[2]+r[2],t[3]=n[3]+r[3],t[4]=n[4]+r[4],t[5]=n[5]+r[5],t[6]=n[6]+r[6],t[7]=n[7]+r[7],t[8]=n[8]+r[8],t},n.subtract=u,n.multiplyScalar=function(t,n,r){return t[0]=n[0]*r,t[1]=n[1]*r,t[2]=n[2]*r,t[3]=n[3]*r,t[4]=n[4]*r,t[5]=n[5]*r,t[6]=n[6]*r,t[7]=n[7]*r,t[8]=n[8]*r,t},n.multiplyScalarAndAdd=function(t,n,r,a){return t[0]=n[0]+r[0]*a,t[1]=n[1]+r[1]*a,t[2]=n[2]+r[2]*a,t[3]=n[3]+r[3]*a,t[4]=n[4]+r[4]*a,t[5]=n[5]+r[5]*a,t[6]=n[6]+r[6]*a,t[7]=n[7]+r[7]*a,t[8]=n[8]+r[8]*a,t},n.exactEquals=function(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]&&t[3]===n[3]&&t[4]===n[4]&&t[5]===n[5]&&t[6]===n[6]&&t[7]===n[7]&&t[8]===n[8]},n.equals=function(t,n){var r=t[0],e=t[1],u=t[2],o=t[3],i=t[4],s=t[5],c=t[6],f=t[7],M=t[8],h=n[0],l=n[1],v=n[2],d=n[3],b=n[4],m=n[5],p=n[6],P=n[7],A=n[8];return Math.abs(r-h)<=a.EPSILON*Math.max(1,Math.abs(r),Math.abs(h))&&Math.abs(e-l)<=a.EPSILON*Math.max(1,Math.abs(e),Math.abs(l))&&Math.abs(u-v)<=a.EPSILON*Math.max(1,Math.abs(u),Math.abs(v))&&Math.abs(o-d)<=a.EPSILON*Math.max(1,Math.abs(o),Math.abs(d))&&Math.abs(i-b)<=a.EPSILON*Math.max(1,Math.abs(i),Math.abs(b))&&Math.abs(s-m)<=a.EPSILON*Math.max(1,Math.abs(s),Math.abs(m))&&Math.abs(c-p)<=a.EPSILON*Math.max(1,Math.abs(c),Math.abs(p))&&Math.abs(f-P)<=a.EPSILON*Math.max(1,Math.abs(f),Math.abs(P))&&Math.abs(M-A)<=a.EPSILON*Math.max(1,Math.abs(M),Math.abs(A))};var a=function(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}(r(0));function e(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],s=n[5],c=n[6],f=n[7],M=n[8],h=r[0],l=r[1],v=r[2],d=r[3],b=r[4],m=r[5],p=r[6],P=r[7],A=r[8];return t[0]=h*a+l*o+v*c,t[1]=h*e+l*i+v*f,t[2]=h*u+l*s+v*M,t[3]=d*a+b*o+m*c,t[4]=d*e+b*i+m*f,t[5]=d*u+b*s+m*M,t[6]=p*a+P*o+A*c,t[7]=p*e+P*i+A*f,t[8]=p*u+P*s+A*M,t}function u(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t[2]=n[2]-r[2],t[3]=n[3]-r[3],t[4]=n[4]-r[4],t[5]=n[5]-r[5],t[6]=n[6]-r[6],t[7]=n[7]-r[7],t[8]=n[8]-r[8],t}n.mul=e,n.sub=u},function(t,n,r){\"use strict\";Object.defineProperty(n,\"__esModule\",{value:!0}),n.forEach=n.sqrLen=n.sqrDist=n.dist=n.div=n.mul=n.sub=n.len=void 0,n.create=e,n.clone=function(t){var n=new a.ARRAY_TYPE(2);return n[0]=t[0],n[1]=t[1],n},n.fromValues=function(t,n){var r=new a.ARRAY_TYPE(2);return r[0]=t,r[1]=n,r},n.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t},n.set=function(t,n,r){return t[0]=n,t[1]=r,t},n.add=function(t,n,r){return t[0]=n[0]+r[0],t[1]=n[1]+r[1],t},n.subtract=u,n.multiply=o,n.divide=i,n.ceil=function(t,n){return t[0]=Math.ceil(n[0]),t[1]=Math.ceil(n[1]),t},n.floor=function(t,n){return t[0]=Math.floor(n[0]),t[1]=Math.floor(n[1]),t},n.min=function(t,n,r){return t[0]=Math.min(n[0],r[0]),t[1]=Math.min(n[1],r[1]),t},n.max=function(t,n,r){return t[0]=Math.max(n[0],r[0]),t[1]=Math.max(n[1],r[1]),t},n.round=function(t,n){return t[0]=Math.round(n[0]),t[1]=Math.round(n[1]),t},n.scale=function(t,n,r){return t[0]=n[0]*r,t[1]=n[1]*r,t},n.scaleAndAdd=function(t,n,r,a){return t[0]=n[0]+r[0]*a,t[1]=n[1]+r[1]*a,t},n.distance=s,n.squaredDistance=c,n.length=f,n.squaredLength=M,n.negate=function(t,n){return t[0]=-n[0],t[1]=-n[1],t},n.inverse=function(t,n){return t[0]=1/n[0],t[1]=1/n[1],t},n.normalize=function(t,n){var r=n[0],a=n[1],e=r*r+a*a;e>0&&(e=1/Math.sqrt(e),t[0]=n[0]*e,t[1]=n[1]*e);return t},n.dot=function(t,n){return t[0]*n[0]+t[1]*n[1]},n.cross=function(t,n,r){var a=n[0]*r[1]-n[1]*r[0];return t[0]=t[1]=0,t[2]=a,t},n.lerp=function(t,n,r,a){var e=n[0],u=n[1];return t[0]=e+a*(r[0]-e),t[1]=u+a*(r[1]-u),t},n.random=function(t,n){n=n||1;var r=2*a.RANDOM()*Math.PI;return t[0]=Math.cos(r)*n,t[1]=Math.sin(r)*n,t},n.transformMat2=function(t,n,r){var a=n[0],e=n[1];return t[0]=r[0]*a+r[2]*e,t[1]=r[1]*a+r[3]*e,t},n.transformMat2d=function(t,n,r){var a=n[0],e=n[1];return t[0]=r[0]*a+r[2]*e+r[4],t[1]=r[1]*a+r[3]*e+r[5],t},n.transformMat3=function(t,n,r){var a=n[0],e=n[1];return t[0]=r[0]*a+r[3]*e+r[6],t[1]=r[1]*a+r[4]*e+r[7],t},n.transformMat4=function(t,n,r){var a=n[0],e=n[1];return t[0]=r[0]*a+r[4]*e+r[12],t[1]=r[1]*a+r[5]*e+r[13],t},n.rotate=function(t,n,r,a){var e=n[0]-r[0],u=n[1]-r[1],o=Math.sin(a),i=Math.cos(a);return t[0]=e*i-u*o+r[0],t[1]=e*o+u*i+r[1],t},n.angle=function(t,n){var r=t[0],a=t[1],e=n[0],u=n[1],o=r*r+a*a;o>0&&(o=1/Math.sqrt(o));var i=e*e+u*u;i>0&&(i=1/Math.sqrt(i));var s=(r*e+a*u)*o*i;return s>1?0:s<-1?Math.PI:Math.acos(s)},n.str=function(t){return\"vec2(\"+t[0]+\", \"+t[1]+\")\"},n.exactEquals=function(t,n){return t[0]===n[0]&&t[1]===n[1]},n.equals=function(t,n){var r=t[0],e=t[1],u=n[0],o=n[1];return Math.abs(r-u)<=a.EPSILON*Math.max(1,Math.abs(r),Math.abs(u))&&Math.abs(e-o)<=a.EPSILON*Math.max(1,Math.abs(e),Math.abs(o))};var a=function(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}(r(0));function e(){var t=new a.ARRAY_TYPE(2);return a.ARRAY_TYPE!=Float32Array&&(t[0]=0,t[1]=0),t}function u(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t}function o(t,n,r){return t[0]=n[0]*r[0],t[1]=n[1]*r[1],t}function i(t,n,r){return t[0]=n[0]/r[0],t[1]=n[1]/r[1],t}function s(t,n){var r=n[0]-t[0],a=n[1]-t[1];return Math.sqrt(r*r+a*a)}function c(t,n){var r=n[0]-t[0],a=n[1]-t[1];return r*r+a*a}function f(t){var n=t[0],r=t[1];return Math.sqrt(n*n+r*r)}function M(t){var n=t[0],r=t[1];return n*n+r*r}n.len=f,n.sub=u,n.mul=o,n.div=i,n.dist=s,n.sqrDist=c,n.sqrLen=M,n.forEach=function(){var t=e();return function(n,r,a,e,u,o){var i=void 0,s=void 0;for(r||(r=2),a||(a=0),s=e?Math.min(e*r+a,n.length):n.length,i=a;i<s;i+=r)t[0]=n[i],t[1]=n[i+1],u(t,t,o),n[i]=t[0],n[i+1]=t[1];return n}}()},function(t,n,r){\"use strict\";Object.defineProperty(n,\"__esModule\",{value:!0}),n.sqrLen=n.squaredLength=n.len=n.length=n.dot=n.mul=n.setReal=n.getReal=void 0,n.create=function(){var t=new a.ARRAY_TYPE(8);a.ARRAY_TYPE!=Float32Array&&(t[0]=0,t[1]=0,t[2]=0,t[4]=0,t[5]=0,t[6]=0,t[7]=0);return t[3]=1,t},n.clone=function(t){var n=new a.ARRAY_TYPE(8);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n[4]=t[4],n[5]=t[5],n[6]=t[6],n[7]=t[7],n},n.fromValues=function(t,n,r,e,u,o,i,s){var c=new a.ARRAY_TYPE(8);return c[0]=t,c[1]=n,c[2]=r,c[3]=e,c[4]=u,c[5]=o,c[6]=i,c[7]=s,c},n.fromRotationTranslationValues=function(t,n,r,e,u,o,i){var s=new a.ARRAY_TYPE(8);s[0]=t,s[1]=n,s[2]=r,s[3]=e;var c=.5*u,f=.5*o,M=.5*i;return s[4]=c*e+f*r-M*n,s[5]=f*e+M*t-c*r,s[6]=M*e+c*n-f*t,s[7]=-c*t-f*n-M*r,s},n.fromRotationTranslation=i,n.fromTranslation=function(t,n){return t[0]=0,t[1]=0,t[2]=0,t[3]=1,t[4]=.5*n[0],t[5]=.5*n[1],t[6]=.5*n[2],t[7]=0,t},n.fromRotation=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[4]=0,t[5]=0,t[6]=0,t[7]=0,t},n.fromMat4=function(t,n){var r=e.create();u.getRotation(r,n);var o=new a.ARRAY_TYPE(3);return u.getTranslation(o,n),i(t,r,o),t},n.copy=s,n.identity=function(t){return t[0]=0,t[1]=0,t[2]=0,t[3]=1,t[4]=0,t[5]=0,t[6]=0,t[7]=0,t},n.set=function(t,n,r,a,e,u,o,i,s){return t[0]=n,t[1]=r,t[2]=a,t[3]=e,t[4]=u,t[5]=o,t[6]=i,t[7]=s,t},n.getDual=function(t,n){return t[0]=n[4],t[1]=n[5],t[2]=n[6],t[3]=n[7],t},n.setDual=function(t,n){return t[4]=n[0],t[5]=n[1],t[6]=n[2],t[7]=n[3],t},n.getTranslation=function(t,n){var r=n[4],a=n[5],e=n[6],u=n[7],o=-n[0],i=-n[1],s=-n[2],c=n[3];return t[0]=2*(r*c+u*o+a*s-e*i),t[1]=2*(a*c+u*i+e*o-r*s),t[2]=2*(e*c+u*s+r*i-a*o),t},n.translate=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=.5*r[0],s=.5*r[1],c=.5*r[2],f=n[4],M=n[5],h=n[6],l=n[7];return t[0]=a,t[1]=e,t[2]=u,t[3]=o,t[4]=o*i+e*c-u*s+f,t[5]=o*s+u*i-a*c+M,t[6]=o*c+a*s-e*i+h,t[7]=-a*i-e*s-u*c+l,t},n.rotateX=function(t,n,r){var a=-n[0],u=-n[1],o=-n[2],i=n[3],s=n[4],c=n[5],f=n[6],M=n[7],h=s*i+M*a+c*o-f*u,l=c*i+M*u+f*a-s*o,v=f*i+M*o+s*u-c*a,d=M*i-s*a-c*u-f*o;return e.rotateX(t,n,r),a=t[0],u=t[1],o=t[2],i=t[3],t[4]=h*i+d*a+l*o-v*u,t[5]=l*i+d*u+v*a-h*o,t[6]=v*i+d*o+h*u-l*a,t[7]=d*i-h*a-l*u-v*o,t},n.rotateY=function(t,n,r){var a=-n[0],u=-n[1],o=-n[2],i=n[3],s=n[4],c=n[5],f=n[6],M=n[7],h=s*i+M*a+c*o-f*u,l=c*i+M*u+f*a-s*o,v=f*i+M*o+s*u-c*a,d=M*i-s*a-c*u-f*o;return e.rotateY(t,n,r),a=t[0],u=t[1],o=t[2],i=t[3],t[4]=h*i+d*a+l*o-v*u,t[5]=l*i+d*u+v*a-h*o,t[6]=v*i+d*o+h*u-l*a,t[7]=d*i-h*a-l*u-v*o,t},n.rotateZ=function(t,n,r){var a=-n[0],u=-n[1],o=-n[2],i=n[3],s=n[4],c=n[5],f=n[6],M=n[7],h=s*i+M*a+c*o-f*u,l=c*i+M*u+f*a-s*o,v=f*i+M*o+s*u-c*a,d=M*i-s*a-c*u-f*o;return e.rotateZ(t,n,r),a=t[0],u=t[1],o=t[2],i=t[3],t[4]=h*i+d*a+l*o-v*u,t[5]=l*i+d*u+v*a-h*o,t[6]=v*i+d*o+h*u-l*a,t[7]=d*i-h*a-l*u-v*o,t},n.rotateByQuatAppend=function(t,n,r){var a=r[0],e=r[1],u=r[2],o=r[3],i=n[0],s=n[1],c=n[2],f=n[3];return t[0]=i*o+f*a+s*u-c*e,t[1]=s*o+f*e+c*a-i*u,t[2]=c*o+f*u+i*e-s*a,t[3]=f*o-i*a-s*e-c*u,i=n[4],s=n[5],c=n[6],f=n[7],t[4]=i*o+f*a+s*u-c*e,t[5]=s*o+f*e+c*a-i*u,t[6]=c*o+f*u+i*e-s*a,t[7]=f*o-i*a-s*e-c*u,t},n.rotateByQuatPrepend=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=r[0],s=r[1],c=r[2],f=r[3];return t[0]=a*f+o*i+e*c-u*s,t[1]=e*f+o*s+u*i-a*c,t[2]=u*f+o*c+a*s-e*i,t[3]=o*f-a*i-e*s-u*c,i=r[4],s=r[5],c=r[6],f=r[7],t[4]=a*f+o*i+e*c-u*s,t[5]=e*f+o*s+u*i-a*c,t[6]=u*f+o*c+a*s-e*i,t[7]=o*f-a*i-e*s-u*c,t},n.rotateAroundAxis=function(t,n,r,e){if(Math.abs(e)<a.EPSILON)return s(t,n);var u=Math.sqrt(r[0]*r[0]+r[1]*r[1]+r[2]*r[2]);e*=.5;var o=Math.sin(e),i=o*r[0]/u,c=o*r[1]/u,f=o*r[2]/u,M=Math.cos(e),h=n[0],l=n[1],v=n[2],d=n[3];t[0]=h*M+d*i+l*f-v*c,t[1]=l*M+d*c+v*i-h*f,t[2]=v*M+d*f+h*c-l*i,t[3]=d*M-h*i-l*c-v*f;var b=n[4],m=n[5],p=n[6],P=n[7];return t[4]=b*M+P*i+m*f-p*c,t[5]=m*M+P*c+p*i-b*f,t[6]=p*M+P*f+b*c-m*i,t[7]=P*M-b*i-m*c-p*f,t},n.add=function(t,n,r){return t[0]=n[0]+r[0],t[1]=n[1]+r[1],t[2]=n[2]+r[2],t[3]=n[3]+r[3],t[4]=n[4]+r[4],t[5]=n[5]+r[5],t[6]=n[6]+r[6],t[7]=n[7]+r[7],t},n.multiply=c,n.scale=function(t,n,r){return t[0]=n[0]*r,t[1]=n[1]*r,t[2]=n[2]*r,t[3]=n[3]*r,t[4]=n[4]*r,t[5]=n[5]*r,t[6]=n[6]*r,t[7]=n[7]*r,t},n.lerp=function(t,n,r,a){var e=1-a;f(n,r)<0&&(a=-a);return t[0]=n[0]*e+r[0]*a,t[1]=n[1]*e+r[1]*a,t[2]=n[2]*e+r[2]*a,t[3]=n[3]*e+r[3]*a,t[4]=n[4]*e+r[4]*a,t[5]=n[5]*e+r[5]*a,t[6]=n[6]*e+r[6]*a,t[7]=n[7]*e+r[7]*a,t},n.invert=function(t,n){var r=h(n);return t[0]=-n[0]/r,t[1]=-n[1]/r,t[2]=-n[2]/r,t[3]=n[3]/r,t[4]=-n[4]/r,t[5]=-n[5]/r,t[6]=-n[6]/r,t[7]=n[7]/r,t},n.conjugate=function(t,n){return t[0]=-n[0],t[1]=-n[1],t[2]=-n[2],t[3]=n[3],t[4]=-n[4],t[5]=-n[5],t[6]=-n[6],t[7]=n[7],t},n.normalize=function(t,n){var r=h(n);if(r>0){r=Math.sqrt(r);var a=n[0]/r,e=n[1]/r,u=n[2]/r,o=n[3]/r,i=n[4],s=n[5],c=n[6],f=n[7],M=a*i+e*s+u*c+o*f;t[0]=a,t[1]=e,t[2]=u,t[3]=o,t[4]=(i-a*M)/r,t[5]=(s-e*M)/r,t[6]=(c-u*M)/r,t[7]=(f-o*M)/r}return t},n.str=function(t){return\"quat2(\"+t[0]+\", \"+t[1]+\", \"+t[2]+\", \"+t[3]+\", \"+t[4]+\", \"+t[5]+\", \"+t[6]+\", \"+t[7]+\")\"},n.exactEquals=function(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]&&t[3]===n[3]&&t[4]===n[4]&&t[5]===n[5]&&t[6]===n[6]&&t[7]===n[7]},n.equals=function(t,n){var r=t[0],e=t[1],u=t[2],o=t[3],i=t[4],s=t[5],c=t[6],f=t[7],M=n[0],h=n[1],l=n[2],v=n[3],d=n[4],b=n[5],m=n[6],p=n[7];return Math.abs(r-M)<=a.EPSILON*Math.max(1,Math.abs(r),Math.abs(M))&&Math.abs(e-h)<=a.EPSILON*Math.max(1,Math.abs(e),Math.abs(h))&&Math.abs(u-l)<=a.EPSILON*Math.max(1,Math.abs(u),Math.abs(l))&&Math.abs(o-v)<=a.EPSILON*Math.max(1,Math.abs(o),Math.abs(v))&&Math.abs(i-d)<=a.EPSILON*Math.max(1,Math.abs(i),Math.abs(d))&&Math.abs(s-b)<=a.EPSILON*Math.max(1,Math.abs(s),Math.abs(b))&&Math.abs(c-m)<=a.EPSILON*Math.max(1,Math.abs(c),Math.abs(m))&&Math.abs(f-p)<=a.EPSILON*Math.max(1,Math.abs(f),Math.abs(p))};var a=o(r(0)),e=o(r(3)),u=o(r(4));function o(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}function i(t,n,r){var a=.5*r[0],e=.5*r[1],u=.5*r[2],o=n[0],i=n[1],s=n[2],c=n[3];return t[0]=o,t[1]=i,t[2]=s,t[3]=c,t[4]=a*c+e*s-u*i,t[5]=e*c+u*o-a*s,t[6]=u*c+a*i-e*o,t[7]=-a*o-e*i-u*s,t}function s(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[4]=n[4],t[5]=n[5],t[6]=n[6],t[7]=n[7],t}n.getReal=e.copy;n.setReal=e.copy;function c(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=r[4],s=r[5],c=r[6],f=r[7],M=n[4],h=n[5],l=n[6],v=n[7],d=r[0],b=r[1],m=r[2],p=r[3];return t[0]=a*p+o*d+e*m-u*b,t[1]=e*p+o*b+u*d-a*m,t[2]=u*p+o*m+a*b-e*d,t[3]=o*p-a*d-e*b-u*m,t[4]=a*f+o*i+e*c-u*s+M*p+v*d+h*m-l*b,t[5]=e*f+o*s+u*i-a*c+h*p+v*b+l*d-M*m,t[6]=u*f+o*c+a*s-e*i+l*p+v*m+M*b-h*d,t[7]=o*f-a*i-e*s-u*c+v*p-M*d-h*b-l*m,t}n.mul=c;var f=n.dot=e.dot;var M=n.length=e.length,h=(n.len=M,n.squaredLength=e.squaredLength);n.sqrLen=h},function(t,n,r){\"use strict\";Object.defineProperty(n,\"__esModule\",{value:!0}),n.sub=n.mul=void 0,n.create=function(){var t=new a.ARRAY_TYPE(6);a.ARRAY_TYPE!=Float32Array&&(t[1]=0,t[2]=0,t[4]=0,t[5]=0);return t[0]=1,t[3]=1,t},n.clone=function(t){var n=new a.ARRAY_TYPE(6);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n[4]=t[4],n[5]=t[5],n},n.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[4]=n[4],t[5]=n[5],t},n.identity=function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t[4]=0,t[5]=0,t},n.fromValues=function(t,n,r,e,u,o){var i=new a.ARRAY_TYPE(6);return i[0]=t,i[1]=n,i[2]=r,i[3]=e,i[4]=u,i[5]=o,i},n.set=function(t,n,r,a,e,u,o){return t[0]=n,t[1]=r,t[2]=a,t[3]=e,t[4]=u,t[5]=o,t},n.invert=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],s=r*u-a*e;if(!s)return null;return s=1/s,t[0]=u*s,t[1]=-a*s,t[2]=-e*s,t[3]=r*s,t[4]=(e*i-u*o)*s,t[5]=(a*o-r*i)*s,t},n.determinant=function(t){return t[0]*t[3]-t[1]*t[2]},n.multiply=e,n.rotate=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],s=n[5],c=Math.sin(r),f=Math.cos(r);return t[0]=a*f+u*c,t[1]=e*f+o*c,t[2]=a*-c+u*f,t[3]=e*-c+o*f,t[4]=i,t[5]=s,t},n.scale=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],s=n[5],c=r[0],f=r[1];return t[0]=a*c,t[1]=e*c,t[2]=u*f,t[3]=o*f,t[4]=i,t[5]=s,t},n.translate=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],s=n[5],c=r[0],f=r[1];return t[0]=a,t[1]=e,t[2]=u,t[3]=o,t[4]=a*c+u*f+i,t[5]=e*c+o*f+s,t},n.fromRotation=function(t,n){var r=Math.sin(n),a=Math.cos(n);return t[0]=a,t[1]=r,t[2]=-r,t[3]=a,t[4]=0,t[5]=0,t},n.fromScaling=function(t,n){return t[0]=n[0],t[1]=0,t[2]=0,t[3]=n[1],t[4]=0,t[5]=0,t},n.fromTranslation=function(t,n){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t[4]=n[0],t[5]=n[1],t},n.str=function(t){return\"mat2d(\"+t[0]+\", \"+t[1]+\", \"+t[2]+\", \"+t[3]+\", \"+t[4]+\", \"+t[5]+\")\"},n.frob=function(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2)+Math.pow(t[4],2)+Math.pow(t[5],2)+1)},n.add=function(t,n,r){return t[0]=n[0]+r[0],t[1]=n[1]+r[1],t[2]=n[2]+r[2],t[3]=n[3]+r[3],t[4]=n[4]+r[4],t[5]=n[5]+r[5],t},n.subtract=u,n.multiplyScalar=function(t,n,r){return t[0]=n[0]*r,t[1]=n[1]*r,t[2]=n[2]*r,t[3]=n[3]*r,t[4]=n[4]*r,t[5]=n[5]*r,t},n.multiplyScalarAndAdd=function(t,n,r,a){return t[0]=n[0]+r[0]*a,t[1]=n[1]+r[1]*a,t[2]=n[2]+r[2]*a,t[3]=n[3]+r[3]*a,t[4]=n[4]+r[4]*a,t[5]=n[5]+r[5]*a,t},n.exactEquals=function(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]&&t[3]===n[3]&&t[4]===n[4]&&t[5]===n[5]},n.equals=function(t,n){var r=t[0],e=t[1],u=t[2],o=t[3],i=t[4],s=t[5],c=n[0],f=n[1],M=n[2],h=n[3],l=n[4],v=n[5];return Math.abs(r-c)<=a.EPSILON*Math.max(1,Math.abs(r),Math.abs(c))&&Math.abs(e-f)<=a.EPSILON*Math.max(1,Math.abs(e),Math.abs(f))&&Math.abs(u-M)<=a.EPSILON*Math.max(1,Math.abs(u),Math.abs(M))&&Math.abs(o-h)<=a.EPSILON*Math.max(1,Math.abs(o),Math.abs(h))&&Math.abs(i-l)<=a.EPSILON*Math.max(1,Math.abs(i),Math.abs(l))&&Math.abs(s-v)<=a.EPSILON*Math.max(1,Math.abs(s),Math.abs(v))};var a=function(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}(r(0));function e(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],s=n[5],c=r[0],f=r[1],M=r[2],h=r[3],l=r[4],v=r[5];return t[0]=a*c+u*f,t[1]=e*c+o*f,t[2]=a*M+u*h,t[3]=e*M+o*h,t[4]=a*l+u*v+i,t[5]=e*l+o*v+s,t}function u(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t[2]=n[2]-r[2],t[3]=n[3]-r[3],t[4]=n[4]-r[4],t[5]=n[5]-r[5],t}n.mul=e,n.sub=u},function(t,n,r){\"use strict\";Object.defineProperty(n,\"__esModule\",{value:!0}),n.sub=n.mul=void 0,n.create=function(){var t=new a.ARRAY_TYPE(4);a.ARRAY_TYPE!=Float32Array&&(t[1]=0,t[2]=0);return t[0]=1,t[3]=1,t},n.clone=function(t){var n=new a.ARRAY_TYPE(4);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n},n.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t},n.identity=function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t},n.fromValues=function(t,n,r,e){var u=new a.ARRAY_TYPE(4);return u[0]=t,u[1]=n,u[2]=r,u[3]=e,u},n.set=function(t,n,r,a,e){return t[0]=n,t[1]=r,t[2]=a,t[3]=e,t},n.transpose=function(t,n){if(t===n){var r=n[1];t[1]=n[2],t[2]=r}else t[0]=n[0],t[1]=n[2],t[2]=n[1],t[3]=n[3];return t},n.invert=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=r*u-e*a;if(!o)return null;return o=1/o,t[0]=u*o,t[1]=-a*o,t[2]=-e*o,t[3]=r*o,t},n.adjoint=function(t,n){var r=n[0];return t[0]=n[3],t[1]=-n[1],t[2]=-n[2],t[3]=r,t},n.determinant=function(t){return t[0]*t[3]-t[2]*t[1]},n.multiply=e,n.rotate=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=Math.sin(r),s=Math.cos(r);return t[0]=a*s+u*i,t[1]=e*s+o*i,t[2]=a*-i+u*s,t[3]=e*-i+o*s,t},n.scale=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=r[0],s=r[1];return t[0]=a*i,t[1]=e*i,t[2]=u*s,t[3]=o*s,t},n.fromRotation=function(t,n){var r=Math.sin(n),a=Math.cos(n);return t[0]=a,t[1]=r,t[2]=-r,t[3]=a,t},n.fromScaling=function(t,n){return t[0]=n[0],t[1]=0,t[2]=0,t[3]=n[1],t},n.str=function(t){return\"mat2(\"+t[0]+\", \"+t[1]+\", \"+t[2]+\", \"+t[3]+\")\"},n.frob=function(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2))},n.LDU=function(t,n,r,a){return t[2]=a[2]/a[0],r[0]=a[0],r[1]=a[1],r[3]=a[3]-t[2]*r[1],[t,n,r]},n.add=function(t,n,r){return t[0]=n[0]+r[0],t[1]=n[1]+r[1],t[2]=n[2]+r[2],t[3]=n[3]+r[3],t},n.subtract=u,n.exactEquals=function(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]&&t[3]===n[3]},n.equals=function(t,n){var r=t[0],e=t[1],u=t[2],o=t[3],i=n[0],s=n[1],c=n[2],f=n[3];return Math.abs(r-i)<=a.EPSILON*Math.max(1,Math.abs(r),Math.abs(i))&&Math.abs(e-s)<=a.EPSILON*Math.max(1,Math.abs(e),Math.abs(s))&&Math.abs(u-c)<=a.EPSILON*Math.max(1,Math.abs(u),Math.abs(c))&&Math.abs(o-f)<=a.EPSILON*Math.max(1,Math.abs(o),Math.abs(f))},n.multiplyScalar=function(t,n,r){return t[0]=n[0]*r,t[1]=n[1]*r,t[2]=n[2]*r,t[3]=n[3]*r,t},n.multiplyScalarAndAdd=function(t,n,r,a){return t[0]=n[0]+r[0]*a,t[1]=n[1]+r[1]*a,t[2]=n[2]+r[2]*a,t[3]=n[3]+r[3]*a,t};var a=function(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}(r(0));function e(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=r[0],s=r[1],c=r[2],f=r[3];return t[0]=a*i+u*s,t[1]=e*i+o*s,t[2]=a*c+u*f,t[3]=e*c+o*f,t}function u(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t[2]=n[2]-r[2],t[3]=n[3]-r[3],t}n.mul=e,n.sub=u},function(t,n,r){\"use strict\";Object.defineProperty(n,\"__esModule\",{value:!0}),n.vec4=n.vec3=n.vec2=n.quat2=n.quat=n.mat4=n.mat3=n.mat2d=n.mat2=n.glMatrix=void 0;var a=l(r(0)),e=l(r(9)),u=l(r(8)),o=l(r(5)),i=l(r(4)),s=l(r(3)),c=l(r(7)),f=l(r(6)),M=l(r(2)),h=l(r(1));function l(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}n.glMatrix=a,n.mat2=e,n.mat2d=u,n.mat3=o,n.mat4=i,n.quat=s,n.quat2=c,n.vec2=f,n.vec3=M,n.vec4=h}])});"
  },
  {
    "path": "examples/index.html",
    "content": "<!doctype html>\n<html>\n    <head>\n        <title>This is the title of the webpage!</title>\n        <style>\n        html, body {\n            margin: 0px;\n            padding: 0px;\n            background-color: #212733;\n        }\n        </style>\n    </head>\n    <body>\n        <script src=\"gl-matrix-min.js\"></script>\n        <script>\n            // Compiled Examples.bc.js does a check for require,\n            // but it's not needed during runtime.\n            window.require = false;\n        </script>\n        <script>\n            // If we're hosted in an iframe, we'll listen to changes and 'postMessage' them out\n            if (window.parent) {\n                window[\"__revery_playground_example_notify_changed\"] = (s) => {\n                    window.parent.postMessage({ type: \"example.switch\", payload: s}, \"*\");\n                }\n            }\n        </script>\n        <script src=\"Examples.bc.js\"></script>\n    </body>\n</html>\n"
  },
  {
    "path": "examples/stubs/ExampleStubs.re",
    "content": "/* Notify external environments of switching tabs */\nexternal notifyExampleSwitched: string => unit =\n  \"revery_example_notify_changed\";\n"
  },
  {
    "path": "examples/stubs/dune",
    "content": "(library\n (name ExampleStubs)\n (foreign_stubs\n  (language c)\n  (names example_stubs)\n  (flags :standard -Wall -Wextra -Werror))\n (js_of_ocaml\n  (javascript_files example_stubs.js)))\n"
  },
  {
    "path": "examples/stubs/example_stubs.c",
    "content": "#include <stdio.h>\n\n#include <caml/alloc.h>\n#include <caml/callback.h>\n#include <caml/memory.h>\n#include <caml/mlvalues.h>\n\nCAMLprim value revery_example_notify_changed(value vExample) {\n    CAMLparam1(vExample);\n    const char *szExampleSource = String_val(vExample);\n\n    printf(\"Switched to example: %s\\n\", szExampleSource);\n    CAMLreturn(Val_unit);\n}\n"
  },
  {
    "path": "examples/stubs/example_stubs.js",
    "content": "// Provides: revery_example_notify_changed\n// Requires: caml_to_js_string\nfunction revery_example_notify_changed(src) {\n    var window = joo_global_object.window;\n    if (window && window[\"__revery_playground_example_notify_changed\"]) {\n        window[\"__revery_playground_example_notify_changed\"](caml_to_js_string(src));\n    }\n}\n"
  },
  {
    "path": "examples.json",
    "content": "{\n  \"source\": \"./package.json\",\n  \"scripts\": {\n      \"run\": \"esy @examples x Examples\",\n      \"run-harfbuzz\": \"esy @examples x bash -c run-harfbuzz.sh #{os}\",\n      \"run-skia-fontmanager\": \"esy @examples x bash -c run-skia-fontmanager.sh #{os}\"\n  },\n  \"override\": {\n      \"build\": [\"dune build -p reason-harfbuzz,reason-skia,reason-sdl2,Revery,ReveryExamples -j4\"],\n      \"install\": [\n          \"esy-installer reason-harfbuzz.install\",\n          \"esy-installer reason-skia.install\",\n          \"esy-installer Revery.install\",\n          \"esy-installer ReveryExamples.install\"\n      ]\n  }\n}\n"
  },
  {
    "path": "include/COPYING.txt",
    "content": "Copyright (c) 2002-2006 Marcus Geelnard\nCopyright (c) 2006-2016 Camilla Berglund <elmindreda@glfw.org>\n\nThis software is provided 'as-is', without any express or implied\nwarranty. In no event will the authors be held liable for any damages\narising from the use of this software.\n\nPermission is granted to anyone to use this software for any purpose,\nincluding commercial applications, and to alter it and redistribute it\nfreely, subject to the following restrictions:\n\n1. The origin of this software must not be misrepresented; you must not\n   claim that you wrote the original software. If you use this software\n   in a product, an acknowledgment in the product documentation would\n   be appreciated but is not required.\n\n2. Altered source versions must be plainly marked as such, and must not\n   be misrepresented as being the original software.\n\n3. This notice may not be removed or altered from any source\n   distribution.\n\n"
  },
  {
    "path": "include/KHR/khrplatform.h",
    "content": "#ifndef __khrplatform_h_\n#define __khrplatform_h_\n\n/*\n** Copyright (c) 2008-2018 The Khronos Group Inc.\n**\n** Permission is hereby granted, free of charge, to any person obtaining a\n** copy of this software and/or associated documentation files (the\n** \"Materials\"), to deal in the Materials without restriction, including\n** without limitation the rights to use, copy, modify, merge, publish,\n** distribute, sublicense, and/or sell copies of the Materials, and to\n** permit persons to whom the Materials are furnished to do so, subject to\n** the following conditions:\n**\n** The above copyright notice and this permission notice shall be included\n** in all copies or substantial portions of the Materials.\n**\n** THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.\n*/\n\n/* Khronos platform-specific types and definitions.\n *\n * The master copy of khrplatform.h is maintained in the Khronos EGL\n * Registry repository at https://github.com/KhronosGroup/EGL-Registry\n * The last semantic modification to khrplatform.h was at commit ID:\n *      67a3e0864c2d75ea5287b9f3d2eb74a745936692\n *\n * Adopters may modify this file to suit their platform. Adopters are\n * encouraged to submit platform specific modifications to the Khronos\n * group so that they can be included in future versions of this file.\n * Please submit changes by filing pull requests or issues on\n * the EGL Registry repository linked above.\n *\n *\n * See the Implementer's Guidelines for information about where this file\n * should be located on your system and for more details of its use:\n *    http://www.khronos.org/registry/implementers_guide.pdf\n *\n * This file should be included as\n *        #include <KHR/khrplatform.h>\n * by Khronos client API header files that use its types and defines.\n *\n * The types in khrplatform.h should only be used to define API-specific types.\n *\n * Types defined in khrplatform.h:\n *    khronos_int8_t              signed   8  bit\n *    khronos_uint8_t             unsigned 8  bit\n *    khronos_int16_t             signed   16 bit\n *    khronos_uint16_t            unsigned 16 bit\n *    khronos_int32_t             signed   32 bit\n *    khronos_uint32_t            unsigned 32 bit\n *    khronos_int64_t             signed   64 bit\n *    khronos_uint64_t            unsigned 64 bit\n *    khronos_intptr_t            signed   same number of bits as a pointer\n *    khronos_uintptr_t           unsigned same number of bits as a pointer\n *    khronos_ssize_t             signed   size\n *    khronos_usize_t             unsigned size\n *    khronos_float_t             signed   32 bit floating point\n *    khronos_time_ns_t           unsigned 64 bit time in nanoseconds\n *    khronos_utime_nanoseconds_t unsigned time interval or absolute time in\n *                                         nanoseconds\n *    khronos_stime_nanoseconds_t signed time interval in nanoseconds\n *    khronos_boolean_enum_t      enumerated boolean type. This should\n *      only be used as a base type when a client API's boolean type is\n *      an enum. Client APIs which use an integer or other type for\n *      booleans cannot use this as the base type for their boolean.\n *\n * Tokens defined in khrplatform.h:\n *\n *    KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.\n *\n *    KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.\n *    KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.\n *\n * Calling convention macros defined in this file:\n *    KHRONOS_APICALL\n *    KHRONOS_APIENTRY\n *    KHRONOS_APIATTRIBUTES\n *\n * These may be used in function prototypes as:\n *\n *      KHRONOS_APICALL void KHRONOS_APIENTRY funcname(\n *                                  int arg1,\n *                                  int arg2) KHRONOS_APIATTRIBUTES;\n */\n\n/*-------------------------------------------------------------------------\n * Definition of KHRONOS_APICALL\n *-------------------------------------------------------------------------\n * This precedes the return type of the function in the function prototype.\n */\n#if defined(_WIN32) && !defined(__SCITECH_SNAP__)\n#   define KHRONOS_APICALL __declspec(dllimport)\n#elif defined (__SYMBIAN32__)\n#   define KHRONOS_APICALL IMPORT_C\n#elif defined(__ANDROID__)\n#   define KHRONOS_APICALL __attribute__((visibility(\"default\")))\n#else\n#   define KHRONOS_APICALL\n#endif\n\n/*-------------------------------------------------------------------------\n * Definition of KHRONOS_APIENTRY\n *-------------------------------------------------------------------------\n * This follows the return type of the function  and precedes the function\n * name in the function prototype.\n */\n#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)\n/* Win32 but not WinCE */\n#   define KHRONOS_APIENTRY __stdcall\n#else\n#   define KHRONOS_APIENTRY\n#endif\n\n/*-------------------------------------------------------------------------\n * Definition of KHRONOS_APIATTRIBUTES\n *-------------------------------------------------------------------------\n * This follows the closing parenthesis of the function prototype arguments.\n */\n#if defined (__ARMCC_2__)\n#define KHRONOS_APIATTRIBUTES __softfp\n#else\n#define KHRONOS_APIATTRIBUTES\n#endif\n\n/*-------------------------------------------------------------------------\n * basic type definitions\n *-----------------------------------------------------------------------*/\n#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)\n\n\n/*\n * Using <stdint.h>\n */\n#include <stdint.h>\ntypedef int32_t                 khronos_int32_t;\ntypedef uint32_t                khronos_uint32_t;\ntypedef int64_t                 khronos_int64_t;\ntypedef uint64_t                khronos_uint64_t;\n#define KHRONOS_SUPPORT_INT64   1\n#define KHRONOS_SUPPORT_FLOAT   1\n\n#elif defined(__VMS ) || defined(__sgi)\n\n/*\n * Using <inttypes.h>\n */\n#include <inttypes.h>\ntypedef int32_t                 khronos_int32_t;\ntypedef uint32_t                khronos_uint32_t;\ntypedef int64_t                 khronos_int64_t;\ntypedef uint64_t                khronos_uint64_t;\n#define KHRONOS_SUPPORT_INT64   1\n#define KHRONOS_SUPPORT_FLOAT   1\n\n#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)\n\n/*\n * Win32\n */\ntypedef __int32                 khronos_int32_t;\ntypedef unsigned __int32        khronos_uint32_t;\ntypedef __int64                 khronos_int64_t;\ntypedef unsigned __int64        khronos_uint64_t;\n#define KHRONOS_SUPPORT_INT64   1\n#define KHRONOS_SUPPORT_FLOAT   1\n\n#elif defined(__sun__) || defined(__digital__)\n\n/*\n * Sun or Digital\n */\ntypedef int                     khronos_int32_t;\ntypedef unsigned int            khronos_uint32_t;\n#if defined(__arch64__) || defined(_LP64)\ntypedef long int                khronos_int64_t;\ntypedef unsigned long int       khronos_uint64_t;\n#else\ntypedef long long int           khronos_int64_t;\ntypedef unsigned long long int  khronos_uint64_t;\n#endif /* __arch64__ */\n#define KHRONOS_SUPPORT_INT64   1\n#define KHRONOS_SUPPORT_FLOAT   1\n\n#elif 0\n\n/*\n * Hypothetical platform with no float or int64 support\n */\ntypedef int                     khronos_int32_t;\ntypedef unsigned int            khronos_uint32_t;\n#define KHRONOS_SUPPORT_INT64   0\n#define KHRONOS_SUPPORT_FLOAT   0\n\n#else\n\n/*\n * Generic fallback\n */\n#include <stdint.h>\ntypedef int32_t                 khronos_int32_t;\ntypedef uint32_t                khronos_uint32_t;\ntypedef int64_t                 khronos_int64_t;\ntypedef uint64_t                khronos_uint64_t;\n#define KHRONOS_SUPPORT_INT64   1\n#define KHRONOS_SUPPORT_FLOAT   1\n\n#endif\n\n\n/*\n * Types that are (so far) the same on all platforms\n */\ntypedef signed   char          khronos_int8_t;\ntypedef unsigned char          khronos_uint8_t;\ntypedef signed   short int     khronos_int16_t;\ntypedef unsigned short int     khronos_uint16_t;\n\n/*\n * Types that differ between LLP64 and LP64 architectures - in LLP64,\n * pointers are 64 bits, but 'long' is still 32 bits. Win64 appears\n * to be the only LLP64 architecture in current use.\n */\n#ifdef _WIN64\ntypedef signed   long long int khronos_intptr_t;\ntypedef unsigned long long int khronos_uintptr_t;\ntypedef signed   long long int khronos_ssize_t;\ntypedef unsigned long long int khronos_usize_t;\n#else\ntypedef signed   long  int     khronos_intptr_t;\ntypedef unsigned long  int     khronos_uintptr_t;\ntypedef signed   long  int     khronos_ssize_t;\ntypedef unsigned long  int     khronos_usize_t;\n#endif\n\n#if KHRONOS_SUPPORT_FLOAT\n/*\n * Float type\n */\ntypedef          float         khronos_float_t;\n#endif\n\n#if KHRONOS_SUPPORT_INT64\n/* Time types\n *\n * These types can be used to represent a time interval in nanoseconds or\n * an absolute Unadjusted System Time.  Unadjusted System Time is the number\n * of nanoseconds since some arbitrary system event (e.g. since the last\n * time the system booted).  The Unadjusted System Time is an unsigned\n * 64 bit value that wraps back to 0 every 584 years.  Time intervals\n * may be either signed or unsigned.\n */\ntypedef khronos_uint64_t       khronos_utime_nanoseconds_t;\ntypedef khronos_int64_t        khronos_stime_nanoseconds_t;\n#endif\n\n/*\n * Dummy value used to pad enum types to 32 bits.\n */\n#ifndef KHRONOS_MAX_ENUM\n#define KHRONOS_MAX_ENUM 0x7FFFFFFF\n#endif\n\n/*\n * Enumerated boolean type\n *\n * Values other than zero should be considered to be true.  Therefore\n * comparisons should not be made against KHRONOS_TRUE.\n */\ntypedef enum {\n    KHRONOS_FALSE = 0,\n    KHRONOS_TRUE  = 1,\n    KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM\n} khronos_boolean_enum_t;\n\n#endif /* __khrplatform_h_ */\n"
  },
  {
    "path": "include/glad/glad.h",
    "content": "/*\n\n    OpenGL ES loader generated by glad 0.1.27 on Tue Sep 11 02:18:13 2018.\n\n    Language/Generator: C/C++\n    Specification: gl\n    APIs: gles2=2.0\n    Profile: core\n    Extensions:\n\n    Loader: True\n    Local files: False\n    Omit khrplatform: False\n    Reproducible: False\n\n    Commandline:\n        --profile=\"core\" --api=\"gles2=2.0\" --generator=\"c\" --spec=\"gl\" --extensions=\"\"\n    Online:\n        http://glad.dav1d.de/#profile=core&language=c&specification=gl&loader=on&api=gles2%3D2.0\n*/\n\n\n#ifndef __glad_h_\n#define __glad_h_\n\n#ifdef __gl2_h_\n#error OpenGL ES 2 header already included, remove this include, glad already provides it\n#endif\n#define __gl2_h_\n\n#ifdef __gl3_h_\n#error OpenGL ES 3 header already included, remove this include, glad already provides it\n#endif\n#define __gl3_h_\n\n#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__)\n#ifndef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN 1\n#endif\n#ifndef NOMINMAX\n#define NOMINMAX 1\n#endif\n#include <windows.h>\n#endif\n\n#ifndef APIENTRY\n#define APIENTRY\n#endif\n#ifndef APIENTRYP\n#define APIENTRYP APIENTRY *\n#endif\n\n#ifndef GLAPIENTRY\n#define GLAPIENTRY APIENTRY\n#endif\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstruct gladGLversionStruct {\n    int major;\n    int minor;\n};\n\ntypedef void* (* GLADloadproc)(const char *name);\n\n#ifndef GLAPI\n# if defined(GLAD_GLAPI_EXPORT)\n#  if defined(_WIN32) || defined(__CYGWIN__)\n#   if defined(GLAD_GLAPI_EXPORT_BUILD)\n#    if defined(__GNUC__)\n#     define GLAPI __attribute__ ((dllexport)) extern\n#    else\n#     define GLAPI __declspec(dllexport) extern\n#    endif\n#   else\n#    if defined(__GNUC__)\n#     define GLAPI __attribute__ ((dllimport)) extern\n#    else\n#     define GLAPI __declspec(dllimport) extern\n#    endif\n#   endif\n#  elif defined(__GNUC__) && defined(GLAD_GLAPI_EXPORT_BUILD)\n#   define GLAPI __attribute__ ((visibility (\"default\"))) extern\n#  else\n#   define GLAPI extern\n#  endif\n# else\n#  define GLAPI extern\n# endif\n#endif\n\nGLAPI struct gladGLversionStruct GLVersion;\nGLAPI int gladLoadGLES2Loader(GLADloadproc);\n\n#include <stddef.h>\n#include <KHR/khrplatform.h>\n#ifndef GLEXT_64_TYPES_DEFINED\n/* This code block is duplicated in glxext.h, so must be protected */\n#define GLEXT_64_TYPES_DEFINED\n/* Define int32_t, int64_t, and uint64_t types for UST/MSC */\n/* (as used in the GL_EXT_timer_query extension). */\n#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L\n#include <inttypes.h>\n#elif defined(__sun__) || defined(__digital__)\n#include <inttypes.h>\n#if defined(__STDC__)\n#if defined(__arch64__) || defined(_LP64)\ntypedef long int int64_t;\ntypedef unsigned long int uint64_t;\n#else\ntypedef long long int int64_t;\ntypedef unsigned long long int uint64_t;\n#endif /* __arch64__ */\n#endif /* __STDC__ */\n#elif defined( __VMS ) || defined(__sgi)\n#include <inttypes.h>\n#elif defined(__SCO__) || defined(__USLC__)\n#include <stdint.h>\n#elif defined(__UNIXOS2__) || defined(__SOL64__)\ntypedef long int int32_t;\ntypedef long long int int64_t;\ntypedef unsigned long long int uint64_t;\n#elif defined(_WIN32) && defined(__GNUC__)\n#include <stdint.h>\n#elif defined(_WIN32)\ntypedef __int32 int32_t;\ntypedef __int64 int64_t;\ntypedef unsigned __int64 uint64_t;\n#else\n/* Fallback if nothing above works */\n#include <inttypes.h>\n#endif\n#endif\ntypedef unsigned int GLenum;\ntypedef unsigned char GLboolean;\ntypedef unsigned int GLbitfield;\ntypedef void GLvoid;\ntypedef signed char GLbyte;\ntypedef short GLshort;\ntypedef int GLint;\ntypedef int GLclampx;\ntypedef unsigned char GLubyte;\ntypedef unsigned short GLushort;\ntypedef unsigned int GLuint;\ntypedef int GLsizei;\ntypedef float GLfloat;\ntypedef float GLclampf;\ntypedef double GLdouble;\ntypedef double GLclampd;\ntypedef void *GLeglClientBufferEXT;\ntypedef void *GLeglImageOES;\ntypedef char GLchar;\ntypedef char GLcharARB;\n#ifdef __APPLE__\ntypedef void *GLhandleARB;\n#else\ntypedef unsigned int GLhandleARB;\n#endif\ntypedef unsigned short GLhalfARB;\ntypedef unsigned short GLhalf;\ntypedef GLint GLfixed;\ntypedef khronos_intptr_t GLintptr;\ntypedef khronos_ssize_t GLsizeiptr;\ntypedef int64_t GLint64;\ntypedef uint64_t GLuint64;\n#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060)\ntypedef long GLintptrARB;\n#else\ntypedef ptrdiff_t GLintptrARB;\n#endif\n#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060)\ntypedef long GLsizeiptrARB;\n#else\ntypedef ptrdiff_t GLsizeiptrARB;\n#endif\ntypedef int64_t GLint64EXT;\ntypedef uint64_t GLuint64EXT;\ntypedef struct __GLsync *GLsync;\nstruct _cl_context;\nstruct _cl_event;\ntypedef void (APIENTRY *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam);\ntypedef void (APIENTRY *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam);\ntypedef void (APIENTRY *GLDEBUGPROCKHR)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam);\ntypedef void (APIENTRY *GLDEBUGPROCAMD)(GLuint id,GLenum category,GLenum severity,GLsizei length,const GLchar *message,void *userParam);\ntypedef unsigned short GLhalfNV;\ntypedef GLintptr GLvdpauSurfaceNV;\ntypedef void (APIENTRY *GLVULKANPROCNV)(void);\n#define GL_DEPTH_BUFFER_BIT 0x00000100\n#define GL_STENCIL_BUFFER_BIT 0x00000400\n#define GL_COLOR_BUFFER_BIT 0x00004000\n#define GL_FALSE 0\n#define GL_TRUE 1\n#define GL_POINTS 0x0000\n#define GL_LINES 0x0001\n#define GL_LINE_LOOP 0x0002\n#define GL_LINE_STRIP 0x0003\n#define GL_TRIANGLES 0x0004\n#define GL_TRIANGLE_STRIP 0x0005\n#define GL_TRIANGLE_FAN 0x0006\n#define GL_ZERO 0\n#define GL_ONE 1\n#define GL_SRC_COLOR 0x0300\n#define GL_ONE_MINUS_SRC_COLOR 0x0301\n#define GL_SRC_ALPHA 0x0302\n#define GL_ONE_MINUS_SRC_ALPHA 0x0303\n#define GL_DST_ALPHA 0x0304\n#define GL_ONE_MINUS_DST_ALPHA 0x0305\n#define GL_DST_COLOR 0x0306\n#define GL_ONE_MINUS_DST_COLOR 0x0307\n#define GL_SRC_ALPHA_SATURATE 0x0308\n#define GL_FUNC_ADD 0x8006\n#define GL_BLEND_EQUATION 0x8009\n#define GL_BLEND_EQUATION_RGB 0x8009\n#define GL_BLEND_EQUATION_ALPHA 0x883D\n#define GL_FUNC_SUBTRACT 0x800A\n#define GL_FUNC_REVERSE_SUBTRACT 0x800B\n#define GL_BLEND_DST_RGB 0x80C8\n#define GL_BLEND_SRC_RGB 0x80C9\n#define GL_BLEND_DST_ALPHA 0x80CA\n#define GL_BLEND_SRC_ALPHA 0x80CB\n#define GL_CONSTANT_COLOR 0x8001\n#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002\n#define GL_CONSTANT_ALPHA 0x8003\n#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004\n#define GL_BLEND_COLOR 0x8005\n#define GL_ARRAY_BUFFER 0x8892\n#define GL_ELEMENT_ARRAY_BUFFER 0x8893\n#define GL_ARRAY_BUFFER_BINDING 0x8894\n#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895\n#define GL_STREAM_DRAW 0x88E0\n#define GL_STATIC_DRAW 0x88E4\n#define GL_DYNAMIC_DRAW 0x88E8\n#define GL_BUFFER_SIZE 0x8764\n#define GL_BUFFER_USAGE 0x8765\n#define GL_CURRENT_VERTEX_ATTRIB 0x8626\n#define GL_FRONT 0x0404\n#define GL_BACK 0x0405\n#define GL_FRONT_AND_BACK 0x0408\n#define GL_TEXTURE_2D 0x0DE1\n#define GL_CULL_FACE 0x0B44\n#define GL_BLEND 0x0BE2\n#define GL_DITHER 0x0BD0\n#define GL_STENCIL_TEST 0x0B90\n#define GL_DEPTH_TEST 0x0B71\n#define GL_SCISSOR_TEST 0x0C11\n#define GL_POLYGON_OFFSET_FILL 0x8037\n#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E\n#define GL_SAMPLE_COVERAGE 0x80A0\n#define GL_NO_ERROR 0\n#define GL_INVALID_ENUM 0x0500\n#define GL_INVALID_VALUE 0x0501\n#define GL_INVALID_OPERATION 0x0502\n#define GL_OUT_OF_MEMORY 0x0505\n#define GL_CW 0x0900\n#define GL_CCW 0x0901\n#define GL_LINE_WIDTH 0x0B21\n#define GL_ALIASED_POINT_SIZE_RANGE 0x846D\n#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E\n#define GL_CULL_FACE_MODE 0x0B45\n#define GL_FRONT_FACE 0x0B46\n#define GL_DEPTH_RANGE 0x0B70\n#define GL_DEPTH_WRITEMASK 0x0B72\n#define GL_DEPTH_CLEAR_VALUE 0x0B73\n#define GL_DEPTH_FUNC 0x0B74\n#define GL_STENCIL_CLEAR_VALUE 0x0B91\n#define GL_STENCIL_FUNC 0x0B92\n#define GL_STENCIL_FAIL 0x0B94\n#define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95\n#define GL_STENCIL_PASS_DEPTH_PASS 0x0B96\n#define GL_STENCIL_REF 0x0B97\n#define GL_STENCIL_VALUE_MASK 0x0B93\n#define GL_STENCIL_WRITEMASK 0x0B98\n#define GL_STENCIL_BACK_FUNC 0x8800\n#define GL_STENCIL_BACK_FAIL 0x8801\n#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802\n#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803\n#define GL_STENCIL_BACK_REF 0x8CA3\n#define GL_STENCIL_BACK_VALUE_MASK 0x8CA4\n#define GL_STENCIL_BACK_WRITEMASK 0x8CA5\n#define GL_VIEWPORT 0x0BA2\n#define GL_SCISSOR_BOX 0x0C10\n#define GL_COLOR_CLEAR_VALUE 0x0C22\n#define GL_COLOR_WRITEMASK 0x0C23\n#define GL_UNPACK_ALIGNMENT 0x0CF5\n#define GL_PACK_ALIGNMENT 0x0D05\n#define GL_MAX_TEXTURE_SIZE 0x0D33\n#define GL_MAX_VIEWPORT_DIMS 0x0D3A\n#define GL_SUBPIXEL_BITS 0x0D50\n#define GL_RED_BITS 0x0D52\n#define GL_GREEN_BITS 0x0D53\n#define GL_BLUE_BITS 0x0D54\n#define GL_ALPHA_BITS 0x0D55\n#define GL_DEPTH_BITS 0x0D56\n#define GL_STENCIL_BITS 0x0D57\n#define GL_POLYGON_OFFSET_UNITS 0x2A00\n#define GL_POLYGON_OFFSET_FACTOR 0x8038\n#define GL_TEXTURE_BINDING_2D 0x8069\n#define GL_SAMPLE_BUFFERS 0x80A8\n#define GL_SAMPLES 0x80A9\n#define GL_SAMPLE_COVERAGE_VALUE 0x80AA\n#define GL_SAMPLE_COVERAGE_INVERT 0x80AB\n#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2\n#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3\n#define GL_DONT_CARE 0x1100\n#define GL_FASTEST 0x1101\n#define GL_NICEST 0x1102\n#define GL_GENERATE_MIPMAP_HINT 0x8192\n#define GL_BYTE 0x1400\n#define GL_UNSIGNED_BYTE 0x1401\n#define GL_SHORT 0x1402\n#define GL_UNSIGNED_SHORT 0x1403\n#define GL_INT 0x1404\n#define GL_UNSIGNED_INT 0x1405\n#define GL_FLOAT 0x1406\n#define GL_FIXED 0x140C\n#define GL_DEPTH_COMPONENT 0x1902\n#define GL_ALPHA 0x1906\n#define GL_RGB 0x1907\n#define GL_RGBA 0x1908\n#define GL_LUMINANCE 0x1909\n#define GL_LUMINANCE_ALPHA 0x190A\n#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033\n#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034\n#define GL_UNSIGNED_SHORT_5_6_5 0x8363\n#define GL_FRAGMENT_SHADER 0x8B30\n#define GL_VERTEX_SHADER 0x8B31\n#define GL_MAX_VERTEX_ATTRIBS 0x8869\n#define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB\n#define GL_MAX_VARYING_VECTORS 0x8DFC\n#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D\n#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C\n#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872\n#define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD\n#define GL_SHADER_TYPE 0x8B4F\n#define GL_DELETE_STATUS 0x8B80\n#define GL_LINK_STATUS 0x8B82\n#define GL_VALIDATE_STATUS 0x8B83\n#define GL_ATTACHED_SHADERS 0x8B85\n#define GL_ACTIVE_UNIFORMS 0x8B86\n#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87\n#define GL_ACTIVE_ATTRIBUTES 0x8B89\n#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A\n#define GL_SHADING_LANGUAGE_VERSION 0x8B8C\n#define GL_CURRENT_PROGRAM 0x8B8D\n#define GL_NEVER 0x0200\n#define GL_LESS 0x0201\n#define GL_EQUAL 0x0202\n#define GL_LEQUAL 0x0203\n#define GL_GREATER 0x0204\n#define GL_NOTEQUAL 0x0205\n#define GL_GEQUAL 0x0206\n#define GL_ALWAYS 0x0207\n#define GL_KEEP 0x1E00\n#define GL_REPLACE 0x1E01\n#define GL_INCR 0x1E02\n#define GL_DECR 0x1E03\n#define GL_INVERT 0x150A\n#define GL_INCR_WRAP 0x8507\n#define GL_DECR_WRAP 0x8508\n#define GL_VENDOR 0x1F00\n#define GL_RENDERER 0x1F01\n#define GL_VERSION 0x1F02\n#define GL_EXTENSIONS 0x1F03\n#define GL_NEAREST 0x2600\n#define GL_LINEAR 0x2601\n#define GL_NEAREST_MIPMAP_NEAREST 0x2700\n#define GL_LINEAR_MIPMAP_NEAREST 0x2701\n#define GL_NEAREST_MIPMAP_LINEAR 0x2702\n#define GL_LINEAR_MIPMAP_LINEAR 0x2703\n#define GL_TEXTURE_MAG_FILTER 0x2800\n#define GL_TEXTURE_MIN_FILTER 0x2801\n#define GL_TEXTURE_WRAP_S 0x2802\n#define GL_TEXTURE_WRAP_T 0x2803\n#define GL_TEXTURE 0x1702\n#define GL_TEXTURE_CUBE_MAP 0x8513\n#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514\n#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515\n#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516\n#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517\n#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518\n#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519\n#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A\n#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C\n#define GL_TEXTURE0 0x84C0\n#define GL_TEXTURE1 0x84C1\n#define GL_TEXTURE2 0x84C2\n#define GL_TEXTURE3 0x84C3\n#define GL_TEXTURE4 0x84C4\n#define GL_TEXTURE5 0x84C5\n#define GL_TEXTURE6 0x84C6\n#define GL_TEXTURE7 0x84C7\n#define GL_TEXTURE8 0x84C8\n#define GL_TEXTURE9 0x84C9\n#define GL_TEXTURE10 0x84CA\n#define GL_TEXTURE11 0x84CB\n#define GL_TEXTURE12 0x84CC\n#define GL_TEXTURE13 0x84CD\n#define GL_TEXTURE14 0x84CE\n#define GL_TEXTURE15 0x84CF\n#define GL_TEXTURE16 0x84D0\n#define GL_TEXTURE17 0x84D1\n#define GL_TEXTURE18 0x84D2\n#define GL_TEXTURE19 0x84D3\n#define GL_TEXTURE20 0x84D4\n#define GL_TEXTURE21 0x84D5\n#define GL_TEXTURE22 0x84D6\n#define GL_TEXTURE23 0x84D7\n#define GL_TEXTURE24 0x84D8\n#define GL_TEXTURE25 0x84D9\n#define GL_TEXTURE26 0x84DA\n#define GL_TEXTURE27 0x84DB\n#define GL_TEXTURE28 0x84DC\n#define GL_TEXTURE29 0x84DD\n#define GL_TEXTURE30 0x84DE\n#define GL_TEXTURE31 0x84DF\n#define GL_ACTIVE_TEXTURE 0x84E0\n#define GL_REPEAT 0x2901\n#define GL_CLAMP_TO_EDGE 0x812F\n#define GL_MIRRORED_REPEAT 0x8370\n#define GL_FLOAT_VEC2 0x8B50\n#define GL_FLOAT_VEC3 0x8B51\n#define GL_FLOAT_VEC4 0x8B52\n#define GL_INT_VEC2 0x8B53\n#define GL_INT_VEC3 0x8B54\n#define GL_INT_VEC4 0x8B55\n#define GL_BOOL 0x8B56\n#define GL_BOOL_VEC2 0x8B57\n#define GL_BOOL_VEC3 0x8B58\n#define GL_BOOL_VEC4 0x8B59\n#define GL_FLOAT_MAT2 0x8B5A\n#define GL_FLOAT_MAT3 0x8B5B\n#define GL_FLOAT_MAT4 0x8B5C\n#define GL_SAMPLER_2D 0x8B5E\n#define GL_SAMPLER_CUBE 0x8B60\n#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622\n#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623\n#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624\n#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625\n#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A\n#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645\n#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F\n#define GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A\n#define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B\n#define GL_COMPILE_STATUS 0x8B81\n#define GL_INFO_LOG_LENGTH 0x8B84\n#define GL_SHADER_SOURCE_LENGTH 0x8B88\n#define GL_SHADER_COMPILER 0x8DFA\n#define GL_SHADER_BINARY_FORMATS 0x8DF8\n#define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9\n#define GL_LOW_FLOAT 0x8DF0\n#define GL_MEDIUM_FLOAT 0x8DF1\n#define GL_HIGH_FLOAT 0x8DF2\n#define GL_LOW_INT 0x8DF3\n#define GL_MEDIUM_INT 0x8DF4\n#define GL_HIGH_INT 0x8DF5\n#define GL_FRAMEBUFFER 0x8D40\n#define GL_RENDERBUFFER 0x8D41\n#define GL_RGBA4 0x8056\n#define GL_RGB5_A1 0x8057\n#define GL_RGB565 0x8D62\n#define GL_DEPTH_COMPONENT16 0x81A5\n#define GL_STENCIL_INDEX8 0x8D48\n#define GL_RENDERBUFFER_WIDTH 0x8D42\n#define GL_RENDERBUFFER_HEIGHT 0x8D43\n#define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44\n#define GL_RENDERBUFFER_RED_SIZE 0x8D50\n#define GL_RENDERBUFFER_GREEN_SIZE 0x8D51\n#define GL_RENDERBUFFER_BLUE_SIZE 0x8D52\n#define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53\n#define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54\n#define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55\n#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0\n#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1\n#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2\n#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3\n#define GL_COLOR_ATTACHMENT0 0x8CE0\n#define GL_DEPTH_ATTACHMENT 0x8D00\n#define GL_STENCIL_ATTACHMENT 0x8D20\n#define GL_NONE 0\n#define GL_FRAMEBUFFER_COMPLETE 0x8CD5\n#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6\n#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7\n#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS 0x8CD9\n#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD\n#define GL_FRAMEBUFFER_BINDING 0x8CA6\n#define GL_RENDERBUFFER_BINDING 0x8CA7\n#define GL_MAX_RENDERBUFFER_SIZE 0x84E8\n#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506\n#ifndef GL_ES_VERSION_2_0\n#define GL_ES_VERSION_2_0 1\nGLAPI int GLAD_GL_ES_VERSION_2_0;\ntypedef void (APIENTRYP PFNGLACTIVETEXTUREPROC)(GLenum texture);\nGLAPI PFNGLACTIVETEXTUREPROC glad_glActiveTexture;\n#define glActiveTexture glad_glActiveTexture\ntypedef void (APIENTRYP PFNGLATTACHSHADERPROC)(GLuint program, GLuint shader);\nGLAPI PFNGLATTACHSHADERPROC glad_glAttachShader;\n#define glAttachShader glad_glAttachShader\ntypedef void (APIENTRYP PFNGLBINDATTRIBLOCATIONPROC)(GLuint program, GLuint index, const GLchar *name);\nGLAPI PFNGLBINDATTRIBLOCATIONPROC glad_glBindAttribLocation;\n#define glBindAttribLocation glad_glBindAttribLocation\ntypedef void (APIENTRYP PFNGLBINDBUFFERPROC)(GLenum target, GLuint buffer);\nGLAPI PFNGLBINDBUFFERPROC glad_glBindBuffer;\n#define glBindBuffer glad_glBindBuffer\ntypedef void (APIENTRYP PFNGLBINDFRAMEBUFFERPROC)(GLenum target, GLuint framebuffer);\nGLAPI PFNGLBINDFRAMEBUFFERPROC glad_glBindFramebuffer;\n#define glBindFramebuffer glad_glBindFramebuffer\ntypedef void (APIENTRYP PFNGLBINDRENDERBUFFERPROC)(GLenum target, GLuint renderbuffer);\nGLAPI PFNGLBINDRENDERBUFFERPROC glad_glBindRenderbuffer;\n#define glBindRenderbuffer glad_glBindRenderbuffer\ntypedef void (APIENTRYP PFNGLBINDTEXTUREPROC)(GLenum target, GLuint texture);\nGLAPI PFNGLBINDTEXTUREPROC glad_glBindTexture;\n#define glBindTexture glad_glBindTexture\ntypedef void (APIENTRYP PFNGLBLENDCOLORPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);\nGLAPI PFNGLBLENDCOLORPROC glad_glBlendColor;\n#define glBlendColor glad_glBlendColor\ntypedef void (APIENTRYP PFNGLBLENDEQUATIONPROC)(GLenum mode);\nGLAPI PFNGLBLENDEQUATIONPROC glad_glBlendEquation;\n#define glBlendEquation glad_glBlendEquation\ntypedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEPROC)(GLenum modeRGB, GLenum modeAlpha);\nGLAPI PFNGLBLENDEQUATIONSEPARATEPROC glad_glBlendEquationSeparate;\n#define glBlendEquationSeparate glad_glBlendEquationSeparate\ntypedef void (APIENTRYP PFNGLBLENDFUNCPROC)(GLenum sfactor, GLenum dfactor);\nGLAPI PFNGLBLENDFUNCPROC glad_glBlendFunc;\n#define glBlendFunc glad_glBlendFunc\ntypedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC)(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha);\nGLAPI PFNGLBLENDFUNCSEPARATEPROC glad_glBlendFuncSeparate;\n#define glBlendFuncSeparate glad_glBlendFuncSeparate\ntypedef void (APIENTRYP PFNGLBUFFERDATAPROC)(GLenum target, GLsizeiptr size, const void *data, GLenum usage);\nGLAPI PFNGLBUFFERDATAPROC glad_glBufferData;\n#define glBufferData glad_glBufferData\ntypedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC)(GLenum target, GLintptr offset, GLsizeiptr size, const void *data);\nGLAPI PFNGLBUFFERSUBDATAPROC glad_glBufferSubData;\n#define glBufferSubData glad_glBufferSubData\ntypedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSPROC)(GLenum target);\nGLAPI PFNGLCHECKFRAMEBUFFERSTATUSPROC glad_glCheckFramebufferStatus;\n#define glCheckFramebufferStatus glad_glCheckFramebufferStatus\ntypedef void (APIENTRYP PFNGLCLEARPROC)(GLbitfield mask);\nGLAPI PFNGLCLEARPROC glad_glClear;\n#define glClear glad_glClear\ntypedef void (APIENTRYP PFNGLCLEARCOLORPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);\nGLAPI PFNGLCLEARCOLORPROC glad_glClearColor;\n#define glClearColor glad_glClearColor\ntypedef void (APIENTRYP PFNGLCLEARDEPTHFPROC)(GLfloat d);\nGLAPI PFNGLCLEARDEPTHFPROC glad_glClearDepthf;\n#define glClearDepthf glad_glClearDepthf\ntypedef void (APIENTRYP PFNGLCLEARSTENCILPROC)(GLint s);\nGLAPI PFNGLCLEARSTENCILPROC glad_glClearStencil;\n#define glClearStencil glad_glClearStencil\ntypedef void (APIENTRYP PFNGLCOLORMASKPROC)(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha);\nGLAPI PFNGLCOLORMASKPROC glad_glColorMask;\n#define glColorMask glad_glColorMask\ntypedef void (APIENTRYP PFNGLCOMPILESHADERPROC)(GLuint shader);\nGLAPI PFNGLCOMPILESHADERPROC glad_glCompileShader;\n#define glCompileShader glad_glCompileShader\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data);\nGLAPI PFNGLCOMPRESSEDTEXIMAGE2DPROC glad_glCompressedTexImage2D;\n#define glCompressedTexImage2D glad_glCompressedTexImage2D\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data);\nGLAPI PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_glCompressedTexSubImage2D;\n#define glCompressedTexSubImage2D glad_glCompressedTexSubImage2D\ntypedef void (APIENTRYP PFNGLCOPYTEXIMAGE2DPROC)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border);\nGLAPI PFNGLCOPYTEXIMAGE2DPROC glad_glCopyTexImage2D;\n#define glCopyTexImage2D glad_glCopyTexImage2D\ntypedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height);\nGLAPI PFNGLCOPYTEXSUBIMAGE2DPROC glad_glCopyTexSubImage2D;\n#define glCopyTexSubImage2D glad_glCopyTexSubImage2D\ntypedef GLuint (APIENTRYP PFNGLCREATEPROGRAMPROC)(void);\nGLAPI PFNGLCREATEPROGRAMPROC glad_glCreateProgram;\n#define glCreateProgram glad_glCreateProgram\ntypedef GLuint (APIENTRYP PFNGLCREATESHADERPROC)(GLenum type);\nGLAPI PFNGLCREATESHADERPROC glad_glCreateShader;\n#define glCreateShader glad_glCreateShader\ntypedef void (APIENTRYP PFNGLCULLFACEPROC)(GLenum mode);\nGLAPI PFNGLCULLFACEPROC glad_glCullFace;\n#define glCullFace glad_glCullFace\ntypedef void (APIENTRYP PFNGLDELETEBUFFERSPROC)(GLsizei n, const GLuint *buffers);\nGLAPI PFNGLDELETEBUFFERSPROC glad_glDeleteBuffers;\n#define glDeleteBuffers glad_glDeleteBuffers\ntypedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSPROC)(GLsizei n, const GLuint *framebuffers);\nGLAPI PFNGLDELETEFRAMEBUFFERSPROC glad_glDeleteFramebuffers;\n#define glDeleteFramebuffers glad_glDeleteFramebuffers\ntypedef void (APIENTRYP PFNGLDELETEPROGRAMPROC)(GLuint program);\nGLAPI PFNGLDELETEPROGRAMPROC glad_glDeleteProgram;\n#define glDeleteProgram glad_glDeleteProgram\ntypedef void (APIENTRYP PFNGLDELETERENDERBUFFERSPROC)(GLsizei n, const GLuint *renderbuffers);\nGLAPI PFNGLDELETERENDERBUFFERSPROC glad_glDeleteRenderbuffers;\n#define glDeleteRenderbuffers glad_glDeleteRenderbuffers\ntypedef void (APIENTRYP PFNGLDELETESHADERPROC)(GLuint shader);\nGLAPI PFNGLDELETESHADERPROC glad_glDeleteShader;\n#define glDeleteShader glad_glDeleteShader\ntypedef void (APIENTRYP PFNGLDELETETEXTURESPROC)(GLsizei n, const GLuint *textures);\nGLAPI PFNGLDELETETEXTURESPROC glad_glDeleteTextures;\n#define glDeleteTextures glad_glDeleteTextures\ntypedef void (APIENTRYP PFNGLDEPTHFUNCPROC)(GLenum func);\nGLAPI PFNGLDEPTHFUNCPROC glad_glDepthFunc;\n#define glDepthFunc glad_glDepthFunc\ntypedef void (APIENTRYP PFNGLDEPTHMASKPROC)(GLboolean flag);\nGLAPI PFNGLDEPTHMASKPROC glad_glDepthMask;\n#define glDepthMask glad_glDepthMask\ntypedef void (APIENTRYP PFNGLDEPTHRANGEFPROC)(GLfloat n, GLfloat f);\nGLAPI PFNGLDEPTHRANGEFPROC glad_glDepthRangef;\n#define glDepthRangef glad_glDepthRangef\ntypedef void (APIENTRYP PFNGLDETACHSHADERPROC)(GLuint program, GLuint shader);\nGLAPI PFNGLDETACHSHADERPROC glad_glDetachShader;\n#define glDetachShader glad_glDetachShader\ntypedef void (APIENTRYP PFNGLDISABLEPROC)(GLenum cap);\nGLAPI PFNGLDISABLEPROC glad_glDisable;\n#define glDisable glad_glDisable\ntypedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYPROC)(GLuint index);\nGLAPI PFNGLDISABLEVERTEXATTRIBARRAYPROC glad_glDisableVertexAttribArray;\n#define glDisableVertexAttribArray glad_glDisableVertexAttribArray\ntypedef void (APIENTRYP PFNGLDRAWARRAYSPROC)(GLenum mode, GLint first, GLsizei count);\nGLAPI PFNGLDRAWARRAYSPROC glad_glDrawArrays;\n#define glDrawArrays glad_glDrawArrays\ntypedef void (APIENTRYP PFNGLDRAWELEMENTSPROC)(GLenum mode, GLsizei count, GLenum type, const void *indices);\nGLAPI PFNGLDRAWELEMENTSPROC glad_glDrawElements;\n#define glDrawElements glad_glDrawElements\ntypedef void (APIENTRYP PFNGLENABLEPROC)(GLenum cap);\nGLAPI PFNGLENABLEPROC glad_glEnable;\n#define glEnable glad_glEnable\ntypedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYPROC)(GLuint index);\nGLAPI PFNGLENABLEVERTEXATTRIBARRAYPROC glad_glEnableVertexAttribArray;\n#define glEnableVertexAttribArray glad_glEnableVertexAttribArray\ntypedef void (APIENTRYP PFNGLFINISHPROC)(void);\nGLAPI PFNGLFINISHPROC glad_glFinish;\n#define glFinish glad_glFinish\ntypedef void (APIENTRYP PFNGLFLUSHPROC)(void);\nGLAPI PFNGLFLUSHPROC glad_glFlush;\n#define glFlush glad_glFlush\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFERPROC)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);\nGLAPI PFNGLFRAMEBUFFERRENDERBUFFERPROC glad_glFramebufferRenderbuffer;\n#define glFramebufferRenderbuffer glad_glFramebufferRenderbuffer\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);\nGLAPI PFNGLFRAMEBUFFERTEXTURE2DPROC glad_glFramebufferTexture2D;\n#define glFramebufferTexture2D glad_glFramebufferTexture2D\ntypedef void (APIENTRYP PFNGLFRONTFACEPROC)(GLenum mode);\nGLAPI PFNGLFRONTFACEPROC glad_glFrontFace;\n#define glFrontFace glad_glFrontFace\ntypedef void (APIENTRYP PFNGLGENBUFFERSPROC)(GLsizei n, GLuint *buffers);\nGLAPI PFNGLGENBUFFERSPROC glad_glGenBuffers;\n#define glGenBuffers glad_glGenBuffers\ntypedef void (APIENTRYP PFNGLGENERATEMIPMAPPROC)(GLenum target);\nGLAPI PFNGLGENERATEMIPMAPPROC glad_glGenerateMipmap;\n#define glGenerateMipmap glad_glGenerateMipmap\ntypedef void (APIENTRYP PFNGLGENFRAMEBUFFERSPROC)(GLsizei n, GLuint *framebuffers);\nGLAPI PFNGLGENFRAMEBUFFERSPROC glad_glGenFramebuffers;\n#define glGenFramebuffers glad_glGenFramebuffers\ntypedef void (APIENTRYP PFNGLGENRENDERBUFFERSPROC)(GLsizei n, GLuint *renderbuffers);\nGLAPI PFNGLGENRENDERBUFFERSPROC glad_glGenRenderbuffers;\n#define glGenRenderbuffers glad_glGenRenderbuffers\ntypedef void (APIENTRYP PFNGLGENTEXTURESPROC)(GLsizei n, GLuint *textures);\nGLAPI PFNGLGENTEXTURESPROC glad_glGenTextures;\n#define glGenTextures glad_glGenTextures\ntypedef void (APIENTRYP PFNGLGETACTIVEATTRIBPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name);\nGLAPI PFNGLGETACTIVEATTRIBPROC glad_glGetActiveAttrib;\n#define glGetActiveAttrib glad_glGetActiveAttrib\ntypedef void (APIENTRYP PFNGLGETACTIVEUNIFORMPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name);\nGLAPI PFNGLGETACTIVEUNIFORMPROC glad_glGetActiveUniform;\n#define glGetActiveUniform glad_glGetActiveUniform\ntypedef void (APIENTRYP PFNGLGETATTACHEDSHADERSPROC)(GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders);\nGLAPI PFNGLGETATTACHEDSHADERSPROC glad_glGetAttachedShaders;\n#define glGetAttachedShaders glad_glGetAttachedShaders\ntypedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONPROC)(GLuint program, const GLchar *name);\nGLAPI PFNGLGETATTRIBLOCATIONPROC glad_glGetAttribLocation;\n#define glGetAttribLocation glad_glGetAttribLocation\ntypedef void (APIENTRYP PFNGLGETBOOLEANVPROC)(GLenum pname, GLboolean *data);\nGLAPI PFNGLGETBOOLEANVPROC glad_glGetBooleanv;\n#define glGetBooleanv glad_glGetBooleanv\ntypedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint *params);\nGLAPI PFNGLGETBUFFERPARAMETERIVPROC glad_glGetBufferParameteriv;\n#define glGetBufferParameteriv glad_glGetBufferParameteriv\ntypedef GLenum (APIENTRYP PFNGLGETERRORPROC)(void);\nGLAPI PFNGLGETERRORPROC glad_glGetError;\n#define glGetError glad_glGetError\ntypedef void (APIENTRYP PFNGLGETFLOATVPROC)(GLenum pname, GLfloat *data);\nGLAPI PFNGLGETFLOATVPROC glad_glGetFloatv;\n#define glGetFloatv glad_glGetFloatv\ntypedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)(GLenum target, GLenum attachment, GLenum pname, GLint *params);\nGLAPI PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetFramebufferAttachmentParameteriv;\n#define glGetFramebufferAttachmentParameteriv glad_glGetFramebufferAttachmentParameteriv\ntypedef void (APIENTRYP PFNGLGETINTEGERVPROC)(GLenum pname, GLint *data);\nGLAPI PFNGLGETINTEGERVPROC glad_glGetIntegerv;\n#define glGetIntegerv glad_glGetIntegerv\ntypedef void (APIENTRYP PFNGLGETPROGRAMIVPROC)(GLuint program, GLenum pname, GLint *params);\nGLAPI PFNGLGETPROGRAMIVPROC glad_glGetProgramiv;\n#define glGetProgramiv glad_glGetProgramiv\ntypedef void (APIENTRYP PFNGLGETPROGRAMINFOLOGPROC)(GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog);\nGLAPI PFNGLGETPROGRAMINFOLOGPROC glad_glGetProgramInfoLog;\n#define glGetProgramInfoLog glad_glGetProgramInfoLog\ntypedef void (APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint *params);\nGLAPI PFNGLGETRENDERBUFFERPARAMETERIVPROC glad_glGetRenderbufferParameteriv;\n#define glGetRenderbufferParameteriv glad_glGetRenderbufferParameteriv\ntypedef void (APIENTRYP PFNGLGETSHADERIVPROC)(GLuint shader, GLenum pname, GLint *params);\nGLAPI PFNGLGETSHADERIVPROC glad_glGetShaderiv;\n#define glGetShaderiv glad_glGetShaderiv\ntypedef void (APIENTRYP PFNGLGETSHADERINFOLOGPROC)(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog);\nGLAPI PFNGLGETSHADERINFOLOGPROC glad_glGetShaderInfoLog;\n#define glGetShaderInfoLog glad_glGetShaderInfoLog\ntypedef void (APIENTRYP PFNGLGETSHADERPRECISIONFORMATPROC)(GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision);\nGLAPI PFNGLGETSHADERPRECISIONFORMATPROC glad_glGetShaderPrecisionFormat;\n#define glGetShaderPrecisionFormat glad_glGetShaderPrecisionFormat\ntypedef void (APIENTRYP PFNGLGETSHADERSOURCEPROC)(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source);\nGLAPI PFNGLGETSHADERSOURCEPROC glad_glGetShaderSource;\n#define glGetShaderSource glad_glGetShaderSource\ntypedef const GLubyte * (APIENTRYP PFNGLGETSTRINGPROC)(GLenum name);\nGLAPI PFNGLGETSTRINGPROC glad_glGetString;\n#define glGetString glad_glGetString\ntypedef void (APIENTRYP PFNGLGETTEXPARAMETERFVPROC)(GLenum target, GLenum pname, GLfloat *params);\nGLAPI PFNGLGETTEXPARAMETERFVPROC glad_glGetTexParameterfv;\n#define glGetTexParameterfv glad_glGetTexParameterfv\ntypedef void (APIENTRYP PFNGLGETTEXPARAMETERIVPROC)(GLenum target, GLenum pname, GLint *params);\nGLAPI PFNGLGETTEXPARAMETERIVPROC glad_glGetTexParameteriv;\n#define glGetTexParameteriv glad_glGetTexParameteriv\ntypedef void (APIENTRYP PFNGLGETUNIFORMFVPROC)(GLuint program, GLint location, GLfloat *params);\nGLAPI PFNGLGETUNIFORMFVPROC glad_glGetUniformfv;\n#define glGetUniformfv glad_glGetUniformfv\ntypedef void (APIENTRYP PFNGLGETUNIFORMIVPROC)(GLuint program, GLint location, GLint *params);\nGLAPI PFNGLGETUNIFORMIVPROC glad_glGetUniformiv;\n#define glGetUniformiv glad_glGetUniformiv\ntypedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONPROC)(GLuint program, const GLchar *name);\nGLAPI PFNGLGETUNIFORMLOCATIONPROC glad_glGetUniformLocation;\n#define glGetUniformLocation glad_glGetUniformLocation\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVPROC)(GLuint index, GLenum pname, GLfloat *params);\nGLAPI PFNGLGETVERTEXATTRIBFVPROC glad_glGetVertexAttribfv;\n#define glGetVertexAttribfv glad_glGetVertexAttribfv\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVPROC)(GLuint index, GLenum pname, GLint *params);\nGLAPI PFNGLGETVERTEXATTRIBIVPROC glad_glGetVertexAttribiv;\n#define glGetVertexAttribiv glad_glGetVertexAttribiv\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVPROC)(GLuint index, GLenum pname, void **pointer);\nGLAPI PFNGLGETVERTEXATTRIBPOINTERVPROC glad_glGetVertexAttribPointerv;\n#define glGetVertexAttribPointerv glad_glGetVertexAttribPointerv\ntypedef void (APIENTRYP PFNGLHINTPROC)(GLenum target, GLenum mode);\nGLAPI PFNGLHINTPROC glad_glHint;\n#define glHint glad_glHint\ntypedef GLboolean (APIENTRYP PFNGLISBUFFERPROC)(GLuint buffer);\nGLAPI PFNGLISBUFFERPROC glad_glIsBuffer;\n#define glIsBuffer glad_glIsBuffer\ntypedef GLboolean (APIENTRYP PFNGLISENABLEDPROC)(GLenum cap);\nGLAPI PFNGLISENABLEDPROC glad_glIsEnabled;\n#define glIsEnabled glad_glIsEnabled\ntypedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFERPROC)(GLuint framebuffer);\nGLAPI PFNGLISFRAMEBUFFERPROC glad_glIsFramebuffer;\n#define glIsFramebuffer glad_glIsFramebuffer\ntypedef GLboolean (APIENTRYP PFNGLISPROGRAMPROC)(GLuint program);\nGLAPI PFNGLISPROGRAMPROC glad_glIsProgram;\n#define glIsProgram glad_glIsProgram\ntypedef GLboolean (APIENTRYP PFNGLISRENDERBUFFERPROC)(GLuint renderbuffer);\nGLAPI PFNGLISRENDERBUFFERPROC glad_glIsRenderbuffer;\n#define glIsRenderbuffer glad_glIsRenderbuffer\ntypedef GLboolean (APIENTRYP PFNGLISSHADERPROC)(GLuint shader);\nGLAPI PFNGLISSHADERPROC glad_glIsShader;\n#define glIsShader glad_glIsShader\ntypedef GLboolean (APIENTRYP PFNGLISTEXTUREPROC)(GLuint texture);\nGLAPI PFNGLISTEXTUREPROC glad_glIsTexture;\n#define glIsTexture glad_glIsTexture\ntypedef void (APIENTRYP PFNGLLINEWIDTHPROC)(GLfloat width);\nGLAPI PFNGLLINEWIDTHPROC glad_glLineWidth;\n#define glLineWidth glad_glLineWidth\ntypedef void (APIENTRYP PFNGLLINKPROGRAMPROC)(GLuint program);\nGLAPI PFNGLLINKPROGRAMPROC glad_glLinkProgram;\n#define glLinkProgram glad_glLinkProgram\ntypedef void (APIENTRYP PFNGLPIXELSTOREIPROC)(GLenum pname, GLint param);\nGLAPI PFNGLPIXELSTOREIPROC glad_glPixelStorei;\n#define glPixelStorei glad_glPixelStorei\ntypedef void (APIENTRYP PFNGLPOLYGONOFFSETPROC)(GLfloat factor, GLfloat units);\nGLAPI PFNGLPOLYGONOFFSETPROC glad_glPolygonOffset;\n#define glPolygonOffset glad_glPolygonOffset\ntypedef void (APIENTRYP PFNGLREADPIXELSPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels);\nGLAPI PFNGLREADPIXELSPROC glad_glReadPixels;\n#define glReadPixels glad_glReadPixels\ntypedef void (APIENTRYP PFNGLRELEASESHADERCOMPILERPROC)(void);\nGLAPI PFNGLRELEASESHADERCOMPILERPROC glad_glReleaseShaderCompiler;\n#define glReleaseShaderCompiler glad_glReleaseShaderCompiler\ntypedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEPROC)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height);\nGLAPI PFNGLRENDERBUFFERSTORAGEPROC glad_glRenderbufferStorage;\n#define glRenderbufferStorage glad_glRenderbufferStorage\ntypedef void (APIENTRYP PFNGLSAMPLECOVERAGEPROC)(GLfloat value, GLboolean invert);\nGLAPI PFNGLSAMPLECOVERAGEPROC glad_glSampleCoverage;\n#define glSampleCoverage glad_glSampleCoverage\ntypedef void (APIENTRYP PFNGLSCISSORPROC)(GLint x, GLint y, GLsizei width, GLsizei height);\nGLAPI PFNGLSCISSORPROC glad_glScissor;\n#define glScissor glad_glScissor\ntypedef void (APIENTRYP PFNGLSHADERBINARYPROC)(GLsizei count, const GLuint *shaders, GLenum binaryformat, const void *binary, GLsizei length);\nGLAPI PFNGLSHADERBINARYPROC glad_glShaderBinary;\n#define glShaderBinary glad_glShaderBinary\ntypedef void (APIENTRYP PFNGLSHADERSOURCEPROC)(GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length);\nGLAPI PFNGLSHADERSOURCEPROC glad_glShaderSource;\n#define glShaderSource glad_glShaderSource\ntypedef void (APIENTRYP PFNGLSTENCILFUNCPROC)(GLenum func, GLint ref, GLuint mask);\nGLAPI PFNGLSTENCILFUNCPROC glad_glStencilFunc;\n#define glStencilFunc glad_glStencilFunc\ntypedef void (APIENTRYP PFNGLSTENCILFUNCSEPARATEPROC)(GLenum face, GLenum func, GLint ref, GLuint mask);\nGLAPI PFNGLSTENCILFUNCSEPARATEPROC glad_glStencilFuncSeparate;\n#define glStencilFuncSeparate glad_glStencilFuncSeparate\ntypedef void (APIENTRYP PFNGLSTENCILMASKPROC)(GLuint mask);\nGLAPI PFNGLSTENCILMASKPROC glad_glStencilMask;\n#define glStencilMask glad_glStencilMask\ntypedef void (APIENTRYP PFNGLSTENCILMASKSEPARATEPROC)(GLenum face, GLuint mask);\nGLAPI PFNGLSTENCILMASKSEPARATEPROC glad_glStencilMaskSeparate;\n#define glStencilMaskSeparate glad_glStencilMaskSeparate\ntypedef void (APIENTRYP PFNGLSTENCILOPPROC)(GLenum fail, GLenum zfail, GLenum zpass);\nGLAPI PFNGLSTENCILOPPROC glad_glStencilOp;\n#define glStencilOp glad_glStencilOp\ntypedef void (APIENTRYP PFNGLSTENCILOPSEPARATEPROC)(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass);\nGLAPI PFNGLSTENCILOPSEPARATEPROC glad_glStencilOpSeparate;\n#define glStencilOpSeparate glad_glStencilOpSeparate\ntypedef void (APIENTRYP PFNGLTEXIMAGE2DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels);\nGLAPI PFNGLTEXIMAGE2DPROC glad_glTexImage2D;\n#define glTexImage2D glad_glTexImage2D\ntypedef void (APIENTRYP PFNGLTEXPARAMETERFPROC)(GLenum target, GLenum pname, GLfloat param);\nGLAPI PFNGLTEXPARAMETERFPROC glad_glTexParameterf;\n#define glTexParameterf glad_glTexParameterf\ntypedef void (APIENTRYP PFNGLTEXPARAMETERFVPROC)(GLenum target, GLenum pname, const GLfloat *params);\nGLAPI PFNGLTEXPARAMETERFVPROC glad_glTexParameterfv;\n#define glTexParameterfv glad_glTexParameterfv\ntypedef void (APIENTRYP PFNGLTEXPARAMETERIPROC)(GLenum target, GLenum pname, GLint param);\nGLAPI PFNGLTEXPARAMETERIPROC glad_glTexParameteri;\n#define glTexParameteri glad_glTexParameteri\ntypedef void (APIENTRYP PFNGLTEXPARAMETERIVPROC)(GLenum target, GLenum pname, const GLint *params);\nGLAPI PFNGLTEXPARAMETERIVPROC glad_glTexParameteriv;\n#define glTexParameteriv glad_glTexParameteriv\ntypedef void (APIENTRYP PFNGLTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels);\nGLAPI PFNGLTEXSUBIMAGE2DPROC glad_glTexSubImage2D;\n#define glTexSubImage2D glad_glTexSubImage2D\ntypedef void (APIENTRYP PFNGLUNIFORM1FPROC)(GLint location, GLfloat v0);\nGLAPI PFNGLUNIFORM1FPROC glad_glUniform1f;\n#define glUniform1f glad_glUniform1f\ntypedef void (APIENTRYP PFNGLUNIFORM1FVPROC)(GLint location, GLsizei count, const GLfloat *value);\nGLAPI PFNGLUNIFORM1FVPROC glad_glUniform1fv;\n#define glUniform1fv glad_glUniform1fv\ntypedef void (APIENTRYP PFNGLUNIFORM1IPROC)(GLint location, GLint v0);\nGLAPI PFNGLUNIFORM1IPROC glad_glUniform1i;\n#define glUniform1i glad_glUniform1i\ntypedef void (APIENTRYP PFNGLUNIFORM1IVPROC)(GLint location, GLsizei count, const GLint *value);\nGLAPI PFNGLUNIFORM1IVPROC glad_glUniform1iv;\n#define glUniform1iv glad_glUniform1iv\ntypedef void (APIENTRYP PFNGLUNIFORM2FPROC)(GLint location, GLfloat v0, GLfloat v1);\nGLAPI PFNGLUNIFORM2FPROC glad_glUniform2f;\n#define glUniform2f glad_glUniform2f\ntypedef void (APIENTRYP PFNGLUNIFORM2FVPROC)(GLint location, GLsizei count, const GLfloat *value);\nGLAPI PFNGLUNIFORM2FVPROC glad_glUniform2fv;\n#define glUniform2fv glad_glUniform2fv\ntypedef void (APIENTRYP PFNGLUNIFORM2IPROC)(GLint location, GLint v0, GLint v1);\nGLAPI PFNGLUNIFORM2IPROC glad_glUniform2i;\n#define glUniform2i glad_glUniform2i\ntypedef void (APIENTRYP PFNGLUNIFORM2IVPROC)(GLint location, GLsizei count, const GLint *value);\nGLAPI PFNGLUNIFORM2IVPROC glad_glUniform2iv;\n#define glUniform2iv glad_glUniform2iv\ntypedef void (APIENTRYP PFNGLUNIFORM3FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2);\nGLAPI PFNGLUNIFORM3FPROC glad_glUniform3f;\n#define glUniform3f glad_glUniform3f\ntypedef void (APIENTRYP PFNGLUNIFORM3FVPROC)(GLint location, GLsizei count, const GLfloat *value);\nGLAPI PFNGLUNIFORM3FVPROC glad_glUniform3fv;\n#define glUniform3fv glad_glUniform3fv\ntypedef void (APIENTRYP PFNGLUNIFORM3IPROC)(GLint location, GLint v0, GLint v1, GLint v2);\nGLAPI PFNGLUNIFORM3IPROC glad_glUniform3i;\n#define glUniform3i glad_glUniform3i\ntypedef void (APIENTRYP PFNGLUNIFORM3IVPROC)(GLint location, GLsizei count, const GLint *value);\nGLAPI PFNGLUNIFORM3IVPROC glad_glUniform3iv;\n#define glUniform3iv glad_glUniform3iv\ntypedef void (APIENTRYP PFNGLUNIFORM4FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);\nGLAPI PFNGLUNIFORM4FPROC glad_glUniform4f;\n#define glUniform4f glad_glUniform4f\ntypedef void (APIENTRYP PFNGLUNIFORM4FVPROC)(GLint location, GLsizei count, const GLfloat *value);\nGLAPI PFNGLUNIFORM4FVPROC glad_glUniform4fv;\n#define glUniform4fv glad_glUniform4fv\ntypedef void (APIENTRYP PFNGLUNIFORM4IPROC)(GLint location, GLint v0, GLint v1, GLint v2, GLint v3);\nGLAPI PFNGLUNIFORM4IPROC glad_glUniform4i;\n#define glUniform4i glad_glUniform4i\ntypedef void (APIENTRYP PFNGLUNIFORM4IVPROC)(GLint location, GLsizei count, const GLint *value);\nGLAPI PFNGLUNIFORM4IVPROC glad_glUniform4iv;\n#define glUniform4iv glad_glUniform4iv\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\nGLAPI PFNGLUNIFORMMATRIX2FVPROC glad_glUniformMatrix2fv;\n#define glUniformMatrix2fv glad_glUniformMatrix2fv\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\nGLAPI PFNGLUNIFORMMATRIX3FVPROC glad_glUniformMatrix3fv;\n#define glUniformMatrix3fv glad_glUniformMatrix3fv\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\nGLAPI PFNGLUNIFORMMATRIX4FVPROC glad_glUniformMatrix4fv;\n#define glUniformMatrix4fv glad_glUniformMatrix4fv\ntypedef void (APIENTRYP PFNGLUSEPROGRAMPROC)(GLuint program);\nGLAPI PFNGLUSEPROGRAMPROC glad_glUseProgram;\n#define glUseProgram glad_glUseProgram\ntypedef void (APIENTRYP PFNGLVALIDATEPROGRAMPROC)(GLuint program);\nGLAPI PFNGLVALIDATEPROGRAMPROC glad_glValidateProgram;\n#define glValidateProgram glad_glValidateProgram\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB1FPROC)(GLuint index, GLfloat x);\nGLAPI PFNGLVERTEXATTRIB1FPROC glad_glVertexAttrib1f;\n#define glVertexAttrib1f glad_glVertexAttrib1f\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB1FVPROC)(GLuint index, const GLfloat *v);\nGLAPI PFNGLVERTEXATTRIB1FVPROC glad_glVertexAttrib1fv;\n#define glVertexAttrib1fv glad_glVertexAttrib1fv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB2FPROC)(GLuint index, GLfloat x, GLfloat y);\nGLAPI PFNGLVERTEXATTRIB2FPROC glad_glVertexAttrib2f;\n#define glVertexAttrib2f glad_glVertexAttrib2f\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB2FVPROC)(GLuint index, const GLfloat *v);\nGLAPI PFNGLVERTEXATTRIB2FVPROC glad_glVertexAttrib2fv;\n#define glVertexAttrib2fv glad_glVertexAttrib2fv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB3FPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z);\nGLAPI PFNGLVERTEXATTRIB3FPROC glad_glVertexAttrib3f;\n#define glVertexAttrib3f glad_glVertexAttrib3f\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB3FVPROC)(GLuint index, const GLfloat *v);\nGLAPI PFNGLVERTEXATTRIB3FVPROC glad_glVertexAttrib3fv;\n#define glVertexAttrib3fv glad_glVertexAttrib3fv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4FPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w);\nGLAPI PFNGLVERTEXATTRIB4FPROC glad_glVertexAttrib4f;\n#define glVertexAttrib4f glad_glVertexAttrib4f\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4FVPROC)(GLuint index, const GLfloat *v);\nGLAPI PFNGLVERTEXATTRIB4FVPROC glad_glVertexAttrib4fv;\n#define glVertexAttrib4fv glad_glVertexAttrib4fv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer);\nGLAPI PFNGLVERTEXATTRIBPOINTERPROC glad_glVertexAttribPointer;\n#define glVertexAttribPointer glad_glVertexAttribPointer\ntypedef void (APIENTRYP PFNGLVIEWPORTPROC)(GLint x, GLint y, GLsizei width, GLsizei height);\nGLAPI PFNGLVIEWPORTPROC glad_glViewport;\n#define glViewport glad_glViewport\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "include/stb_image.h",
    "content": "/* stb_image - v2.19 - public domain image loader - http://nothings.org/stb\n                                  no warranty implied; use at your own risk\n\n   Do this:\n      #define STB_IMAGE_IMPLEMENTATION\n   before you include this file in *one* C or C++ file to create the implementation.\n\n   // i.e. it should look like this:\n   #include ...\n   #include ...\n   #include ...\n   #define STB_IMAGE_IMPLEMENTATION\n   #include \"stb_image.h\"\n\n   You can #define STBI_ASSERT(x) before the #include to avoid using assert.h.\n   And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free\n\n\n   QUICK NOTES:\n      Primarily of interest to game developers and other people who can\n          avoid problematic images and only need the trivial interface\n\n      JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib)\n      PNG 1/2/4/8/16-bit-per-channel\n\n      TGA (not sure what subset, if a subset)\n      BMP non-1bpp, non-RLE\n      PSD (composited view only, no extra channels, 8/16 bit-per-channel)\n\n      GIF (*comp always reports as 4-channel)\n      HDR (radiance rgbE format)\n      PIC (Softimage PIC)\n      PNM (PPM and PGM binary only)\n\n      Animated GIF still needs a proper API, but here's one way to do it:\n          http://gist.github.com/urraka/685d9a6340b26b830d49\n\n      - decode from memory or through FILE (define STBI_NO_STDIO to remove code)\n      - decode from arbitrary I/O callbacks\n      - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON)\n\n   Full documentation under \"DOCUMENTATION\" below.\n\n\nLICENSE\n\n  See end of file for license information.\n\nRECENT REVISION HISTORY:\n\n      2.19  (2018-02-11) fix warning\n      2.18  (2018-01-30) fix warnings\n      2.17  (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings\n      2.16  (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes\n      2.15  (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC\n      2.14  (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs\n      2.13  (2016-12-04) experimental 16-bit API, only for PNG so far; fixes\n      2.12  (2016-04-02) fix typo in 2.11 PSD fix that caused crashes\n      2.11  (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64\n                         RGB-format JPEG; remove white matting in PSD;\n                         allocate large structures on the stack;\n                         correct channel count for PNG & BMP\n      2.10  (2016-01-22) avoid warning introduced in 2.09\n      2.09  (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED\n\n   See end of file for full revision history.\n\n\n ============================    Contributors    =========================\n\n Image formats                          Extensions, features\n    Sean Barrett (jpeg, png, bmp)          Jetro Lauha (stbi_info)\n    Nicolas Schulz (hdr, psd)              Martin \"SpartanJ\" Golini (stbi_info)\n    Jonathan Dummer (tga)                  James \"moose2000\" Brown (iPhone PNG)\n    Jean-Marc Lienher (gif)                Ben \"Disch\" Wenger (io callbacks)\n    Tom Seddon (pic)                       Omar Cornut (1/2/4-bit PNG)\n    Thatcher Ulrich (psd)                  Nicolas Guillemot (vertical flip)\n    Ken Miller (pgm, ppm)                  Richard Mitton (16-bit PSD)\n    github:urraka (animated gif)           Junggon Kim (PNM comments)\n    Christopher Forseth (animated gif)     Daniel Gibson (16-bit TGA)\n                                           socks-the-fox (16-bit PNG)\n                                           Jeremy Sawicki (handle all ImageNet JPGs)\n Optimizations & bugfixes                  Mikhail Morozov (1-bit BMP)\n    Fabian \"ryg\" Giesen                    Anael Seghezzi (is-16-bit query)\n    Arseny Kapoulkine\n    John-Mark Allen\n\n Bug & warning fixes\n    Marc LeBlanc            David Woo          Guillaume George   Martins Mozeiko\n    Christpher Lloyd        Jerry Jansson      Joseph Thomson     Phil Jordan\n    Dave Moore              Roy Eltham         Hayaki Saito       Nathan Reed\n    Won Chun                Luke Graham        Johan Duparc       Nick Verigakis\n    the Horde3D community   Thomas Ruf         Ronny Chevalier    github:rlyeh\n    Janez Zemva             John Bartholomew   Michal Cichon      github:romigrou\n    Jonathan Blow           Ken Hamada         Tero Hanninen      github:svdijk\n    Laurent Gomila          Cort Stratton      Sergio Gonzalez    github:snagar\n    Aruelien Pocheville     Thibault Reuille   Cass Everitt       github:Zelex\n    Ryamond Barbiero        Paul Du Bois       Engin Manap        github:grim210\n    Aldo Culquicondor       Philipp Wiesemann  Dale Weiler        github:sammyhw\n    Oriol Ferrer Mesia      Josh Tobin         Matthew Gregan     github:phprus\n    Julian Raschke          Gregory Mullen     Baldur Karlsson    github:poppolopoppo\n    Christian Floisand      Kevin Schmidt                         github:darealshinji\n    Blazej Dariusz Roszkowski                                     github:Michaelangel007\n*/\n\n#ifndef STBI_INCLUDE_STB_IMAGE_H\n#define STBI_INCLUDE_STB_IMAGE_H\n\n// DOCUMENTATION\n//\n// Limitations:\n//    - no 12-bit-per-channel JPEG\n//    - no JPEGs with arithmetic coding\n//    - GIF always returns *comp=4\n//\n// Basic usage (see HDR discussion below for HDR usage):\n//    int x,y,n;\n//    unsigned char *data = stbi_load(filename, &x, &y, &n, 0);\n//    // ... process data if not NULL ...\n//    // ... x = width, y = height, n = # 8-bit components per pixel ...\n//    // ... replace '0' with '1'..'4' to force that many components per pixel\n//    // ... but 'n' will always be the number that it would have been if you said 0\n//    stbi_image_free(data)\n//\n// Standard parameters:\n//    int *x                 -- outputs image width in pixels\n//    int *y                 -- outputs image height in pixels\n//    int *channels_in_file  -- outputs # of image components in image file\n//    int desired_channels   -- if non-zero, # of image components requested in result\n//\n// The return value from an image loader is an 'unsigned char *' which points\n// to the pixel data, or NULL on an allocation failure or if the image is\n// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels,\n// with each pixel consisting of N interleaved 8-bit components; the first\n// pixel pointed to is top-left-most in the image. There is no padding between\n// image scanlines or between pixels, regardless of format. The number of\n// components N is 'desired_channels' if desired_channels is non-zero, or\n// *channels_in_file otherwise. If desired_channels is non-zero,\n// *channels_in_file has the number of components that _would_ have been\n// output otherwise. E.g. if you set desired_channels to 4, you will always\n// get RGBA output, but you can check *channels_in_file to see if it's trivially\n// opaque because e.g. there were only 3 channels in the source image.\n//\n// An output image with N components has the following components interleaved\n// in this order in each pixel:\n//\n//     N=#comp     components\n//       1           grey\n//       2           grey, alpha\n//       3           red, green, blue\n//       4           red, green, blue, alpha\n//\n// If image loading fails for any reason, the return value will be NULL,\n// and *x, *y, *channels_in_file will be unchanged. The function\n// stbi_failure_reason() can be queried for an extremely brief, end-user\n// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS\n// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly\n// more user-friendly ones.\n//\n// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized.\n//\n// ===========================================================================\n//\n// Philosophy\n//\n// stb libraries are designed with the following priorities:\n//\n//    1. easy to use\n//    2. easy to maintain\n//    3. good performance\n//\n// Sometimes I let \"good performance\" creep up in priority over \"easy to maintain\",\n// and for best performance I may provide less-easy-to-use APIs that give higher\n// performance, in addition to the easy to use ones. Nevertheless, it's important\n// to keep in mind that from the standpoint of you, a client of this library,\n// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all.\n//\n// Some secondary priorities arise directly from the first two, some of which\n// make more explicit reasons why performance can't be emphasized.\n//\n//    - Portable (\"ease of use\")\n//    - Small source code footprint (\"easy to maintain\")\n//    - No dependencies (\"ease of use\")\n//\n// ===========================================================================\n//\n// I/O callbacks\n//\n// I/O callbacks allow you to read from arbitrary sources, like packaged\n// files or some other source. Data read from callbacks are processed\n// through a small internal buffer (currently 128 bytes) to try to reduce\n// overhead.\n//\n// The three functions you must define are \"read\" (reads some bytes of data),\n// \"skip\" (skips some bytes of data), \"eof\" (reports if the stream is at the end).\n//\n// ===========================================================================\n//\n// SIMD support\n//\n// The JPEG decoder will try to automatically use SIMD kernels on x86 when\n// supported by the compiler. For ARM Neon support, you must explicitly\n// request it.\n//\n// (The old do-it-yourself SIMD API is no longer supported in the current\n// code.)\n//\n// On x86, SSE2 will automatically be used when available based on a run-time\n// test; if not, the generic C versions are used as a fall-back. On ARM targets,\n// the typical path is to have separate builds for NEON and non-NEON devices\n// (at least this is true for iOS and Android). Therefore, the NEON support is\n// toggled by a build flag: define STBI_NEON to get NEON loops.\n//\n// If for some reason you do not want to use any of SIMD code, or if\n// you have issues compiling it, you can disable it entirely by\n// defining STBI_NO_SIMD.\n//\n// ===========================================================================\n//\n// HDR image support   (disable by defining STBI_NO_HDR)\n//\n// stb_image now supports loading HDR images in general, and currently\n// the Radiance .HDR file format, although the support is provided\n// generically. You can still load any file through the existing interface;\n// if you attempt to load an HDR file, it will be automatically remapped to\n// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1;\n// both of these constants can be reconfigured through this interface:\n//\n//     stbi_hdr_to_ldr_gamma(2.2f);\n//     stbi_hdr_to_ldr_scale(1.0f);\n//\n// (note, do not use _inverse_ constants; stbi_image will invert them\n// appropriately).\n//\n// Additionally, there is a new, parallel interface for loading files as\n// (linear) floats to preserve the full dynamic range:\n//\n//    float *data = stbi_loadf(filename, &x, &y, &n, 0);\n//\n// If you load LDR images through this interface, those images will\n// be promoted to floating point values, run through the inverse of\n// constants corresponding to the above:\n//\n//     stbi_ldr_to_hdr_scale(1.0f);\n//     stbi_ldr_to_hdr_gamma(2.2f);\n//\n// Finally, given a filename (or an open file or memory block--see header\n// file for details) containing image data, you can query for the \"most\n// appropriate\" interface to use (that is, whether the image is HDR or\n// not), using:\n//\n//     stbi_is_hdr(char *filename);\n//\n// ===========================================================================\n//\n// iPhone PNG support:\n//\n// By default we convert iphone-formatted PNGs back to RGB, even though\n// they are internally encoded differently. You can disable this conversion\n// by by calling stbi_convert_iphone_png_to_rgb(0), in which case\n// you will always just get the native iphone \"format\" through (which\n// is BGR stored in RGB).\n//\n// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per\n// pixel to remove any premultiplied alpha *only* if the image file explicitly\n// says there's premultiplied data (currently only happens in iPhone images,\n// and only if iPhone convert-to-rgb processing is on).\n//\n// ===========================================================================\n//\n// ADDITIONAL CONFIGURATION\n//\n//  - You can suppress implementation of any of the decoders to reduce\n//    your code footprint by #defining one or more of the following\n//    symbols before creating the implementation.\n//\n//        STBI_NO_JPEG\n//        STBI_NO_PNG\n//        STBI_NO_BMP\n//        STBI_NO_PSD\n//        STBI_NO_TGA\n//        STBI_NO_GIF\n//        STBI_NO_HDR\n//        STBI_NO_PIC\n//        STBI_NO_PNM   (.ppm and .pgm)\n//\n//  - You can request *only* certain decoders and suppress all other ones\n//    (this will be more forward-compatible, as addition of new decoders\n//    doesn't require you to disable them explicitly):\n//\n//        STBI_ONLY_JPEG\n//        STBI_ONLY_PNG\n//        STBI_ONLY_BMP\n//        STBI_ONLY_PSD\n//        STBI_ONLY_TGA\n//        STBI_ONLY_GIF\n//        STBI_ONLY_HDR\n//        STBI_ONLY_PIC\n//        STBI_ONLY_PNM   (.ppm and .pgm)\n//\n//   - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still\n//     want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB\n//\n\n\n#ifndef STBI_NO_STDIO\n#include <stdio.h>\n#endif // STBI_NO_STDIO\n\n#define STBI_VERSION 1\n\nenum {\n    STBI_default = 0, // only used for desired_channels\n\n    STBI_grey       = 1,\n    STBI_grey_alpha = 2,\n    STBI_rgb        = 3,\n    STBI_rgb_alpha  = 4\n};\n\ntypedef unsigned char stbi_uc;\ntypedef unsigned short stbi_us;\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#ifdef STB_IMAGE_STATIC\n#define STBIDEF static\n#else\n#define STBIDEF extern\n#endif\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// PRIMARY API - works on images of any type\n//\n\n//\n// load image by filename, open file, or memory buffer\n//\n\ntypedef struct {\n    int      (*read)  (void *user,char *data,int size);   // fill 'data' with 'size' bytes.  return number of bytes actually read\n    void     (*skip)  (void *user,int n);                 // skip the next 'n' bytes, or 'unget' the last -n bytes if negative\n    int      (*eof)   (void *user);                       // returns nonzero if we are at end of file/data\n} stbi_io_callbacks;\n\n////////////////////////////////////\n//\n// 8-bits-per-channel interface\n//\n\nSTBIDEF stbi_uc *stbi_load_from_memory   (stbi_uc           const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels);\nSTBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels);\n#ifndef STBI_NO_GIF\nSTBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp);\n#endif\n\n\n#ifndef STBI_NO_STDIO\nSTBIDEF stbi_uc *stbi_load            (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels);\nSTBIDEF stbi_uc *stbi_load_from_file  (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels);\n// for stbi_load_from_file, file pointer is left pointing immediately after image\n#endif\n\n////////////////////////////////////\n//\n// 16-bits-per-channel interface\n//\n\nSTBIDEF stbi_us *stbi_load_16_from_memory   (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels);\nSTBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels);\n\n#ifndef STBI_NO_STDIO\nSTBIDEF stbi_us *stbi_load_16          (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels);\nSTBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels);\n#endif\n\n////////////////////////////////////\n//\n// float-per-channel interface\n//\n#ifndef STBI_NO_LINEAR\nSTBIDEF float *stbi_loadf_from_memory     (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels);\nSTBIDEF float *stbi_loadf_from_callbacks  (stbi_io_callbacks const *clbk, void *user, int *x, int *y,  int *channels_in_file, int desired_channels);\n\n#ifndef STBI_NO_STDIO\nSTBIDEF float *stbi_loadf            (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels);\nSTBIDEF float *stbi_loadf_from_file  (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels);\n#endif\n#endif\n\n#ifndef STBI_NO_HDR\nSTBIDEF void   stbi_hdr_to_ldr_gamma(float gamma);\nSTBIDEF void   stbi_hdr_to_ldr_scale(float scale);\n#endif // STBI_NO_HDR\n\n#ifndef STBI_NO_LINEAR\nSTBIDEF void   stbi_ldr_to_hdr_gamma(float gamma);\nSTBIDEF void   stbi_ldr_to_hdr_scale(float scale);\n#endif // STBI_NO_LINEAR\n\n// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR\nSTBIDEF int    stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user);\nSTBIDEF int    stbi_is_hdr_from_memory(stbi_uc const *buffer, int len);\n#ifndef STBI_NO_STDIO\nSTBIDEF int      stbi_is_hdr          (char const *filename);\nSTBIDEF int      stbi_is_hdr_from_file(FILE *f);\n#endif // STBI_NO_STDIO\n\n\n// get a VERY brief reason for failure\n// NOT THREADSAFE\nSTBIDEF const char *stbi_failure_reason  (void);\n\n// free the loaded image -- this is just free()\nSTBIDEF void     stbi_image_free      (void *retval_from_stbi_load);\n\n// get image dimensions & components without fully decoding\nSTBIDEF int      stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp);\nSTBIDEF int      stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp);\nSTBIDEF int      stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len);\nSTBIDEF int      stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user);\n\n#ifndef STBI_NO_STDIO\nSTBIDEF int      stbi_info               (char const *filename,     int *x, int *y, int *comp);\nSTBIDEF int      stbi_info_from_file     (FILE *f,                  int *x, int *y, int *comp);\nSTBIDEF int      stbi_is_16_bit          (char const *filename);\nSTBIDEF int      stbi_is_16_bit_from_file(FILE *f);\n#endif\n\n\n\n// for image formats that explicitly notate that they have premultiplied alpha,\n// we just return the colors as stored in the file. set this flag to force\n// unpremultiplication. results are undefined if the unpremultiply overflow.\nSTBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply);\n\n// indicate whether we should process iphone images back to canonical format,\n// or just pass them through \"as-is\"\nSTBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert);\n\n// flip the image vertically, so the first pixel in the output array is the bottom left\nSTBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip);\n\n// ZLIB client - used by PNG, available for other purposes\n\nSTBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen);\nSTBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header);\nSTBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen);\nSTBIDEF int   stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen);\n\nSTBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen);\nSTBIDEF int   stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen);\n\n\n#ifdef __cplusplus\n}\n#endif\n\n//\n//\n////   end header file   /////////////////////////////////////////////////////\n#endif // STBI_INCLUDE_STB_IMAGE_H\n\n#ifdef STB_IMAGE_IMPLEMENTATION\n\n#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \\\n  || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \\\n  || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \\\n  || defined(STBI_ONLY_ZLIB)\n#ifndef STBI_ONLY_JPEG\n#define STBI_NO_JPEG\n#endif\n#ifndef STBI_ONLY_PNG\n#define STBI_NO_PNG\n#endif\n#ifndef STBI_ONLY_BMP\n#define STBI_NO_BMP\n#endif\n#ifndef STBI_ONLY_PSD\n#define STBI_NO_PSD\n#endif\n#ifndef STBI_ONLY_TGA\n#define STBI_NO_TGA\n#endif\n#ifndef STBI_ONLY_GIF\n#define STBI_NO_GIF\n#endif\n#ifndef STBI_ONLY_HDR\n#define STBI_NO_HDR\n#endif\n#ifndef STBI_ONLY_PIC\n#define STBI_NO_PIC\n#endif\n#ifndef STBI_ONLY_PNM\n#define STBI_NO_PNM\n#endif\n#endif\n\n#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB)\n#define STBI_NO_ZLIB\n#endif\n\n\n#include <stdarg.h>\n#include <stddef.h> // ptrdiff_t on osx\n#include <stdlib.h>\n#include <string.h>\n#include <limits.h>\n\n#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR)\n#include <math.h>  // ldexp, pow\n#endif\n\n#ifndef STBI_NO_STDIO\n#include <stdio.h>\n#endif\n\n#ifndef STBI_ASSERT\n#include <assert.h>\n#define STBI_ASSERT(x) assert(x)\n#endif\n\n\n#ifndef _MSC_VER\n#ifdef __cplusplus\n#define stbi_inline inline\n#else\n#define stbi_inline\n#endif\n#else\n#define stbi_inline __forceinline\n#endif\n\n\n#ifdef _MSC_VER\ntypedef unsigned short stbi__uint16;\ntypedef   signed short stbi__int16;\ntypedef unsigned int   stbi__uint32;\ntypedef   signed int   stbi__int32;\n#else\n#include <stdint.h>\ntypedef uint16_t stbi__uint16;\ntypedef int16_t  stbi__int16;\ntypedef uint32_t stbi__uint32;\ntypedef int32_t  stbi__int32;\n#endif\n\n// should produce compiler error if size is wrong\ntypedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1];\n\n#ifdef _MSC_VER\n#define STBI_NOTUSED(v)  (void)(v)\n#else\n#define STBI_NOTUSED(v)  (void)sizeof(v)\n#endif\n\n#ifdef _MSC_VER\n#define STBI_HAS_LROTL\n#endif\n\n#ifdef STBI_HAS_LROTL\n#define stbi_lrot(x,y)  _lrotl(x,y)\n#else\n#define stbi_lrot(x,y)  (((x) << (y)) | ((x) >> (32 - (y))))\n#endif\n\n#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED))\n// ok\n#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED)\n// ok\n#else\n#error \"Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED).\"\n#endif\n\n#ifndef STBI_MALLOC\n#define STBI_MALLOC(sz)           malloc(sz)\n#define STBI_REALLOC(p,newsz)     realloc(p,newsz)\n#define STBI_FREE(p)              free(p)\n#endif\n\n#ifndef STBI_REALLOC_SIZED\n#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz)\n#endif\n\n// x86/x64 detection\n#if defined(__x86_64__) || defined(_M_X64)\n#define STBI__X64_TARGET\n#elif defined(__i386) || defined(_M_IX86)\n#define STBI__X86_TARGET\n#endif\n\n#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD)\n// gcc doesn't support sse2 intrinsics unless you compile with -msse2,\n// which in turn means it gets to use SSE2 everywhere. This is unfortunate,\n// but previous attempts to provide the SSE2 functions with runtime\n// detection caused numerous issues. The way architecture extensions are\n// exposed in GCC/Clang is, sadly, not really suited for one-file libs.\n// New behavior: if compiled with -msse2, we use SSE2 without any\n// detection; if not, we don't use it at all.\n#define STBI_NO_SIMD\n#endif\n\n#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD)\n// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET\n//\n// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the\n// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant.\n// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not\n// simultaneously enabling \"-mstackrealign\".\n//\n// See https://github.com/nothings/stb/issues/81 for more information.\n//\n// So default to no SSE2 on 32-bit MinGW. If you've read this far and added\n// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2.\n#define STBI_NO_SIMD\n#endif\n\n#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET))\n#define STBI_SSE2\n#include <emmintrin.h>\n\n#ifdef _MSC_VER\n\n#if _MSC_VER >= 1400  // not VC6\n#include <intrin.h> // __cpuid\nstatic int stbi__cpuid3(void) {\n    int info[4];\n    __cpuid(info,1);\n    return info[3];\n}\n#else\nstatic int stbi__cpuid3(void) {\n    int res;\n    __asm {\n        mov  eax,1\n        cpuid\n        mov  res,edx\n    }\n    return res;\n}\n#endif\n\n#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name\n\nstatic int stbi__sse2_available(void) {\n    int info3 = stbi__cpuid3();\n    return ((info3 >> 26) & 1) != 0;\n}\n#else // assume GCC-style if not VC++\n#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16)))\n\nstatic int stbi__sse2_available(void) {\n    // If we're even attempting to compile this on GCC/Clang, that means\n    // -msse2 is on, which means the compiler is allowed to use SSE2\n    // instructions at will, and so are we.\n    return 1;\n}\n#endif\n#endif\n\n// ARM NEON\n#if defined(STBI_NO_SIMD) && defined(STBI_NEON)\n#undef STBI_NEON\n#endif\n\n#ifdef STBI_NEON\n#include <arm_neon.h>\n// assume GCC or Clang on ARM targets\n#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16)))\n#endif\n\n#ifndef STBI_SIMD_ALIGN\n#define STBI_SIMD_ALIGN(type, name) type name\n#endif\n\n///////////////////////////////////////////////\n//\n//  stbi__context struct and start_xxx functions\n\n// stbi__context structure is our basic context used by all images, so it\n// contains all the IO context, plus some basic image information\ntypedef struct {\n    stbi__uint32 img_x, img_y;\n    int img_n, img_out_n;\n\n    stbi_io_callbacks io;\n    void *io_user_data;\n\n    int read_from_callbacks;\n    int buflen;\n    stbi_uc buffer_start[128];\n\n    stbi_uc *img_buffer, *img_buffer_end;\n    stbi_uc *img_buffer_original, *img_buffer_original_end;\n} stbi__context;\n\n\nstatic void stbi__refill_buffer(stbi__context *s);\n\n// initialize a memory-decode context\nstatic void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) {\n    s->io.read = NULL;\n    s->read_from_callbacks = 0;\n    s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer;\n    s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len;\n}\n\n// initialize a callback-based context\nstatic void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) {\n    s->io = *c;\n    s->io_user_data = user;\n    s->buflen = sizeof(s->buffer_start);\n    s->read_from_callbacks = 1;\n    s->img_buffer_original = s->buffer_start;\n    stbi__refill_buffer(s);\n    s->img_buffer_original_end = s->img_buffer_end;\n}\n\n#ifndef STBI_NO_STDIO\n\nstatic int stbi__stdio_read(void *user, char *data, int size) {\n    return (int) fread(data,1,size,(FILE*) user);\n}\n\nstatic void stbi__stdio_skip(void *user, int n) {\n    fseek((FILE*) user, n, SEEK_CUR);\n}\n\nstatic int stbi__stdio_eof(void *user) {\n    return feof((FILE*) user);\n}\n\nstatic stbi_io_callbacks stbi__stdio_callbacks = {\n    stbi__stdio_read,\n    stbi__stdio_skip,\n    stbi__stdio_eof,\n};\n\nstatic void stbi__start_file(stbi__context *s, FILE *f) {\n    stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f);\n}\n\n//static void stop_file(stbi__context *s) { }\n\n#endif // !STBI_NO_STDIO\n\nstatic void stbi__rewind(stbi__context *s) {\n    // conceptually rewind SHOULD rewind to the beginning of the stream,\n    // but we just rewind to the beginning of the initial buffer, because\n    // we only use it after doing 'test', which only ever looks at at most 92 bytes\n    s->img_buffer = s->img_buffer_original;\n    s->img_buffer_end = s->img_buffer_original_end;\n}\n\nenum {\n    STBI_ORDER_RGB,\n    STBI_ORDER_BGR\n};\n\ntypedef struct {\n    int bits_per_channel;\n    int num_channels;\n    int channel_order;\n} stbi__result_info;\n\n#ifndef STBI_NO_JPEG\nstatic int      stbi__jpeg_test(stbi__context *s);\nstatic void    *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);\nstatic int      stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp);\n#endif\n\n#ifndef STBI_NO_PNG\nstatic int      stbi__png_test(stbi__context *s);\nstatic void    *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);\nstatic int      stbi__png_info(stbi__context *s, int *x, int *y, int *comp);\nstatic int      stbi__png_is16(stbi__context *s);\n#endif\n\n#ifndef STBI_NO_BMP\nstatic int      stbi__bmp_test(stbi__context *s);\nstatic void    *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);\nstatic int      stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp);\n#endif\n\n#ifndef STBI_NO_TGA\nstatic int      stbi__tga_test(stbi__context *s);\nstatic void    *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);\nstatic int      stbi__tga_info(stbi__context *s, int *x, int *y, int *comp);\n#endif\n\n#ifndef STBI_NO_PSD\nstatic int      stbi__psd_test(stbi__context *s);\nstatic void    *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc);\nstatic int      stbi__psd_info(stbi__context *s, int *x, int *y, int *comp);\nstatic int      stbi__psd_is16(stbi__context *s);\n#endif\n\n#ifndef STBI_NO_HDR\nstatic int      stbi__hdr_test(stbi__context *s);\nstatic float   *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);\nstatic int      stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp);\n#endif\n\n#ifndef STBI_NO_PIC\nstatic int      stbi__pic_test(stbi__context *s);\nstatic void    *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);\nstatic int      stbi__pic_info(stbi__context *s, int *x, int *y, int *comp);\n#endif\n\n#ifndef STBI_NO_GIF\nstatic int      stbi__gif_test(stbi__context *s);\nstatic void    *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);\nstatic void    *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp);\nstatic int      stbi__gif_info(stbi__context *s, int *x, int *y, int *comp);\n#endif\n\n#ifndef STBI_NO_PNM\nstatic int      stbi__pnm_test(stbi__context *s);\nstatic void    *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);\nstatic int      stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp);\n#endif\n\n// this is not threadsafe\nstatic const char *stbi__g_failure_reason;\n\nSTBIDEF const char *stbi_failure_reason(void) {\n    return stbi__g_failure_reason;\n}\n\nstatic int stbi__err(const char *str) {\n    stbi__g_failure_reason = str;\n    return 0;\n}\n\nstatic void *stbi__malloc(size_t size) {\n    return STBI_MALLOC(size);\n}\n\n// stb_image uses ints pervasively, including for offset calculations.\n// therefore the largest decoded image size we can support with the\n// current code, even on 64-bit targets, is INT_MAX. this is not a\n// significant limitation for the intended use case.\n//\n// we do, however, need to make sure our size calculations don't\n// overflow. hence a few helper functions for size calculations that\n// multiply integers together, making sure that they're non-negative\n// and no overflow occurs.\n\n// return 1 if the sum is valid, 0 on overflow.\n// negative terms are considered invalid.\nstatic int stbi__addsizes_valid(int a, int b) {\n    if (b < 0) return 0;\n    // now 0 <= b <= INT_MAX, hence also\n    // 0 <= INT_MAX - b <= INTMAX.\n    // And \"a + b <= INT_MAX\" (which might overflow) is the\n    // same as a <= INT_MAX - b (no overflow)\n    return a <= INT_MAX - b;\n}\n\n// returns 1 if the product is valid, 0 on overflow.\n// negative factors are considered invalid.\nstatic int stbi__mul2sizes_valid(int a, int b) {\n    if (a < 0 || b < 0) return 0;\n    if (b == 0) return 1; // mul-by-0 is always safe\n    // portable way to check for no overflows in a*b\n    return a <= INT_MAX/b;\n}\n\n// returns 1 if \"a*b + add\" has no negative terms/factors and doesn't overflow\nstatic int stbi__mad2sizes_valid(int a, int b, int add) {\n    return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add);\n}\n\n// returns 1 if \"a*b*c + add\" has no negative terms/factors and doesn't overflow\nstatic int stbi__mad3sizes_valid(int a, int b, int c, int add) {\n    return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) &&\n           stbi__addsizes_valid(a*b*c, add);\n}\n\n// returns 1 if \"a*b*c*d + add\" has no negative terms/factors and doesn't overflow\n#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR)\nstatic int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) {\n    return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) &&\n           stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add);\n}\n#endif\n\n// mallocs with size overflow checking\nstatic void *stbi__malloc_mad2(int a, int b, int add) {\n    if (!stbi__mad2sizes_valid(a, b, add)) return NULL;\n    return stbi__malloc(a*b + add);\n}\n\nstatic void *stbi__malloc_mad3(int a, int b, int c, int add) {\n    if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL;\n    return stbi__malloc(a*b*c + add);\n}\n\n#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR)\nstatic void *stbi__malloc_mad4(int a, int b, int c, int d, int add) {\n    if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL;\n    return stbi__malloc(a*b*c*d + add);\n}\n#endif\n\n// stbi__err - error\n// stbi__errpf - error returning pointer to float\n// stbi__errpuc - error returning pointer to unsigned char\n\n#ifdef STBI_NO_FAILURE_STRINGS\n#define stbi__err(x,y)  0\n#elif defined(STBI_FAILURE_USERMSG)\n#define stbi__err(x,y)  stbi__err(y)\n#else\n#define stbi__err(x,y)  stbi__err(x)\n#endif\n\n#define stbi__errpf(x,y)   ((float *)(size_t) (stbi__err(x,y)?NULL:NULL))\n#define stbi__errpuc(x,y)  ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL))\n\nSTBIDEF void stbi_image_free(void *retval_from_stbi_load) {\n    STBI_FREE(retval_from_stbi_load);\n}\n\n#ifndef STBI_NO_LINEAR\nstatic float   *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp);\n#endif\n\n#ifndef STBI_NO_HDR\nstatic stbi_uc *stbi__hdr_to_ldr(float   *data, int x, int y, int comp);\n#endif\n\nstatic int stbi__vertically_flip_on_load = 0;\n\nSTBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) {\n    stbi__vertically_flip_on_load = flag_true_if_should_flip;\n}\n\nstatic void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) {\n    memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields\n    ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed\n    ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order\n    ri->num_channels = 0;\n\n#ifndef STBI_NO_JPEG\n    if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri);\n#endif\n#ifndef STBI_NO_PNG\n    if (stbi__png_test(s))  return stbi__png_load(s,x,y,comp,req_comp, ri);\n#endif\n#ifndef STBI_NO_BMP\n    if (stbi__bmp_test(s))  return stbi__bmp_load(s,x,y,comp,req_comp, ri);\n#endif\n#ifndef STBI_NO_GIF\n    if (stbi__gif_test(s))  return stbi__gif_load(s,x,y,comp,req_comp, ri);\n#endif\n#ifndef STBI_NO_PSD\n    if (stbi__psd_test(s))  return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc);\n#endif\n#ifndef STBI_NO_PIC\n    if (stbi__pic_test(s))  return stbi__pic_load(s,x,y,comp,req_comp, ri);\n#endif\n#ifndef STBI_NO_PNM\n    if (stbi__pnm_test(s))  return stbi__pnm_load(s,x,y,comp,req_comp, ri);\n#endif\n\n#ifndef STBI_NO_HDR\n    if (stbi__hdr_test(s)) {\n        float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri);\n        return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp);\n    }\n#endif\n\n#ifndef STBI_NO_TGA\n    // test tga last because it's a crappy test!\n    if (stbi__tga_test(s))\n        return stbi__tga_load(s,x,y,comp,req_comp, ri);\n#endif\n\n    return stbi__errpuc(\"unknown image type\", \"Image not of any known type, or corrupt\");\n}\n\nstatic stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) {\n    int i;\n    int img_len = w * h * channels;\n    stbi_uc *reduced;\n\n    reduced = (stbi_uc *) stbi__malloc(img_len);\n    if (reduced == NULL) return stbi__errpuc(\"outofmem\", \"Out of memory\");\n\n    for (i = 0; i < img_len; ++i)\n        reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling\n\n    STBI_FREE(orig);\n    return reduced;\n}\n\nstatic stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) {\n    int i;\n    int img_len = w * h * channels;\n    stbi__uint16 *enlarged;\n\n    enlarged = (stbi__uint16 *) stbi__malloc(img_len*2);\n    if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc(\"outofmem\", \"Out of memory\");\n\n    for (i = 0; i < img_len; ++i)\n        enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff\n\n    STBI_FREE(orig);\n    return enlarged;\n}\n\nstatic void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) {\n    int row;\n    size_t bytes_per_row = (size_t)w * bytes_per_pixel;\n    stbi_uc temp[2048];\n    stbi_uc *bytes = (stbi_uc *)image;\n\n    for (row = 0; row < (h>>1); row++) {\n        stbi_uc *row0 = bytes + row*bytes_per_row;\n        stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row;\n        // swap row0 with row1\n        size_t bytes_left = bytes_per_row;\n        while (bytes_left) {\n            size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp);\n            memcpy(temp, row0, bytes_copy);\n            memcpy(row0, row1, bytes_copy);\n            memcpy(row1, temp, bytes_copy);\n            row0 += bytes_copy;\n            row1 += bytes_copy;\n            bytes_left -= bytes_copy;\n        }\n    }\n}\n\nstatic void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) {\n    int slice;\n    int slice_size = w * h * bytes_per_pixel;\n\n    stbi_uc *bytes = (stbi_uc *)image;\n    for (slice = 0; slice < z; ++slice) {\n        stbi__vertical_flip(bytes, w, h, bytes_per_pixel);\n        bytes += slice_size;\n    }\n}\n\nstatic unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) {\n    stbi__result_info ri;\n    void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8);\n\n    if (result == NULL)\n        return NULL;\n\n    if (ri.bits_per_channel != 8) {\n        STBI_ASSERT(ri.bits_per_channel == 16);\n        result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp);\n        ri.bits_per_channel = 8;\n    }\n\n    // @TODO: move stbi__convert_format to here\n\n    if (stbi__vertically_flip_on_load) {\n        int channels = req_comp ? req_comp : *comp;\n        stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc));\n    }\n\n    return (unsigned char *) result;\n}\n\nstatic stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) {\n    stbi__result_info ri;\n    void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16);\n\n    if (result == NULL)\n        return NULL;\n\n    if (ri.bits_per_channel != 16) {\n        STBI_ASSERT(ri.bits_per_channel == 8);\n        result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp);\n        ri.bits_per_channel = 16;\n    }\n\n    // @TODO: move stbi__convert_format16 to here\n    // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision\n\n    if (stbi__vertically_flip_on_load) {\n        int channels = req_comp ? req_comp : *comp;\n        stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16));\n    }\n\n    return (stbi__uint16 *) result;\n}\n\n#if !defined(STBI_NO_HDR) || !defined(STBI_NO_LINEAR)\nstatic void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) {\n    if (stbi__vertically_flip_on_load && result != NULL) {\n        int channels = req_comp ? req_comp : *comp;\n        stbi__vertical_flip(result, *x, *y, channels * sizeof(float));\n    }\n}\n#endif\n\n#ifndef STBI_NO_STDIO\n\nstatic FILE *stbi__fopen(char const *filename, char const *mode) {\n    FILE *f;\n#if defined(_MSC_VER) && _MSC_VER >= 1400\n    if (0 != fopen_s(&f, filename, mode))\n        f=0;\n#else\n    f = fopen(filename, mode);\n#endif\n    return f;\n}\n\n\nSTBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) {\n    FILE *f = stbi__fopen(filename, \"rb\");\n    unsigned char *result;\n    if (!f) return stbi__errpuc(\"can't fopen\", \"Unable to open file\");\n    result = stbi_load_from_file(f,x,y,comp,req_comp);\n    fclose(f);\n    return result;\n}\n\nSTBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) {\n    unsigned char *result;\n    stbi__context s;\n    stbi__start_file(&s,f);\n    result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp);\n    if (result) {\n        // need to 'unget' all the characters in the IO buffer\n        fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR);\n    }\n    return result;\n}\n\nSTBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) {\n    stbi__uint16 *result;\n    stbi__context s;\n    stbi__start_file(&s,f);\n    result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp);\n    if (result) {\n        // need to 'unget' all the characters in the IO buffer\n        fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR);\n    }\n    return result;\n}\n\nSTBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) {\n    FILE *f = stbi__fopen(filename, \"rb\");\n    stbi__uint16 *result;\n    if (!f) return (stbi_us *) stbi__errpuc(\"can't fopen\", \"Unable to open file\");\n    result = stbi_load_from_file_16(f,x,y,comp,req_comp);\n    fclose(f);\n    return result;\n}\n\n\n#endif //!STBI_NO_STDIO\n\nSTBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) {\n    stbi__context s;\n    stbi__start_mem(&s,buffer,len);\n    return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels);\n}\n\nSTBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) {\n    stbi__context s;\n    stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user);\n    return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels);\n}\n\nSTBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) {\n    stbi__context s;\n    stbi__start_mem(&s,buffer,len);\n    return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp);\n}\n\nSTBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) {\n    stbi__context s;\n    stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user);\n    return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp);\n}\n\n#ifndef STBI_NO_GIF\nSTBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) {\n    unsigned char *result;\n    stbi__context s;\n    stbi__start_mem(&s,buffer,len);\n\n    result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp);\n    if (stbi__vertically_flip_on_load) {\n        stbi__vertical_flip_slices( result, *x, *y, *z, *comp );\n    }\n\n    return result;\n}\n#endif\n\n#ifndef STBI_NO_LINEAR\nstatic float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) {\n    unsigned char *data;\n#ifndef STBI_NO_HDR\n    if (stbi__hdr_test(s)) {\n        stbi__result_info ri;\n        float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri);\n        if (hdr_data)\n            stbi__float_postprocess(hdr_data,x,y,comp,req_comp);\n        return hdr_data;\n    }\n#endif\n    data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp);\n    if (data)\n        return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp);\n    return stbi__errpf(\"unknown image type\", \"Image not of any known type, or corrupt\");\n}\n\nSTBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) {\n    stbi__context s;\n    stbi__start_mem(&s,buffer,len);\n    return stbi__loadf_main(&s,x,y,comp,req_comp);\n}\n\nSTBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) {\n    stbi__context s;\n    stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user);\n    return stbi__loadf_main(&s,x,y,comp,req_comp);\n}\n\n#ifndef STBI_NO_STDIO\nSTBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) {\n    float *result;\n    FILE *f = stbi__fopen(filename, \"rb\");\n    if (!f) return stbi__errpf(\"can't fopen\", \"Unable to open file\");\n    result = stbi_loadf_from_file(f,x,y,comp,req_comp);\n    fclose(f);\n    return result;\n}\n\nSTBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) {\n    stbi__context s;\n    stbi__start_file(&s,f);\n    return stbi__loadf_main(&s,x,y,comp,req_comp);\n}\n#endif // !STBI_NO_STDIO\n\n#endif // !STBI_NO_LINEAR\n\n// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is\n// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always\n// reports false!\n\nSTBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) {\n#ifndef STBI_NO_HDR\n    stbi__context s;\n    stbi__start_mem(&s,buffer,len);\n    return stbi__hdr_test(&s);\n#else\n    STBI_NOTUSED(buffer);\n    STBI_NOTUSED(len);\n    return 0;\n#endif\n}\n\n#ifndef STBI_NO_STDIO\nSTBIDEF int      stbi_is_hdr          (char const *filename) {\n    FILE *f = stbi__fopen(filename, \"rb\");\n    int result=0;\n    if (f) {\n        result = stbi_is_hdr_from_file(f);\n        fclose(f);\n    }\n    return result;\n}\n\nSTBIDEF int stbi_is_hdr_from_file(FILE *f) {\n#ifndef STBI_NO_HDR\n    long pos = ftell(f);\n    int res;\n    stbi__context s;\n    stbi__start_file(&s,f);\n    res = stbi__hdr_test(&s);\n    fseek(f, pos, SEEK_SET);\n    return res;\n#else\n    STBI_NOTUSED(f);\n    return 0;\n#endif\n}\n#endif // !STBI_NO_STDIO\n\nSTBIDEF int      stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) {\n#ifndef STBI_NO_HDR\n    stbi__context s;\n    stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user);\n    return stbi__hdr_test(&s);\n#else\n    STBI_NOTUSED(clbk);\n    STBI_NOTUSED(user);\n    return 0;\n#endif\n}\n\n#ifndef STBI_NO_LINEAR\nstatic float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f;\n\nSTBIDEF void   stbi_ldr_to_hdr_gamma(float gamma) {\n    stbi__l2h_gamma = gamma;\n}\nSTBIDEF void   stbi_ldr_to_hdr_scale(float scale) {\n    stbi__l2h_scale = scale;\n}\n#endif\n\nstatic float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f;\n\nSTBIDEF void   stbi_hdr_to_ldr_gamma(float gamma) {\n    stbi__h2l_gamma_i = 1/gamma;\n}\nSTBIDEF void   stbi_hdr_to_ldr_scale(float scale) {\n    stbi__h2l_scale_i = 1/scale;\n}\n\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// Common code used by all image loaders\n//\n\nenum {\n    STBI__SCAN_load=0,\n    STBI__SCAN_type,\n    STBI__SCAN_header\n};\n\nstatic void stbi__refill_buffer(stbi__context *s) {\n    int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen);\n    if (n == 0) {\n        // at end of file, treat same as if from memory, but need to handle case\n        // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file\n        s->read_from_callbacks = 0;\n        s->img_buffer = s->buffer_start;\n        s->img_buffer_end = s->buffer_start+1;\n        *s->img_buffer = 0;\n    } else {\n        s->img_buffer = s->buffer_start;\n        s->img_buffer_end = s->buffer_start + n;\n    }\n}\n\nstbi_inline static stbi_uc stbi__get8(stbi__context *s) {\n    if (s->img_buffer < s->img_buffer_end)\n        return *s->img_buffer++;\n    if (s->read_from_callbacks) {\n        stbi__refill_buffer(s);\n        return *s->img_buffer++;\n    }\n    return 0;\n}\n\nstbi_inline static int stbi__at_eof(stbi__context *s) {\n    if (s->io.read) {\n        if (!(s->io.eof)(s->io_user_data)) return 0;\n        // if feof() is true, check if buffer = end\n        // special case: we've only got the special 0 character at the end\n        if (s->read_from_callbacks == 0) return 1;\n    }\n\n    return s->img_buffer >= s->img_buffer_end;\n}\n\nstatic void stbi__skip(stbi__context *s, int n) {\n    if (n < 0) {\n        s->img_buffer = s->img_buffer_end;\n        return;\n    }\n    if (s->io.read) {\n        int blen = (int) (s->img_buffer_end - s->img_buffer);\n        if (blen < n) {\n            s->img_buffer = s->img_buffer_end;\n            (s->io.skip)(s->io_user_data, n - blen);\n            return;\n        }\n    }\n    s->img_buffer += n;\n}\n\nstatic int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) {\n    if (s->io.read) {\n        int blen = (int) (s->img_buffer_end - s->img_buffer);\n        if (blen < n) {\n            int res, count;\n\n            memcpy(buffer, s->img_buffer, blen);\n\n            count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen);\n            res = (count == (n-blen));\n            s->img_buffer = s->img_buffer_end;\n            return res;\n        }\n    }\n\n    if (s->img_buffer+n <= s->img_buffer_end) {\n        memcpy(buffer, s->img_buffer, n);\n        s->img_buffer += n;\n        return 1;\n    } else\n        return 0;\n}\n\nstatic int stbi__get16be(stbi__context *s) {\n    int z = stbi__get8(s);\n    return (z << 8) + stbi__get8(s);\n}\n\nstatic stbi__uint32 stbi__get32be(stbi__context *s) {\n    stbi__uint32 z = stbi__get16be(s);\n    return (z << 16) + stbi__get16be(s);\n}\n\n#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF)\n// nothing\n#else\nstatic int stbi__get16le(stbi__context *s) {\n    int z = stbi__get8(s);\n    return z + (stbi__get8(s) << 8);\n}\n#endif\n\n#ifndef STBI_NO_BMP\nstatic stbi__uint32 stbi__get32le(stbi__context *s) {\n    stbi__uint32 z = stbi__get16le(s);\n    return z + (stbi__get16le(s) << 16);\n}\n#endif\n\n#define STBI__BYTECAST(x)  ((stbi_uc) ((x) & 255))  // truncate int to byte without warnings\n\n\n//////////////////////////////////////////////////////////////////////////////\n//\n//  generic converter from built-in img_n to req_comp\n//    individual types do this automatically as much as possible (e.g. jpeg\n//    does all cases internally since it needs to colorspace convert anyway,\n//    and it never has alpha, so very few cases ). png can automatically\n//    interleave an alpha=255 channel, but falls back to this for other cases\n//\n//  assume data buffer is malloced, so malloc a new one and free that one\n//  only failure mode is malloc failing\n\nstatic stbi_uc stbi__compute_y(int r, int g, int b) {\n    return (stbi_uc) (((r*77) + (g*150) +  (29*b)) >> 8);\n}\n\nstatic unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) {\n    int i,j;\n    unsigned char *good;\n\n    if (req_comp == img_n) return data;\n    STBI_ASSERT(req_comp >= 1 && req_comp <= 4);\n\n    good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0);\n    if (good == NULL) {\n        STBI_FREE(data);\n        return stbi__errpuc(\"outofmem\", \"Out of memory\");\n    }\n\n    for (j=0; j < (int) y; ++j) {\n        unsigned char *src  = data + j * x * img_n   ;\n        unsigned char *dest = good + j * x * req_comp;\n\n#define STBI__COMBO(a,b)  ((a)*8+(b))\n#define STBI__CASE(a,b)   case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b)\n        // convert source image with img_n components to one with req_comp components;\n        // avoid switch per pixel, so use switch per scanline and massive macros\n        switch (STBI__COMBO(img_n, req_comp)) {\n            STBI__CASE(1,2) {\n                dest[0]=src[0], dest[1]=255;\n            }\n            break;\n            STBI__CASE(1,3) {\n                dest[0]=dest[1]=dest[2]=src[0];\n            }\n            break;\n            STBI__CASE(1,4) {\n                dest[0]=dest[1]=dest[2]=src[0], dest[3]=255;\n            }\n            break;\n            STBI__CASE(2,1) {\n                dest[0]=src[0];\n            }\n            break;\n            STBI__CASE(2,3) {\n                dest[0]=dest[1]=dest[2]=src[0];\n            }\n            break;\n            STBI__CASE(2,4) {\n                dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1];\n            }\n            break;\n            STBI__CASE(3,4) {\n                dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255;\n            }\n            break;\n            STBI__CASE(3,1) {\n                dest[0]=stbi__compute_y(src[0],src[1],src[2]);\n            }\n            break;\n            STBI__CASE(3,2) {\n                dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = 255;\n            }\n            break;\n            STBI__CASE(4,1) {\n                dest[0]=stbi__compute_y(src[0],src[1],src[2]);\n            }\n            break;\n            STBI__CASE(4,2) {\n                dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3];\n            }\n            break;\n            STBI__CASE(4,3) {\n                dest[0]=src[0],dest[1]=src[1],dest[2]=src[2];\n            }\n            break;\n        default:\n            STBI_ASSERT(0);\n        }\n#undef STBI__CASE\n    }\n\n    STBI_FREE(data);\n    return good;\n}\n\nstatic stbi__uint16 stbi__compute_y_16(int r, int g, int b) {\n    return (stbi__uint16) (((r*77) + (g*150) +  (29*b)) >> 8);\n}\n\nstatic stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) {\n    int i,j;\n    stbi__uint16 *good;\n\n    if (req_comp == img_n) return data;\n    STBI_ASSERT(req_comp >= 1 && req_comp <= 4);\n\n    good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2);\n    if (good == NULL) {\n        STBI_FREE(data);\n        return (stbi__uint16 *) stbi__errpuc(\"outofmem\", \"Out of memory\");\n    }\n\n    for (j=0; j < (int) y; ++j) {\n        stbi__uint16 *src  = data + j * x * img_n   ;\n        stbi__uint16 *dest = good + j * x * req_comp;\n\n#define STBI__COMBO(a,b)  ((a)*8+(b))\n#define STBI__CASE(a,b)   case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b)\n        // convert source image with img_n components to one with req_comp components;\n        // avoid switch per pixel, so use switch per scanline and massive macros\n        switch (STBI__COMBO(img_n, req_comp)) {\n            STBI__CASE(1,2) {\n                dest[0]=src[0], dest[1]=0xffff;\n            }\n            break;\n            STBI__CASE(1,3) {\n                dest[0]=dest[1]=dest[2]=src[0];\n            }\n            break;\n            STBI__CASE(1,4) {\n                dest[0]=dest[1]=dest[2]=src[0], dest[3]=0xffff;\n            }\n            break;\n            STBI__CASE(2,1) {\n                dest[0]=src[0];\n            }\n            break;\n            STBI__CASE(2,3) {\n                dest[0]=dest[1]=dest[2]=src[0];\n            }\n            break;\n            STBI__CASE(2,4) {\n                dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1];\n            }\n            break;\n            STBI__CASE(3,4) {\n                dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=0xffff;\n            }\n            break;\n            STBI__CASE(3,1) {\n                dest[0]=stbi__compute_y_16(src[0],src[1],src[2]);\n            }\n            break;\n            STBI__CASE(3,2) {\n                dest[0]=stbi__compute_y_16(src[0],src[1],src[2]), dest[1] = 0xffff;\n            }\n            break;\n            STBI__CASE(4,1) {\n                dest[0]=stbi__compute_y_16(src[0],src[1],src[2]);\n            }\n            break;\n            STBI__CASE(4,2) {\n                dest[0]=stbi__compute_y_16(src[0],src[1],src[2]), dest[1] = src[3];\n            }\n            break;\n            STBI__CASE(4,3) {\n                dest[0]=src[0],dest[1]=src[1],dest[2]=src[2];\n            }\n            break;\n        default:\n            STBI_ASSERT(0);\n        }\n#undef STBI__CASE\n    }\n\n    STBI_FREE(data);\n    return good;\n}\n\n#ifndef STBI_NO_LINEAR\nstatic float   *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) {\n    int i,k,n;\n    float *output;\n    if (!data) return NULL;\n    output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0);\n    if (output == NULL) {\n        STBI_FREE(data);\n        return stbi__errpf(\"outofmem\", \"Out of memory\");\n    }\n    // compute number of non-alpha components\n    if (comp & 1) n = comp;\n    else n = comp-1;\n    for (i=0; i < x*y; ++i) {\n        for (k=0; k < n; ++k) {\n            output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale);\n        }\n        if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f;\n    }\n    STBI_FREE(data);\n    return output;\n}\n#endif\n\n#ifndef STBI_NO_HDR\n#define stbi__float2int(x)   ((int) (x))\nstatic stbi_uc *stbi__hdr_to_ldr(float   *data, int x, int y, int comp) {\n    int i,k,n;\n    stbi_uc *output;\n    if (!data) return NULL;\n    output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0);\n    if (output == NULL) {\n        STBI_FREE(data);\n        return stbi__errpuc(\"outofmem\", \"Out of memory\");\n    }\n    // compute number of non-alpha components\n    if (comp & 1) n = comp;\n    else n = comp-1;\n    for (i=0; i < x*y; ++i) {\n        for (k=0; k < n; ++k) {\n            float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f;\n            if (z < 0) z = 0;\n            if (z > 255) z = 255;\n            output[i*comp + k] = (stbi_uc) stbi__float2int(z);\n        }\n        if (k < comp) {\n            float z = data[i*comp+k] * 255 + 0.5f;\n            if (z < 0) z = 0;\n            if (z > 255) z = 255;\n            output[i*comp + k] = (stbi_uc) stbi__float2int(z);\n        }\n    }\n    STBI_FREE(data);\n    return output;\n}\n#endif\n\n//////////////////////////////////////////////////////////////////////////////\n//\n//  \"baseline\" JPEG/JFIF decoder\n//\n//    simple implementation\n//      - doesn't support delayed output of y-dimension\n//      - simple interface (only one output format: 8-bit interleaved RGB)\n//      - doesn't try to recover corrupt jpegs\n//      - doesn't allow partial loading, loading multiple at once\n//      - still fast on x86 (copying globals into locals doesn't help x86)\n//      - allocates lots of intermediate memory (full size of all components)\n//        - non-interleaved case requires this anyway\n//        - allows good upsampling (see next)\n//    high-quality\n//      - upsampled channels are bilinearly interpolated, even across blocks\n//      - quality integer IDCT derived from IJG's 'slow'\n//    performance\n//      - fast huffman; reasonable integer IDCT\n//      - some SIMD kernels for common paths on targets with SSE2/NEON\n//      - uses a lot of intermediate memory, could cache poorly\n\n#ifndef STBI_NO_JPEG\n\n// huffman decoding acceleration\n#define FAST_BITS   9  // larger handles more cases; smaller stomps less cache\n\ntypedef struct {\n    stbi_uc  fast[1 << FAST_BITS];\n    // weirdly, repacking this into AoS is a 10% speed loss, instead of a win\n    stbi__uint16 code[256];\n    stbi_uc  values[256];\n    stbi_uc  size[257];\n    unsigned int maxcode[18];\n    int    delta[17];   // old 'firstsymbol' - old 'firstcode'\n} stbi__huffman;\n\ntypedef struct {\n    stbi__context *s;\n    stbi__huffman huff_dc[4];\n    stbi__huffman huff_ac[4];\n    stbi__uint16 dequant[4][64];\n    stbi__int16 fast_ac[4][1 << FAST_BITS];\n\n// sizes for components, interleaved MCUs\n    int img_h_max, img_v_max;\n    int img_mcu_x, img_mcu_y;\n    int img_mcu_w, img_mcu_h;\n\n// definition of jpeg image component\n    struct {\n        int id;\n        int h,v;\n        int tq;\n        int hd,ha;\n        int dc_pred;\n\n        int x,y,w2,h2;\n        stbi_uc *data;\n        void *raw_data, *raw_coeff;\n        stbi_uc *linebuf;\n        short   *coeff;   // progressive only\n        int      coeff_w, coeff_h; // number of 8x8 coefficient blocks\n    } img_comp[4];\n\n    stbi__uint32   code_buffer; // jpeg entropy-coded buffer\n    int            code_bits;   // number of valid bits\n    unsigned char  marker;      // marker seen while filling entropy buffer\n    int            nomore;      // flag if we saw a marker so must stop\n\n    int            progressive;\n    int            spec_start;\n    int            spec_end;\n    int            succ_high;\n    int            succ_low;\n    int            eob_run;\n    int            jfif;\n    int            app14_color_transform; // Adobe APP14 tag\n    int            rgb;\n\n    int scan_n, order[4];\n    int restart_interval, todo;\n\n// kernels\n    void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]);\n    void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step);\n    stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs);\n} stbi__jpeg;\n\nstatic int stbi__build_huffman(stbi__huffman *h, int *count) {\n    int i,j,k=0;\n    unsigned int code;\n    // build size list for each symbol (from JPEG spec)\n    for (i=0; i < 16; ++i)\n        for (j=0; j < count[i]; ++j)\n            h->size[k++] = (stbi_uc) (i+1);\n    h->size[k] = 0;\n\n    // compute actual symbols (from jpeg spec)\n    code = 0;\n    k = 0;\n    for(j=1; j <= 16; ++j) {\n        // compute delta to add to code to compute symbol id\n        h->delta[j] = k - code;\n        if (h->size[k] == j) {\n            while (h->size[k] == j)\n                h->code[k++] = (stbi__uint16) (code++);\n            if (code-1 >= (1u << j)) return stbi__err(\"bad code lengths\",\"Corrupt JPEG\");\n        }\n        // compute largest code + 1 for this size, preshifted as needed later\n        h->maxcode[j] = code << (16-j);\n        code <<= 1;\n    }\n    h->maxcode[j] = 0xffffffff;\n\n    // build non-spec acceleration table; 255 is flag for not-accelerated\n    memset(h->fast, 255, 1 << FAST_BITS);\n    for (i=0; i < k; ++i) {\n        int s = h->size[i];\n        if (s <= FAST_BITS) {\n            int c = h->code[i] << (FAST_BITS-s);\n            int m = 1 << (FAST_BITS-s);\n            for (j=0; j < m; ++j) {\n                h->fast[c+j] = (stbi_uc) i;\n            }\n        }\n    }\n    return 1;\n}\n\n// build a table that decodes both magnitude and value of small ACs in\n// one go.\nstatic void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) {\n    int i;\n    for (i=0; i < (1 << FAST_BITS); ++i) {\n        stbi_uc fast = h->fast[i];\n        fast_ac[i] = 0;\n        if (fast < 255) {\n            int rs = h->values[fast];\n            int run = (rs >> 4) & 15;\n            int magbits = rs & 15;\n            int len = h->size[fast];\n\n            if (magbits && len + magbits <= FAST_BITS) {\n                // magnitude code followed by receive_extend code\n                int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits);\n                int m = 1 << (magbits - 1);\n                if (k < m) k += (~0U << magbits) + 1;\n                // if the result is small enough, we can fit it in fast_ac table\n                if (k >= -128 && k <= 127)\n                    fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits));\n            }\n        }\n    }\n}\n\nstatic void stbi__grow_buffer_unsafe(stbi__jpeg *j) {\n    do {\n        unsigned int b = j->nomore ? 0 : stbi__get8(j->s);\n        if (b == 0xff) {\n            int c = stbi__get8(j->s);\n            while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes\n            if (c != 0) {\n                j->marker = (unsigned char) c;\n                j->nomore = 1;\n                return;\n            }\n        }\n        j->code_buffer |= b << (24 - j->code_bits);\n        j->code_bits += 8;\n    } while (j->code_bits <= 24);\n}\n\n// (1 << n) - 1\nstatic const stbi__uint32 stbi__bmask[17]= {0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535};\n\n// decode a jpeg huffman value from the bitstream\nstbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) {\n    unsigned int temp;\n    int c,k;\n\n    if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);\n\n    // look at the top FAST_BITS and determine what symbol ID it is,\n    // if the code is <= FAST_BITS\n    c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1);\n    k = h->fast[c];\n    if (k < 255) {\n        int s = h->size[k];\n        if (s > j->code_bits)\n            return -1;\n        j->code_buffer <<= s;\n        j->code_bits -= s;\n        return h->values[k];\n    }\n\n    // naive test is to shift the code_buffer down so k bits are\n    // valid, then test against maxcode. To speed this up, we've\n    // preshifted maxcode left so that it has (16-k) 0s at the\n    // end; in other words, regardless of the number of bits, it\n    // wants to be compared against something shifted to have 16;\n    // that way we don't need to shift inside the loop.\n    temp = j->code_buffer >> 16;\n    for (k=FAST_BITS+1 ; ; ++k)\n        if (temp < h->maxcode[k])\n            break;\n    if (k == 17) {\n        // error! code not found\n        j->code_bits -= 16;\n        return -1;\n    }\n\n    if (k > j->code_bits)\n        return -1;\n\n    // convert the huffman code to the symbol id\n    c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k];\n    STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]);\n\n    // convert the id to a symbol\n    j->code_bits -= k;\n    j->code_buffer <<= k;\n    return h->values[c];\n}\n\n// bias[n] = (-1<<n) + 1\nstatic const int stbi__jbias[16] = {0,-1,-3,-7,-15,-31,-63,-127,-255,-511,-1023,-2047,-4095,-8191,-16383,-32767};\n\n// combined JPEG 'receive' and JPEG 'extend', since baseline\n// always extends everything it receives.\nstbi_inline static int stbi__extend_receive(stbi__jpeg *j, int n) {\n    unsigned int k;\n    int sgn;\n    if (j->code_bits < n) stbi__grow_buffer_unsafe(j);\n\n    sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB\n    k = stbi_lrot(j->code_buffer, n);\n    STBI_ASSERT(n >= 0 && n < (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask)));\n    j->code_buffer = k & ~stbi__bmask[n];\n    k &= stbi__bmask[n];\n    j->code_bits -= n;\n    return k + (stbi__jbias[n] & ~sgn);\n}\n\n// get some unsigned bits\nstbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) {\n    unsigned int k;\n    if (j->code_bits < n) stbi__grow_buffer_unsafe(j);\n    k = stbi_lrot(j->code_buffer, n);\n    j->code_buffer = k & ~stbi__bmask[n];\n    k &= stbi__bmask[n];\n    j->code_bits -= n;\n    return k;\n}\n\nstbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) {\n    unsigned int k;\n    if (j->code_bits < 1) stbi__grow_buffer_unsafe(j);\n    k = j->code_buffer;\n    j->code_buffer <<= 1;\n    --j->code_bits;\n    return k & 0x80000000;\n}\n\n// given a value that's at position X in the zigzag stream,\n// where does it appear in the 8x8 matrix coded as row-major?\nstatic const stbi_uc stbi__jpeg_dezigzag[64+15] = {\n    0,  1,  8, 16,  9,  2,  3, 10,\n    17, 24, 32, 25, 18, 11,  4,  5,\n    12, 19, 26, 33, 40, 48, 41, 34,\n    27, 20, 13,  6,  7, 14, 21, 28,\n    35, 42, 49, 56, 57, 50, 43, 36,\n    29, 22, 15, 23, 30, 37, 44, 51,\n    58, 59, 52, 45, 38, 31, 39, 46,\n    53, 60, 61, 54, 47, 55, 62, 63,\n    // let corrupt input sample past end\n    63, 63, 63, 63, 63, 63, 63, 63,\n    63, 63, 63, 63, 63, 63, 63\n};\n\n// decode one 64-entry block--\nstatic int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) {\n    int diff,dc,k;\n    int t;\n\n    if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);\n    t = stbi__jpeg_huff_decode(j, hdc);\n    if (t < 0) return stbi__err(\"bad huffman code\",\"Corrupt JPEG\");\n\n    // 0 all the ac values now so we can do it 32-bits at a time\n    memset(data,0,64*sizeof(data[0]));\n\n    diff = t ? stbi__extend_receive(j, t) : 0;\n    dc = j->img_comp[b].dc_pred + diff;\n    j->img_comp[b].dc_pred = dc;\n    data[0] = (short) (dc * dequant[0]);\n\n    // decode AC components, see JPEG spec\n    k = 1;\n    do {\n        unsigned int zig;\n        int c,r,s;\n        if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);\n        c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1);\n        r = fac[c];\n        if (r) { // fast-AC path\n            k += (r >> 4) & 15; // run\n            s = r & 15; // combined length\n            j->code_buffer <<= s;\n            j->code_bits -= s;\n            // decode into unzigzag'd location\n            zig = stbi__jpeg_dezigzag[k++];\n            data[zig] = (short) ((r >> 8) * dequant[zig]);\n        } else {\n            int rs = stbi__jpeg_huff_decode(j, hac);\n            if (rs < 0) return stbi__err(\"bad huffman code\",\"Corrupt JPEG\");\n            s = rs & 15;\n            r = rs >> 4;\n            if (s == 0) {\n                if (rs != 0xf0) break; // end block\n                k += 16;\n            } else {\n                k += r;\n                // decode into unzigzag'd location\n                zig = stbi__jpeg_dezigzag[k++];\n                data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]);\n            }\n        }\n    } while (k < 64);\n    return 1;\n}\n\nstatic int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) {\n    int diff,dc;\n    int t;\n    if (j->spec_end != 0) return stbi__err(\"can't merge dc and ac\", \"Corrupt JPEG\");\n\n    if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);\n\n    if (j->succ_high == 0) {\n        // first scan for DC coefficient, must be first\n        memset(data,0,64*sizeof(data[0])); // 0 all the ac values now\n        t = stbi__jpeg_huff_decode(j, hdc);\n        diff = t ? stbi__extend_receive(j, t) : 0;\n\n        dc = j->img_comp[b].dc_pred + diff;\n        j->img_comp[b].dc_pred = dc;\n        data[0] = (short) (dc << j->succ_low);\n    } else {\n        // refinement scan for DC coefficient\n        if (stbi__jpeg_get_bit(j))\n            data[0] += (short) (1 << j->succ_low);\n    }\n    return 1;\n}\n\n// @OPTIMIZE: store non-zigzagged during the decode passes,\n// and only de-zigzag when dequantizing\nstatic int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) {\n    int k;\n    if (j->spec_start == 0) return stbi__err(\"can't merge dc and ac\", \"Corrupt JPEG\");\n\n    if (j->succ_high == 0) {\n        int shift = j->succ_low;\n\n        if (j->eob_run) {\n            --j->eob_run;\n            return 1;\n        }\n\n        k = j->spec_start;\n        do {\n            unsigned int zig;\n            int c,r,s;\n            if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);\n            c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1);\n            r = fac[c];\n            if (r) { // fast-AC path\n                k += (r >> 4) & 15; // run\n                s = r & 15; // combined length\n                j->code_buffer <<= s;\n                j->code_bits -= s;\n                zig = stbi__jpeg_dezigzag[k++];\n                data[zig] = (short) ((r >> 8) << shift);\n            } else {\n                int rs = stbi__jpeg_huff_decode(j, hac);\n                if (rs < 0) return stbi__err(\"bad huffman code\",\"Corrupt JPEG\");\n                s = rs & 15;\n                r = rs >> 4;\n                if (s == 0) {\n                    if (r < 15) {\n                        j->eob_run = (1 << r);\n                        if (r)\n                            j->eob_run += stbi__jpeg_get_bits(j, r);\n                        --j->eob_run;\n                        break;\n                    }\n                    k += 16;\n                } else {\n                    k += r;\n                    zig = stbi__jpeg_dezigzag[k++];\n                    data[zig] = (short) (stbi__extend_receive(j,s) << shift);\n                }\n            }\n        } while (k <= j->spec_end);\n    } else {\n        // refinement scan for these AC coefficients\n\n        short bit = (short) (1 << j->succ_low);\n\n        if (j->eob_run) {\n            --j->eob_run;\n            for (k = j->spec_start; k <= j->spec_end; ++k) {\n                short *p = &data[stbi__jpeg_dezigzag[k]];\n                if (*p != 0)\n                    if (stbi__jpeg_get_bit(j))\n                        if ((*p & bit)==0) {\n                            if (*p > 0)\n                                *p += bit;\n                            else\n                                *p -= bit;\n                        }\n            }\n        } else {\n            k = j->spec_start;\n            do {\n                int r,s;\n                int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh\n                if (rs < 0) return stbi__err(\"bad huffman code\",\"Corrupt JPEG\");\n                s = rs & 15;\n                r = rs >> 4;\n                if (s == 0) {\n                    if (r < 15) {\n                        j->eob_run = (1 << r) - 1;\n                        if (r)\n                            j->eob_run += stbi__jpeg_get_bits(j, r);\n                        r = 64; // force end of block\n                    } else {\n                        // r=15 s=0 should write 16 0s, so we just do\n                        // a run of 15 0s and then write s (which is 0),\n                        // so we don't have to do anything special here\n                    }\n                } else {\n                    if (s != 1) return stbi__err(\"bad huffman code\", \"Corrupt JPEG\");\n                    // sign bit\n                    if (stbi__jpeg_get_bit(j))\n                        s = bit;\n                    else\n                        s = -bit;\n                }\n\n                // advance by r\n                while (k <= j->spec_end) {\n                    short *p = &data[stbi__jpeg_dezigzag[k++]];\n                    if (*p != 0) {\n                        if (stbi__jpeg_get_bit(j))\n                            if ((*p & bit)==0) {\n                                if (*p > 0)\n                                    *p += bit;\n                                else\n                                    *p -= bit;\n                            }\n                    } else {\n                        if (r == 0) {\n                            *p = (short) s;\n                            break;\n                        }\n                        --r;\n                    }\n                }\n            } while (k <= j->spec_end);\n        }\n    }\n    return 1;\n}\n\n// take a -128..127 value and stbi__clamp it and convert to 0..255\nstbi_inline static stbi_uc stbi__clamp(int x) {\n    // trick to use a single test to catch both cases\n    if ((unsigned int) x > 255) {\n        if (x < 0) return 0;\n        if (x > 255) return 255;\n    }\n    return (stbi_uc) x;\n}\n\n#define stbi__f2f(x)  ((int) (((x) * 4096 + 0.5)))\n#define stbi__fsh(x)  ((x) * 4096)\n\n// derived from jidctint -- DCT_ISLOW\n#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \\\n   int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \\\n   p2 = s2;                                    \\\n   p3 = s6;                                    \\\n   p1 = (p2+p3) * stbi__f2f(0.5411961f);       \\\n   t2 = p1 + p3*stbi__f2f(-1.847759065f);      \\\n   t3 = p1 + p2*stbi__f2f( 0.765366865f);      \\\n   p2 = s0;                                    \\\n   p3 = s4;                                    \\\n   t0 = stbi__fsh(p2+p3);                      \\\n   t1 = stbi__fsh(p2-p3);                      \\\n   x0 = t0+t3;                                 \\\n   x3 = t0-t3;                                 \\\n   x1 = t1+t2;                                 \\\n   x2 = t1-t2;                                 \\\n   t0 = s7;                                    \\\n   t1 = s5;                                    \\\n   t2 = s3;                                    \\\n   t3 = s1;                                    \\\n   p3 = t0+t2;                                 \\\n   p4 = t1+t3;                                 \\\n   p1 = t0+t3;                                 \\\n   p2 = t1+t2;                                 \\\n   p5 = (p3+p4)*stbi__f2f( 1.175875602f);      \\\n   t0 = t0*stbi__f2f( 0.298631336f);           \\\n   t1 = t1*stbi__f2f( 2.053119869f);           \\\n   t2 = t2*stbi__f2f( 3.072711026f);           \\\n   t3 = t3*stbi__f2f( 1.501321110f);           \\\n   p1 = p5 + p1*stbi__f2f(-0.899976223f);      \\\n   p2 = p5 + p2*stbi__f2f(-2.562915447f);      \\\n   p3 = p3*stbi__f2f(-1.961570560f);           \\\n   p4 = p4*stbi__f2f(-0.390180644f);           \\\n   t3 += p1+p4;                                \\\n   t2 += p2+p3;                                \\\n   t1 += p2+p4;                                \\\n   t0 += p1+p3;\n\nstatic void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) {\n    int i,val[64],*v=val;\n    stbi_uc *o;\n    short *d = data;\n\n    // columns\n    for (i=0; i < 8; ++i,++d, ++v) {\n        // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing\n        if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0\n                && d[40]==0 && d[48]==0 && d[56]==0) {\n            //    no shortcut                 0     seconds\n            //    (1|2|3|4|5|6|7)==0          0     seconds\n            //    all separate               -0.047 seconds\n            //    1 && 2|3 && 4|5 && 6|7:    -0.047 seconds\n            int dcterm = d[0]*4;\n            v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm;\n        } else {\n            STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56])\n            // constants scaled things up by 1<<12; let's bring them back\n            // down, but keep 2 extra bits of precision\n            x0 += 512;\n            x1 += 512;\n            x2 += 512;\n            x3 += 512;\n            v[ 0] = (x0+t3) >> 10;\n            v[56] = (x0-t3) >> 10;\n            v[ 8] = (x1+t2) >> 10;\n            v[48] = (x1-t2) >> 10;\n            v[16] = (x2+t1) >> 10;\n            v[40] = (x2-t1) >> 10;\n            v[24] = (x3+t0) >> 10;\n            v[32] = (x3-t0) >> 10;\n        }\n    }\n\n    for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) {\n        // no fast case since the first 1D IDCT spread components out\n        STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7])\n        // constants scaled things up by 1<<12, plus we had 1<<2 from first\n        // loop, plus horizontal and vertical each scale by sqrt(8) so together\n        // we've got an extra 1<<3, so 1<<17 total we need to remove.\n        // so we want to round that, which means adding 0.5 * 1<<17,\n        // aka 65536. Also, we'll end up with -128 to 127 that we want\n        // to encode as 0..255 by adding 128, so we'll add that before the shift\n        x0 += 65536 + (128<<17);\n        x1 += 65536 + (128<<17);\n        x2 += 65536 + (128<<17);\n        x3 += 65536 + (128<<17);\n        // tried computing the shifts into temps, or'ing the temps to see\n        // if any were out of range, but that was slower\n        o[0] = stbi__clamp((x0+t3) >> 17);\n        o[7] = stbi__clamp((x0-t3) >> 17);\n        o[1] = stbi__clamp((x1+t2) >> 17);\n        o[6] = stbi__clamp((x1-t2) >> 17);\n        o[2] = stbi__clamp((x2+t1) >> 17);\n        o[5] = stbi__clamp((x2-t1) >> 17);\n        o[3] = stbi__clamp((x3+t0) >> 17);\n        o[4] = stbi__clamp((x3-t0) >> 17);\n    }\n}\n\n#ifdef STBI_SSE2\n// sse2 integer IDCT. not the fastest possible implementation but it\n// produces bit-identical results to the generic C version so it's\n// fully \"transparent\".\nstatic void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) {\n    // This is constructed to match our regular (generic) integer IDCT exactly.\n    __m128i row0, row1, row2, row3, row4, row5, row6, row7;\n    __m128i tmp;\n\n    // dot product constant: even elems=x, odd elems=y\n#define dct_const(x,y)  _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y))\n\n    // out(0) = c0[even]*x + c0[odd]*y   (c0, x, y 16-bit, out 32-bit)\n    // out(1) = c1[even]*x + c1[odd]*y\n#define dct_rot(out0,out1, x,y,c0,c1) \\\n      __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \\\n      __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \\\n      __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \\\n      __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \\\n      __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \\\n      __m128i out1##_h = _mm_madd_epi16(c0##hi, c1)\n\n    // out = in << 12  (in 16-bit, out 32-bit)\n#define dct_widen(out, in) \\\n      __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \\\n      __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4)\n\n    // wide add\n#define dct_wadd(out, a, b) \\\n      __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \\\n      __m128i out##_h = _mm_add_epi32(a##_h, b##_h)\n\n    // wide sub\n#define dct_wsub(out, a, b) \\\n      __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \\\n      __m128i out##_h = _mm_sub_epi32(a##_h, b##_h)\n\n    // butterfly a/b, add bias, then shift by \"s\" and pack\n#define dct_bfly32o(out0, out1, a,b,bias,s) \\\n      { \\\n         __m128i abiased_l = _mm_add_epi32(a##_l, bias); \\\n         __m128i abiased_h = _mm_add_epi32(a##_h, bias); \\\n         dct_wadd(sum, abiased, b); \\\n         dct_wsub(dif, abiased, b); \\\n         out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \\\n         out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \\\n      }\n\n    // 8-bit interleave step (for transposes)\n#define dct_interleave8(a, b) \\\n      tmp = a; \\\n      a = _mm_unpacklo_epi8(a, b); \\\n      b = _mm_unpackhi_epi8(tmp, b)\n\n    // 16-bit interleave step (for transposes)\n#define dct_interleave16(a, b) \\\n      tmp = a; \\\n      a = _mm_unpacklo_epi16(a, b); \\\n      b = _mm_unpackhi_epi16(tmp, b)\n\n#define dct_pass(bias,shift) \\\n      { \\\n         /* even part */ \\\n         dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \\\n         __m128i sum04 = _mm_add_epi16(row0, row4); \\\n         __m128i dif04 = _mm_sub_epi16(row0, row4); \\\n         dct_widen(t0e, sum04); \\\n         dct_widen(t1e, dif04); \\\n         dct_wadd(x0, t0e, t3e); \\\n         dct_wsub(x3, t0e, t3e); \\\n         dct_wadd(x1, t1e, t2e); \\\n         dct_wsub(x2, t1e, t2e); \\\n         /* odd part */ \\\n         dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \\\n         dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \\\n         __m128i sum17 = _mm_add_epi16(row1, row7); \\\n         __m128i sum35 = _mm_add_epi16(row3, row5); \\\n         dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \\\n         dct_wadd(x4, y0o, y4o); \\\n         dct_wadd(x5, y1o, y5o); \\\n         dct_wadd(x6, y2o, y5o); \\\n         dct_wadd(x7, y3o, y4o); \\\n         dct_bfly32o(row0,row7, x0,x7,bias,shift); \\\n         dct_bfly32o(row1,row6, x1,x6,bias,shift); \\\n         dct_bfly32o(row2,row5, x2,x5,bias,shift); \\\n         dct_bfly32o(row3,row4, x3,x4,bias,shift); \\\n      }\n\n    __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f));\n    __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f));\n    __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f));\n    __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f));\n    __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f));\n    __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f));\n    __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f));\n    __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f));\n\n    // rounding biases in column/row passes, see stbi__idct_block for explanation.\n    __m128i bias_0 = _mm_set1_epi32(512);\n    __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17));\n\n    // load\n    row0 = _mm_load_si128((const __m128i *) (data + 0*8));\n    row1 = _mm_load_si128((const __m128i *) (data + 1*8));\n    row2 = _mm_load_si128((const __m128i *) (data + 2*8));\n    row3 = _mm_load_si128((const __m128i *) (data + 3*8));\n    row4 = _mm_load_si128((const __m128i *) (data + 4*8));\n    row5 = _mm_load_si128((const __m128i *) (data + 5*8));\n    row6 = _mm_load_si128((const __m128i *) (data + 6*8));\n    row7 = _mm_load_si128((const __m128i *) (data + 7*8));\n\n    // column pass\n    dct_pass(bias_0, 10);\n\n    {\n        // 16bit 8x8 transpose pass 1\n        dct_interleave16(row0, row4);\n        dct_interleave16(row1, row5);\n        dct_interleave16(row2, row6);\n        dct_interleave16(row3, row7);\n\n        // transpose pass 2\n        dct_interleave16(row0, row2);\n        dct_interleave16(row1, row3);\n        dct_interleave16(row4, row6);\n        dct_interleave16(row5, row7);\n\n        // transpose pass 3\n        dct_interleave16(row0, row1);\n        dct_interleave16(row2, row3);\n        dct_interleave16(row4, row5);\n        dct_interleave16(row6, row7);\n    }\n\n    // row pass\n    dct_pass(bias_1, 17);\n\n    {\n        // pack\n        __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7\n        __m128i p1 = _mm_packus_epi16(row2, row3);\n        __m128i p2 = _mm_packus_epi16(row4, row5);\n        __m128i p3 = _mm_packus_epi16(row6, row7);\n\n        // 8bit 8x8 transpose pass 1\n        dct_interleave8(p0, p2); // a0e0a1e1...\n        dct_interleave8(p1, p3); // c0g0c1g1...\n\n        // transpose pass 2\n        dct_interleave8(p0, p1); // a0c0e0g0...\n        dct_interleave8(p2, p3); // b0d0f0h0...\n\n        // transpose pass 3\n        dct_interleave8(p0, p2); // a0b0c0d0...\n        dct_interleave8(p1, p3); // a4b4c4d4...\n\n        // store\n        _mm_storel_epi64((__m128i *) out, p0);\n        out += out_stride;\n        _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e));\n        out += out_stride;\n        _mm_storel_epi64((__m128i *) out, p2);\n        out += out_stride;\n        _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e));\n        out += out_stride;\n        _mm_storel_epi64((__m128i *) out, p1);\n        out += out_stride;\n        _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e));\n        out += out_stride;\n        _mm_storel_epi64((__m128i *) out, p3);\n        out += out_stride;\n        _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e));\n    }\n\n#undef dct_const\n#undef dct_rot\n#undef dct_widen\n#undef dct_wadd\n#undef dct_wsub\n#undef dct_bfly32o\n#undef dct_interleave8\n#undef dct_interleave16\n#undef dct_pass\n}\n\n#endif // STBI_SSE2\n\n#ifdef STBI_NEON\n\n// NEON integer IDCT. should produce bit-identical\n// results to the generic C version.\nstatic void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) {\n    int16x8_t row0, row1, row2, row3, row4, row5, row6, row7;\n\n    int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f));\n    int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f));\n    int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f));\n    int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f));\n    int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f));\n    int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f));\n    int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f));\n    int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f));\n    int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f));\n    int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f));\n    int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f));\n    int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f));\n\n#define dct_long_mul(out, inq, coeff) \\\n   int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \\\n   int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff)\n\n#define dct_long_mac(out, acc, inq, coeff) \\\n   int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \\\n   int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff)\n\n#define dct_widen(out, inq) \\\n   int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \\\n   int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12)\n\n// wide add\n#define dct_wadd(out, a, b) \\\n   int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \\\n   int32x4_t out##_h = vaddq_s32(a##_h, b##_h)\n\n// wide sub\n#define dct_wsub(out, a, b) \\\n   int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \\\n   int32x4_t out##_h = vsubq_s32(a##_h, b##_h)\n\n// butterfly a/b, then shift using \"shiftop\" by \"s\" and pack\n#define dct_bfly32o(out0,out1, a,b,shiftop,s) \\\n   { \\\n      dct_wadd(sum, a, b); \\\n      dct_wsub(dif, a, b); \\\n      out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \\\n      out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \\\n   }\n\n#define dct_pass(shiftop, shift) \\\n   { \\\n      /* even part */ \\\n      int16x8_t sum26 = vaddq_s16(row2, row6); \\\n      dct_long_mul(p1e, sum26, rot0_0); \\\n      dct_long_mac(t2e, p1e, row6, rot0_1); \\\n      dct_long_mac(t3e, p1e, row2, rot0_2); \\\n      int16x8_t sum04 = vaddq_s16(row0, row4); \\\n      int16x8_t dif04 = vsubq_s16(row0, row4); \\\n      dct_widen(t0e, sum04); \\\n      dct_widen(t1e, dif04); \\\n      dct_wadd(x0, t0e, t3e); \\\n      dct_wsub(x3, t0e, t3e); \\\n      dct_wadd(x1, t1e, t2e); \\\n      dct_wsub(x2, t1e, t2e); \\\n      /* odd part */ \\\n      int16x8_t sum15 = vaddq_s16(row1, row5); \\\n      int16x8_t sum17 = vaddq_s16(row1, row7); \\\n      int16x8_t sum35 = vaddq_s16(row3, row5); \\\n      int16x8_t sum37 = vaddq_s16(row3, row7); \\\n      int16x8_t sumodd = vaddq_s16(sum17, sum35); \\\n      dct_long_mul(p5o, sumodd, rot1_0); \\\n      dct_long_mac(p1o, p5o, sum17, rot1_1); \\\n      dct_long_mac(p2o, p5o, sum35, rot1_2); \\\n      dct_long_mul(p3o, sum37, rot2_0); \\\n      dct_long_mul(p4o, sum15, rot2_1); \\\n      dct_wadd(sump13o, p1o, p3o); \\\n      dct_wadd(sump24o, p2o, p4o); \\\n      dct_wadd(sump23o, p2o, p3o); \\\n      dct_wadd(sump14o, p1o, p4o); \\\n      dct_long_mac(x4, sump13o, row7, rot3_0); \\\n      dct_long_mac(x5, sump24o, row5, rot3_1); \\\n      dct_long_mac(x6, sump23o, row3, rot3_2); \\\n      dct_long_mac(x7, sump14o, row1, rot3_3); \\\n      dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \\\n      dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \\\n      dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \\\n      dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \\\n   }\n\n    // load\n    row0 = vld1q_s16(data + 0*8);\n    row1 = vld1q_s16(data + 1*8);\n    row2 = vld1q_s16(data + 2*8);\n    row3 = vld1q_s16(data + 3*8);\n    row4 = vld1q_s16(data + 4*8);\n    row5 = vld1q_s16(data + 5*8);\n    row6 = vld1q_s16(data + 6*8);\n    row7 = vld1q_s16(data + 7*8);\n\n    // add DC bias\n    row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0));\n\n    // column pass\n    dct_pass(vrshrn_n_s32, 10);\n\n    // 16bit 8x8 transpose\n    {\n// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively.\n// whether compilers actually get this is another story, sadly.\n#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; }\n#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); }\n#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); }\n\n        // pass 1\n        dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6\n        dct_trn16(row2, row3);\n        dct_trn16(row4, row5);\n        dct_trn16(row6, row7);\n\n        // pass 2\n        dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4\n        dct_trn32(row1, row3);\n        dct_trn32(row4, row6);\n        dct_trn32(row5, row7);\n\n        // pass 3\n        dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0\n        dct_trn64(row1, row5);\n        dct_trn64(row2, row6);\n        dct_trn64(row3, row7);\n\n#undef dct_trn16\n#undef dct_trn32\n#undef dct_trn64\n    }\n\n    // row pass\n    // vrshrn_n_s32 only supports shifts up to 16, we need\n    // 17. so do a non-rounding shift of 16 first then follow\n    // up with a rounding shift by 1.\n    dct_pass(vshrn_n_s32, 16);\n\n    {\n        // pack and round\n        uint8x8_t p0 = vqrshrun_n_s16(row0, 1);\n        uint8x8_t p1 = vqrshrun_n_s16(row1, 1);\n        uint8x8_t p2 = vqrshrun_n_s16(row2, 1);\n        uint8x8_t p3 = vqrshrun_n_s16(row3, 1);\n        uint8x8_t p4 = vqrshrun_n_s16(row4, 1);\n        uint8x8_t p5 = vqrshrun_n_s16(row5, 1);\n        uint8x8_t p6 = vqrshrun_n_s16(row6, 1);\n        uint8x8_t p7 = vqrshrun_n_s16(row7, 1);\n\n        // again, these can translate into one instruction, but often don't.\n#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; }\n#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); }\n#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); }\n\n        // sadly can't use interleaved stores here since we only write\n        // 8 bytes to each scan line!\n\n        // 8x8 8-bit transpose pass 1\n        dct_trn8_8(p0, p1);\n        dct_trn8_8(p2, p3);\n        dct_trn8_8(p4, p5);\n        dct_trn8_8(p6, p7);\n\n        // pass 2\n        dct_trn8_16(p0, p2);\n        dct_trn8_16(p1, p3);\n        dct_trn8_16(p4, p6);\n        dct_trn8_16(p5, p7);\n\n        // pass 3\n        dct_trn8_32(p0, p4);\n        dct_trn8_32(p1, p5);\n        dct_trn8_32(p2, p6);\n        dct_trn8_32(p3, p7);\n\n        // store\n        vst1_u8(out, p0);\n        out += out_stride;\n        vst1_u8(out, p1);\n        out += out_stride;\n        vst1_u8(out, p2);\n        out += out_stride;\n        vst1_u8(out, p3);\n        out += out_stride;\n        vst1_u8(out, p4);\n        out += out_stride;\n        vst1_u8(out, p5);\n        out += out_stride;\n        vst1_u8(out, p6);\n        out += out_stride;\n        vst1_u8(out, p7);\n\n#undef dct_trn8_8\n#undef dct_trn8_16\n#undef dct_trn8_32\n    }\n\n#undef dct_long_mul\n#undef dct_long_mac\n#undef dct_widen\n#undef dct_wadd\n#undef dct_wsub\n#undef dct_bfly32o\n#undef dct_pass\n}\n\n#endif // STBI_NEON\n\n#define STBI__MARKER_none  0xff\n// if there's a pending marker from the entropy stream, return that\n// otherwise, fetch from the stream and get a marker. if there's no\n// marker, return 0xff, which is never a valid marker value\nstatic stbi_uc stbi__get_marker(stbi__jpeg *j) {\n    stbi_uc x;\n    if (j->marker != STBI__MARKER_none) {\n        x = j->marker;\n        j->marker = STBI__MARKER_none;\n        return x;\n    }\n    x = stbi__get8(j->s);\n    if (x != 0xff) return STBI__MARKER_none;\n    while (x == 0xff)\n        x = stbi__get8(j->s); // consume repeated 0xff fill bytes\n    return x;\n}\n\n// in each scan, we'll have scan_n components, and the order\n// of the components is specified by order[]\n#define STBI__RESTART(x)     ((x) >= 0xd0 && (x) <= 0xd7)\n\n// after a restart interval, stbi__jpeg_reset the entropy decoder and\n// the dc prediction\nstatic void stbi__jpeg_reset(stbi__jpeg *j) {\n    j->code_bits = 0;\n    j->code_buffer = 0;\n    j->nomore = 0;\n    j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0;\n    j->marker = STBI__MARKER_none;\n    j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff;\n    j->eob_run = 0;\n    // no more than 1<<31 MCUs if no restart_interal? that's plenty safe,\n    // since we don't even allow 1<<30 pixels\n}\n\nstatic int stbi__parse_entropy_coded_data(stbi__jpeg *z) {\n    stbi__jpeg_reset(z);\n    if (!z->progressive) {\n        if (z->scan_n == 1) {\n            int i,j;\n            STBI_SIMD_ALIGN(short, data[64]);\n            int n = z->order[0];\n            // non-interleaved data, we just need to process one block at a time,\n            // in trivial scanline order\n            // number of blocks to do just depends on how many actual \"pixels\" this\n            // component has, independent of interleaved MCU blocking and such\n            int w = (z->img_comp[n].x+7) >> 3;\n            int h = (z->img_comp[n].y+7) >> 3;\n            for (j=0; j < h; ++j) {\n                for (i=0; i < w; ++i) {\n                    int ha = z->img_comp[n].ha;\n                    if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0;\n                    z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data);\n                    // every data block is an MCU, so countdown the restart interval\n                    if (--z->todo <= 0) {\n                        if (z->code_bits < 24) stbi__grow_buffer_unsafe(z);\n                        // if it's NOT a restart, then just bail, so we get corrupt data\n                        // rather than no data\n                        if (!STBI__RESTART(z->marker)) return 1;\n                        stbi__jpeg_reset(z);\n                    }\n                }\n            }\n            return 1;\n        } else { // interleaved\n            int i,j,k,x,y;\n            STBI_SIMD_ALIGN(short, data[64]);\n            for (j=0; j < z->img_mcu_y; ++j) {\n                for (i=0; i < z->img_mcu_x; ++i) {\n                    // scan an interleaved mcu... process scan_n components in order\n                    for (k=0; k < z->scan_n; ++k) {\n                        int n = z->order[k];\n                        // scan out an mcu's worth of this component; that's just determined\n                        // by the basic H and V specified for the component\n                        for (y=0; y < z->img_comp[n].v; ++y) {\n                            for (x=0; x < z->img_comp[n].h; ++x) {\n                                int x2 = (i*z->img_comp[n].h + x)*8;\n                                int y2 = (j*z->img_comp[n].v + y)*8;\n                                int ha = z->img_comp[n].ha;\n                                if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0;\n                                z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data);\n                            }\n                        }\n                    }\n                    // after all interleaved components, that's an interleaved MCU,\n                    // so now count down the restart interval\n                    if (--z->todo <= 0) {\n                        if (z->code_bits < 24) stbi__grow_buffer_unsafe(z);\n                        if (!STBI__RESTART(z->marker)) return 1;\n                        stbi__jpeg_reset(z);\n                    }\n                }\n            }\n            return 1;\n        }\n    } else {\n        if (z->scan_n == 1) {\n            int i,j;\n            int n = z->order[0];\n            // non-interleaved data, we just need to process one block at a time,\n            // in trivial scanline order\n            // number of blocks to do just depends on how many actual \"pixels\" this\n            // component has, independent of interleaved MCU blocking and such\n            int w = (z->img_comp[n].x+7) >> 3;\n            int h = (z->img_comp[n].y+7) >> 3;\n            for (j=0; j < h; ++j) {\n                for (i=0; i < w; ++i) {\n                    short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w);\n                    if (z->spec_start == 0) {\n                        if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n))\n                            return 0;\n                    } else {\n                        int ha = z->img_comp[n].ha;\n                        if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha]))\n                            return 0;\n                    }\n                    // every data block is an MCU, so countdown the restart interval\n                    if (--z->todo <= 0) {\n                        if (z->code_bits < 24) stbi__grow_buffer_unsafe(z);\n                        if (!STBI__RESTART(z->marker)) return 1;\n                        stbi__jpeg_reset(z);\n                    }\n                }\n            }\n            return 1;\n        } else { // interleaved\n            int i,j,k,x,y;\n            for (j=0; j < z->img_mcu_y; ++j) {\n                for (i=0; i < z->img_mcu_x; ++i) {\n                    // scan an interleaved mcu... process scan_n components in order\n                    for (k=0; k < z->scan_n; ++k) {\n                        int n = z->order[k];\n                        // scan out an mcu's worth of this component; that's just determined\n                        // by the basic H and V specified for the component\n                        for (y=0; y < z->img_comp[n].v; ++y) {\n                            for (x=0; x < z->img_comp[n].h; ++x) {\n                                int x2 = (i*z->img_comp[n].h + x);\n                                int y2 = (j*z->img_comp[n].v + y);\n                                short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w);\n                                if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n))\n                                    return 0;\n                            }\n                        }\n                    }\n                    // after all interleaved components, that's an interleaved MCU,\n                    // so now count down the restart interval\n                    if (--z->todo <= 0) {\n                        if (z->code_bits < 24) stbi__grow_buffer_unsafe(z);\n                        if (!STBI__RESTART(z->marker)) return 1;\n                        stbi__jpeg_reset(z);\n                    }\n                }\n            }\n            return 1;\n        }\n    }\n}\n\nstatic void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) {\n    int i;\n    for (i=0; i < 64; ++i)\n        data[i] *= dequant[i];\n}\n\nstatic void stbi__jpeg_finish(stbi__jpeg *z) {\n    if (z->progressive) {\n        // dequantize and idct the data\n        int i,j,n;\n        for (n=0; n < z->s->img_n; ++n) {\n            int w = (z->img_comp[n].x+7) >> 3;\n            int h = (z->img_comp[n].y+7) >> 3;\n            for (j=0; j < h; ++j) {\n                for (i=0; i < w; ++i) {\n                    short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w);\n                    stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]);\n                    z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data);\n                }\n            }\n        }\n    }\n}\n\nstatic int stbi__process_marker(stbi__jpeg *z, int m) {\n    int L;\n    switch (m) {\n    case STBI__MARKER_none: // no marker found\n        return stbi__err(\"expected marker\",\"Corrupt JPEG\");\n\n    case 0xDD: // DRI - specify restart interval\n        if (stbi__get16be(z->s) != 4) return stbi__err(\"bad DRI len\",\"Corrupt JPEG\");\n        z->restart_interval = stbi__get16be(z->s);\n        return 1;\n\n    case 0xDB: // DQT - define quantization table\n        L = stbi__get16be(z->s)-2;\n        while (L > 0) {\n            int q = stbi__get8(z->s);\n            int p = q >> 4, sixteen = (p != 0);\n            int t = q & 15,i;\n            if (p != 0 && p != 1) return stbi__err(\"bad DQT type\",\"Corrupt JPEG\");\n            if (t > 3) return stbi__err(\"bad DQT table\",\"Corrupt JPEG\");\n\n            for (i=0; i < 64; ++i)\n                z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s));\n            L -= (sixteen ? 129 : 65);\n        }\n        return L==0;\n\n    case 0xC4: // DHT - define huffman table\n        L = stbi__get16be(z->s)-2;\n        while (L > 0) {\n            stbi_uc *v;\n            int sizes[16],i,n=0;\n            int q = stbi__get8(z->s);\n            int tc = q >> 4;\n            int th = q & 15;\n            if (tc > 1 || th > 3) return stbi__err(\"bad DHT header\",\"Corrupt JPEG\");\n            for (i=0; i < 16; ++i) {\n                sizes[i] = stbi__get8(z->s);\n                n += sizes[i];\n            }\n            L -= 17;\n            if (tc == 0) {\n                if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0;\n                v = z->huff_dc[th].values;\n            } else {\n                if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0;\n                v = z->huff_ac[th].values;\n            }\n            for (i=0; i < n; ++i)\n                v[i] = stbi__get8(z->s);\n            if (tc != 0)\n                stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th);\n            L -= n;\n        }\n        return L==0;\n    }\n\n    // check for comment block or APP blocks\n    if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) {\n        L = stbi__get16be(z->s);\n        if (L < 2) {\n            if (m == 0xFE)\n                return stbi__err(\"bad COM len\",\"Corrupt JPEG\");\n            else\n                return stbi__err(\"bad APP len\",\"Corrupt JPEG\");\n        }\n        L -= 2;\n\n        if (m == 0xE0 && L >= 5) { // JFIF APP0 segment\n            static const unsigned char tag[5] = {'J','F','I','F','\\0'};\n            int ok = 1;\n            int i;\n            for (i=0; i < 5; ++i)\n                if (stbi__get8(z->s) != tag[i])\n                    ok = 0;\n            L -= 5;\n            if (ok)\n                z->jfif = 1;\n        } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment\n            static const unsigned char tag[6] = {'A','d','o','b','e','\\0'};\n            int ok = 1;\n            int i;\n            for (i=0; i < 6; ++i)\n                if (stbi__get8(z->s) != tag[i])\n                    ok = 0;\n            L -= 6;\n            if (ok) {\n                stbi__get8(z->s); // version\n                stbi__get16be(z->s); // flags0\n                stbi__get16be(z->s); // flags1\n                z->app14_color_transform = stbi__get8(z->s); // color transform\n                L -= 6;\n            }\n        }\n\n        stbi__skip(z->s, L);\n        return 1;\n    }\n\n    return stbi__err(\"unknown marker\",\"Corrupt JPEG\");\n}\n\n// after we see SOS\nstatic int stbi__process_scan_header(stbi__jpeg *z) {\n    int i;\n    int Ls = stbi__get16be(z->s);\n    z->scan_n = stbi__get8(z->s);\n    if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err(\"bad SOS component count\",\"Corrupt JPEG\");\n    if (Ls != 6+2*z->scan_n) return stbi__err(\"bad SOS len\",\"Corrupt JPEG\");\n    for (i=0; i < z->scan_n; ++i) {\n        int id = stbi__get8(z->s), which;\n        int q = stbi__get8(z->s);\n        for (which = 0; which < z->s->img_n; ++which)\n            if (z->img_comp[which].id == id)\n                break;\n        if (which == z->s->img_n) return 0; // no match\n        z->img_comp[which].hd = q >> 4;\n        if (z->img_comp[which].hd > 3) return stbi__err(\"bad DC huff\",\"Corrupt JPEG\");\n        z->img_comp[which].ha = q & 15;\n        if (z->img_comp[which].ha > 3) return stbi__err(\"bad AC huff\",\"Corrupt JPEG\");\n        z->order[i] = which;\n    }\n\n    {\n        int aa;\n        z->spec_start = stbi__get8(z->s);\n        z->spec_end   = stbi__get8(z->s); // should be 63, but might be 0\n        aa = stbi__get8(z->s);\n        z->succ_high = (aa >> 4);\n        z->succ_low  = (aa & 15);\n        if (z->progressive) {\n            if (z->spec_start > 63 || z->spec_end > 63  || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13)\n                return stbi__err(\"bad SOS\", \"Corrupt JPEG\");\n        } else {\n            if (z->spec_start != 0) return stbi__err(\"bad SOS\",\"Corrupt JPEG\");\n            if (z->succ_high != 0 || z->succ_low != 0) return stbi__err(\"bad SOS\",\"Corrupt JPEG\");\n            z->spec_end = 63;\n        }\n    }\n\n    return 1;\n}\n\nstatic int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) {\n    int i;\n    for (i=0; i < ncomp; ++i) {\n        if (z->img_comp[i].raw_data) {\n            STBI_FREE(z->img_comp[i].raw_data);\n            z->img_comp[i].raw_data = NULL;\n            z->img_comp[i].data = NULL;\n        }\n        if (z->img_comp[i].raw_coeff) {\n            STBI_FREE(z->img_comp[i].raw_coeff);\n            z->img_comp[i].raw_coeff = 0;\n            z->img_comp[i].coeff = 0;\n        }\n        if (z->img_comp[i].linebuf) {\n            STBI_FREE(z->img_comp[i].linebuf);\n            z->img_comp[i].linebuf = NULL;\n        }\n    }\n    return why;\n}\n\nstatic int stbi__process_frame_header(stbi__jpeg *z, int scan) {\n    stbi__context *s = z->s;\n    int Lf,p,i,q, h_max=1,v_max=1,c;\n    Lf = stbi__get16be(s);\n    if (Lf < 11) return stbi__err(\"bad SOF len\",\"Corrupt JPEG\"); // JPEG\n    p  = stbi__get8(s);\n    if (p != 8) return stbi__err(\"only 8-bit\",\"JPEG format not supported: 8-bit only\"); // JPEG baseline\n    s->img_y = stbi__get16be(s);\n    if (s->img_y == 0) return stbi__err(\"no header height\", \"JPEG format not supported: delayed height\"); // Legal, but we don't handle it--but neither does IJG\n    s->img_x = stbi__get16be(s);\n    if (s->img_x == 0) return stbi__err(\"0 width\",\"Corrupt JPEG\"); // JPEG requires\n    c = stbi__get8(s);\n    if (c != 3 && c != 1 && c != 4) return stbi__err(\"bad component count\",\"Corrupt JPEG\");\n    s->img_n = c;\n    for (i=0; i < c; ++i) {\n        z->img_comp[i].data = NULL;\n        z->img_comp[i].linebuf = NULL;\n    }\n\n    if (Lf != 8+3*s->img_n) return stbi__err(\"bad SOF len\",\"Corrupt JPEG\");\n\n    z->rgb = 0;\n    for (i=0; i < s->img_n; ++i) {\n        static const unsigned char rgb[3] = { 'R', 'G', 'B' };\n        z->img_comp[i].id = stbi__get8(s);\n        if (s->img_n == 3 && z->img_comp[i].id == rgb[i])\n            ++z->rgb;\n        q = stbi__get8(s);\n        z->img_comp[i].h = (q >> 4);\n        if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err(\"bad H\",\"Corrupt JPEG\");\n        z->img_comp[i].v = q & 15;\n        if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err(\"bad V\",\"Corrupt JPEG\");\n        z->img_comp[i].tq = stbi__get8(s);\n        if (z->img_comp[i].tq > 3) return stbi__err(\"bad TQ\",\"Corrupt JPEG\");\n    }\n\n    if (scan != STBI__SCAN_load) return 1;\n\n    if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err(\"too large\", \"Image too large to decode\");\n\n    for (i=0; i < s->img_n; ++i) {\n        if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h;\n        if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v;\n    }\n\n    // compute interleaved mcu info\n    z->img_h_max = h_max;\n    z->img_v_max = v_max;\n    z->img_mcu_w = h_max * 8;\n    z->img_mcu_h = v_max * 8;\n    // these sizes can't be more than 17 bits\n    z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w;\n    z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h;\n\n    for (i=0; i < s->img_n; ++i) {\n        // number of effective pixels (e.g. for non-interleaved MCU)\n        z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max;\n        z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max;\n        // to simplify generation, we'll allocate enough memory to decode\n        // the bogus oversized data from using interleaved MCUs and their\n        // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't\n        // discard the extra data until colorspace conversion\n        //\n        // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier)\n        // so these muls can't overflow with 32-bit ints (which we require)\n        z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8;\n        z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8;\n        z->img_comp[i].coeff = 0;\n        z->img_comp[i].raw_coeff = 0;\n        z->img_comp[i].linebuf = NULL;\n        z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15);\n        if (z->img_comp[i].raw_data == NULL)\n            return stbi__free_jpeg_components(z, i+1, stbi__err(\"outofmem\", \"Out of memory\"));\n        // align blocks for idct using mmx/sse\n        z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15);\n        if (z->progressive) {\n            // w2, h2 are multiples of 8 (see above)\n            z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8;\n            z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8;\n            z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15);\n            if (z->img_comp[i].raw_coeff == NULL)\n                return stbi__free_jpeg_components(z, i+1, stbi__err(\"outofmem\", \"Out of memory\"));\n            z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15);\n        }\n    }\n\n    return 1;\n}\n\n// use comparisons since in some cases we handle more than one case (e.g. SOF)\n#define stbi__DNL(x)         ((x) == 0xdc)\n#define stbi__SOI(x)         ((x) == 0xd8)\n#define stbi__EOI(x)         ((x) == 0xd9)\n#define stbi__SOF(x)         ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2)\n#define stbi__SOS(x)         ((x) == 0xda)\n\n#define stbi__SOF_progressive(x)   ((x) == 0xc2)\n\nstatic int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) {\n    int m;\n    z->jfif = 0;\n    z->app14_color_transform = -1; // valid values are 0,1,2\n    z->marker = STBI__MARKER_none; // initialize cached marker to empty\n    m = stbi__get_marker(z);\n    if (!stbi__SOI(m)) return stbi__err(\"no SOI\",\"Corrupt JPEG\");\n    if (scan == STBI__SCAN_type) return 1;\n    m = stbi__get_marker(z);\n    while (!stbi__SOF(m)) {\n        if (!stbi__process_marker(z,m)) return 0;\n        m = stbi__get_marker(z);\n        while (m == STBI__MARKER_none) {\n            // some files have extra padding after their blocks, so ok, we'll scan\n            if (stbi__at_eof(z->s)) return stbi__err(\"no SOF\", \"Corrupt JPEG\");\n            m = stbi__get_marker(z);\n        }\n    }\n    z->progressive = stbi__SOF_progressive(m);\n    if (!stbi__process_frame_header(z, scan)) return 0;\n    return 1;\n}\n\n// decode image to YCbCr format\nstatic int stbi__decode_jpeg_image(stbi__jpeg *j) {\n    int m;\n    for (m = 0; m < 4; m++) {\n        j->img_comp[m].raw_data = NULL;\n        j->img_comp[m].raw_coeff = NULL;\n    }\n    j->restart_interval = 0;\n    if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0;\n    m = stbi__get_marker(j);\n    while (!stbi__EOI(m)) {\n        if (stbi__SOS(m)) {\n            if (!stbi__process_scan_header(j)) return 0;\n            if (!stbi__parse_entropy_coded_data(j)) return 0;\n            if (j->marker == STBI__MARKER_none ) {\n                // handle 0s at the end of image data from IP Kamera 9060\n                while (!stbi__at_eof(j->s)) {\n                    int x = stbi__get8(j->s);\n                    if (x == 255) {\n                        j->marker = stbi__get8(j->s);\n                        break;\n                    }\n                }\n                // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0\n            }\n        } else if (stbi__DNL(m)) {\n            int Ld = stbi__get16be(j->s);\n            stbi__uint32 NL = stbi__get16be(j->s);\n            if (Ld != 4) return stbi__err(\"bad DNL len\", \"Corrupt JPEG\");\n            if (NL != j->s->img_y) return stbi__err(\"bad DNL height\", \"Corrupt JPEG\");\n        } else {\n            if (!stbi__process_marker(j, m)) return 0;\n        }\n        m = stbi__get_marker(j);\n    }\n    if (j->progressive)\n        stbi__jpeg_finish(j);\n    return 1;\n}\n\n// static jfif-centered resampling (across block boundaries)\n\ntypedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1,\n                                      int w, int hs);\n\n#define stbi__div4(x) ((stbi_uc) ((x) >> 2))\n\nstatic stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) {\n    STBI_NOTUSED(out);\n    STBI_NOTUSED(in_far);\n    STBI_NOTUSED(w);\n    STBI_NOTUSED(hs);\n    return in_near;\n}\n\nstatic stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) {\n    // need to generate two samples vertically for every one in input\n    int i;\n    STBI_NOTUSED(hs);\n    for (i=0; i < w; ++i)\n        out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2);\n    return out;\n}\n\nstatic stbi_uc*  stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) {\n    // need to generate two samples horizontally for every one in input\n    int i;\n    stbi_uc *input = in_near;\n\n    if (w == 1) {\n        // if only one sample, can't do any interpolation\n        out[0] = out[1] = input[0];\n        return out;\n    }\n\n    out[0] = input[0];\n    out[1] = stbi__div4(input[0]*3 + input[1] + 2);\n    for (i=1; i < w-1; ++i) {\n        int n = 3*input[i]+2;\n        out[i*2+0] = stbi__div4(n+input[i-1]);\n        out[i*2+1] = stbi__div4(n+input[i+1]);\n    }\n    out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2);\n    out[i*2+1] = input[w-1];\n\n    STBI_NOTUSED(in_far);\n    STBI_NOTUSED(hs);\n\n    return out;\n}\n\n#define stbi__div16(x) ((stbi_uc) ((x) >> 4))\n\nstatic stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) {\n    // need to generate 2x2 samples for every one in input\n    int i,t0,t1;\n    if (w == 1) {\n        out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2);\n        return out;\n    }\n\n    t1 = 3*in_near[0] + in_far[0];\n    out[0] = stbi__div4(t1+2);\n    for (i=1; i < w; ++i) {\n        t0 = t1;\n        t1 = 3*in_near[i]+in_far[i];\n        out[i*2-1] = stbi__div16(3*t0 + t1 + 8);\n        out[i*2  ] = stbi__div16(3*t1 + t0 + 8);\n    }\n    out[w*2-1] = stbi__div4(t1+2);\n\n    STBI_NOTUSED(hs);\n\n    return out;\n}\n\n#if defined(STBI_SSE2) || defined(STBI_NEON)\nstatic stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) {\n    // need to generate 2x2 samples for every one in input\n    int i=0,t0,t1;\n\n    if (w == 1) {\n        out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2);\n        return out;\n    }\n\n    t1 = 3*in_near[0] + in_far[0];\n    // process groups of 8 pixels for as long as we can.\n    // note we can't handle the last pixel in a row in this loop\n    // because we need to handle the filter boundary conditions.\n    for (; i < ((w-1) & ~7); i += 8) {\n#if defined(STBI_SSE2)\n        // load and perform the vertical filtering pass\n        // this uses 3*x + y = 4*x + (y - x)\n        __m128i zero  = _mm_setzero_si128();\n        __m128i farb  = _mm_loadl_epi64((__m128i *) (in_far + i));\n        __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i));\n        __m128i farw  = _mm_unpacklo_epi8(farb, zero);\n        __m128i nearw = _mm_unpacklo_epi8(nearb, zero);\n        __m128i diff  = _mm_sub_epi16(farw, nearw);\n        __m128i nears = _mm_slli_epi16(nearw, 2);\n        __m128i curr  = _mm_add_epi16(nears, diff); // current row\n\n        // horizontal filter works the same based on shifted vers of current\n        // row. \"prev\" is current row shifted right by 1 pixel; we need to\n        // insert the previous pixel value (from t1).\n        // \"next\" is current row shifted left by 1 pixel, with first pixel\n        // of next block of 8 pixels added in.\n        __m128i prv0 = _mm_slli_si128(curr, 2);\n        __m128i nxt0 = _mm_srli_si128(curr, 2);\n        __m128i prev = _mm_insert_epi16(prv0, t1, 0);\n        __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7);\n\n        // horizontal filter, polyphase implementation since it's convenient:\n        // even pixels = 3*cur + prev = cur*4 + (prev - cur)\n        // odd  pixels = 3*cur + next = cur*4 + (next - cur)\n        // note the shared term.\n        __m128i bias  = _mm_set1_epi16(8);\n        __m128i curs = _mm_slli_epi16(curr, 2);\n        __m128i prvd = _mm_sub_epi16(prev, curr);\n        __m128i nxtd = _mm_sub_epi16(next, curr);\n        __m128i curb = _mm_add_epi16(curs, bias);\n        __m128i even = _mm_add_epi16(prvd, curb);\n        __m128i odd  = _mm_add_epi16(nxtd, curb);\n\n        // interleave even and odd pixels, then undo scaling.\n        __m128i int0 = _mm_unpacklo_epi16(even, odd);\n        __m128i int1 = _mm_unpackhi_epi16(even, odd);\n        __m128i de0  = _mm_srli_epi16(int0, 4);\n        __m128i de1  = _mm_srli_epi16(int1, 4);\n\n        // pack and write output\n        __m128i outv = _mm_packus_epi16(de0, de1);\n        _mm_storeu_si128((__m128i *) (out + i*2), outv);\n#elif defined(STBI_NEON)\n        // load and perform the vertical filtering pass\n        // this uses 3*x + y = 4*x + (y - x)\n        uint8x8_t farb  = vld1_u8(in_far + i);\n        uint8x8_t nearb = vld1_u8(in_near + i);\n        int16x8_t diff  = vreinterpretq_s16_u16(vsubl_u8(farb, nearb));\n        int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2));\n        int16x8_t curr  = vaddq_s16(nears, diff); // current row\n\n        // horizontal filter works the same based on shifted vers of current\n        // row. \"prev\" is current row shifted right by 1 pixel; we need to\n        // insert the previous pixel value (from t1).\n        // \"next\" is current row shifted left by 1 pixel, with first pixel\n        // of next block of 8 pixels added in.\n        int16x8_t prv0 = vextq_s16(curr, curr, 7);\n        int16x8_t nxt0 = vextq_s16(curr, curr, 1);\n        int16x8_t prev = vsetq_lane_s16(t1, prv0, 0);\n        int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7);\n\n        // horizontal filter, polyphase implementation since it's convenient:\n        // even pixels = 3*cur + prev = cur*4 + (prev - cur)\n        // odd  pixels = 3*cur + next = cur*4 + (next - cur)\n        // note the shared term.\n        int16x8_t curs = vshlq_n_s16(curr, 2);\n        int16x8_t prvd = vsubq_s16(prev, curr);\n        int16x8_t nxtd = vsubq_s16(next, curr);\n        int16x8_t even = vaddq_s16(curs, prvd);\n        int16x8_t odd  = vaddq_s16(curs, nxtd);\n\n        // undo scaling and round, then store with even/odd phases interleaved\n        uint8x8x2_t o;\n        o.val[0] = vqrshrun_n_s16(even, 4);\n        o.val[1] = vqrshrun_n_s16(odd,  4);\n        vst2_u8(out + i*2, o);\n#endif\n\n        // \"previous\" value for next iter\n        t1 = 3*in_near[i+7] + in_far[i+7];\n    }\n\n    t0 = t1;\n    t1 = 3*in_near[i] + in_far[i];\n    out[i*2] = stbi__div16(3*t1 + t0 + 8);\n\n    for (++i; i < w; ++i) {\n        t0 = t1;\n        t1 = 3*in_near[i]+in_far[i];\n        out[i*2-1] = stbi__div16(3*t0 + t1 + 8);\n        out[i*2  ] = stbi__div16(3*t1 + t0 + 8);\n    }\n    out[w*2-1] = stbi__div4(t1+2);\n\n    STBI_NOTUSED(hs);\n\n    return out;\n}\n#endif\n\nstatic stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) {\n    // resample with nearest-neighbor\n    int i,j;\n    STBI_NOTUSED(in_far);\n    for (i=0; i < w; ++i)\n        for (j=0; j < hs; ++j)\n            out[i*hs+j] = in_near[i];\n    return out;\n}\n\n// this is a reduced-precision calculation of YCbCr-to-RGB introduced\n// to make sure the code produces the same results in both SIMD and scalar\n#define stbi__float2fixed(x)  (((int) ((x) * 4096.0f + 0.5f)) << 8)\nstatic void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) {\n    int i;\n    for (i=0; i < count; ++i) {\n        int y_fixed = (y[i] << 20) + (1<<19); // rounding\n        int r,g,b;\n        int cr = pcr[i] - 128;\n        int cb = pcb[i] - 128;\n        r = y_fixed +  cr* stbi__float2fixed(1.40200f);\n        g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000);\n        b = y_fixed                                     +   cb* stbi__float2fixed(1.77200f);\n        r >>= 20;\n        g >>= 20;\n        b >>= 20;\n        if ((unsigned) r > 255) {\n            if (r < 0) r = 0;\n            else r = 255;\n        }\n        if ((unsigned) g > 255) {\n            if (g < 0) g = 0;\n            else g = 255;\n        }\n        if ((unsigned) b > 255) {\n            if (b < 0) b = 0;\n            else b = 255;\n        }\n        out[0] = (stbi_uc)r;\n        out[1] = (stbi_uc)g;\n        out[2] = (stbi_uc)b;\n        out[3] = 255;\n        out += step;\n    }\n}\n\n#if defined(STBI_SSE2) || defined(STBI_NEON)\nstatic void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) {\n    int i = 0;\n\n#ifdef STBI_SSE2\n    // step == 3 is pretty ugly on the final interleave, and i'm not convinced\n    // it's useful in practice (you wouldn't use it for textures, for example).\n    // so just accelerate step == 4 case.\n    if (step == 4) {\n        // this is a fairly straightforward implementation and not super-optimized.\n        __m128i signflip  = _mm_set1_epi8(-0x80);\n        __m128i cr_const0 = _mm_set1_epi16(   (short) ( 1.40200f*4096.0f+0.5f));\n        __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f));\n        __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f));\n        __m128i cb_const1 = _mm_set1_epi16(   (short) ( 1.77200f*4096.0f+0.5f));\n        __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128);\n        __m128i xw = _mm_set1_epi16(255); // alpha channel\n\n        for (; i+7 < count; i += 8) {\n            // load\n            __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i));\n            __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i));\n            __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i));\n            __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128\n            __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128\n\n            // unpack to short (and left-shift cr, cb by 8)\n            __m128i yw  = _mm_unpacklo_epi8(y_bias, y_bytes);\n            __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased);\n            __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased);\n\n            // color transform\n            __m128i yws = _mm_srli_epi16(yw, 4);\n            __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw);\n            __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw);\n            __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1);\n            __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1);\n            __m128i rws = _mm_add_epi16(cr0, yws);\n            __m128i gwt = _mm_add_epi16(cb0, yws);\n            __m128i bws = _mm_add_epi16(yws, cb1);\n            __m128i gws = _mm_add_epi16(gwt, cr1);\n\n            // descale\n            __m128i rw = _mm_srai_epi16(rws, 4);\n            __m128i bw = _mm_srai_epi16(bws, 4);\n            __m128i gw = _mm_srai_epi16(gws, 4);\n\n            // back to byte, set up for transpose\n            __m128i brb = _mm_packus_epi16(rw, bw);\n            __m128i gxb = _mm_packus_epi16(gw, xw);\n\n            // transpose to interleave channels\n            __m128i t0 = _mm_unpacklo_epi8(brb, gxb);\n            __m128i t1 = _mm_unpackhi_epi8(brb, gxb);\n            __m128i o0 = _mm_unpacklo_epi16(t0, t1);\n            __m128i o1 = _mm_unpackhi_epi16(t0, t1);\n\n            // store\n            _mm_storeu_si128((__m128i *) (out + 0), o0);\n            _mm_storeu_si128((__m128i *) (out + 16), o1);\n            out += 32;\n        }\n    }\n#endif\n\n#ifdef STBI_NEON\n    // in this version, step=3 support would be easy to add. but is there demand?\n    if (step == 4) {\n        // this is a fairly straightforward implementation and not super-optimized.\n        uint8x8_t signflip = vdup_n_u8(0x80);\n        int16x8_t cr_const0 = vdupq_n_s16(   (short) ( 1.40200f*4096.0f+0.5f));\n        int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f));\n        int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f));\n        int16x8_t cb_const1 = vdupq_n_s16(   (short) ( 1.77200f*4096.0f+0.5f));\n\n        for (; i+7 < count; i += 8) {\n            // load\n            uint8x8_t y_bytes  = vld1_u8(y + i);\n            uint8x8_t cr_bytes = vld1_u8(pcr + i);\n            uint8x8_t cb_bytes = vld1_u8(pcb + i);\n            int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip));\n            int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip));\n\n            // expand to s16\n            int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4));\n            int16x8_t crw = vshll_n_s8(cr_biased, 7);\n            int16x8_t cbw = vshll_n_s8(cb_biased, 7);\n\n            // color transform\n            int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0);\n            int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0);\n            int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1);\n            int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1);\n            int16x8_t rws = vaddq_s16(yws, cr0);\n            int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1);\n            int16x8_t bws = vaddq_s16(yws, cb1);\n\n            // undo scaling, round, convert to byte\n            uint8x8x4_t o;\n            o.val[0] = vqrshrun_n_s16(rws, 4);\n            o.val[1] = vqrshrun_n_s16(gws, 4);\n            o.val[2] = vqrshrun_n_s16(bws, 4);\n            o.val[3] = vdup_n_u8(255);\n\n            // store, interleaving r/g/b/a\n            vst4_u8(out, o);\n            out += 8*4;\n        }\n    }\n#endif\n\n    for (; i < count; ++i) {\n        int y_fixed = (y[i] << 20) + (1<<19); // rounding\n        int r,g,b;\n        int cr = pcr[i] - 128;\n        int cb = pcb[i] - 128;\n        r = y_fixed + cr* stbi__float2fixed(1.40200f);\n        g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000);\n        b = y_fixed                                   +   cb* stbi__float2fixed(1.77200f);\n        r >>= 20;\n        g >>= 20;\n        b >>= 20;\n        if ((unsigned) r > 255) {\n            if (r < 0) r = 0;\n            else r = 255;\n        }\n        if ((unsigned) g > 255) {\n            if (g < 0) g = 0;\n            else g = 255;\n        }\n        if ((unsigned) b > 255) {\n            if (b < 0) b = 0;\n            else b = 255;\n        }\n        out[0] = (stbi_uc)r;\n        out[1] = (stbi_uc)g;\n        out[2] = (stbi_uc)b;\n        out[3] = 255;\n        out += step;\n    }\n}\n#endif\n\n// set up the kernels\nstatic void stbi__setup_jpeg(stbi__jpeg *j) {\n    j->idct_block_kernel = stbi__idct_block;\n    j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row;\n    j->resample_row_hv_2_kernel = stbi__resample_row_hv_2;\n\n#ifdef STBI_SSE2\n    if (stbi__sse2_available()) {\n        j->idct_block_kernel = stbi__idct_simd;\n        j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd;\n        j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd;\n    }\n#endif\n\n#ifdef STBI_NEON\n    j->idct_block_kernel = stbi__idct_simd;\n    j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd;\n    j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd;\n#endif\n}\n\n// clean up the temporary component buffers\nstatic void stbi__cleanup_jpeg(stbi__jpeg *j) {\n    stbi__free_jpeg_components(j, j->s->img_n, 0);\n}\n\ntypedef struct {\n    resample_row_func resample;\n    stbi_uc *line0,*line1;\n    int hs,vs;   // expansion factor in each axis\n    int w_lores; // horizontal pixels pre-expansion\n    int ystep;   // how far through vertical expansion we are\n    int ypos;    // which pre-expansion row we're on\n} stbi__resample;\n\n// fast 0..255 * 0..255 => 0..255 rounded multiplication\nstatic stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) {\n    unsigned int t = x*y + 128;\n    return (stbi_uc) ((t + (t >>8)) >> 8);\n}\n\nstatic stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) {\n    int n, decode_n, is_rgb;\n    z->s->img_n = 0; // make stbi__cleanup_jpeg safe\n\n    // validate req_comp\n    if (req_comp < 0 || req_comp > 4) return stbi__errpuc(\"bad req_comp\", \"Internal error\");\n\n    // load a jpeg image from whichever source, but leave in YCbCr format\n    if (!stbi__decode_jpeg_image(z)) {\n        stbi__cleanup_jpeg(z);\n        return NULL;\n    }\n\n    // determine actual number of components to generate\n    n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1;\n\n    is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif));\n\n    if (z->s->img_n == 3 && n < 3 && !is_rgb)\n        decode_n = 1;\n    else\n        decode_n = z->s->img_n;\n\n    // resample and color-convert\n    {\n        int k;\n        unsigned int i,j;\n        stbi_uc *output;\n        stbi_uc *coutput[4];\n\n        stbi__resample res_comp[4];\n\n        for (k=0; k < decode_n; ++k) {\n            stbi__resample *r = &res_comp[k];\n\n            // allocate line buffer big enough for upsampling off the edges\n            // with upsample factor of 4\n            z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3);\n            if (!z->img_comp[k].linebuf) {\n                stbi__cleanup_jpeg(z);\n                return stbi__errpuc(\"outofmem\", \"Out of memory\");\n            }\n\n            r->hs      = z->img_h_max / z->img_comp[k].h;\n            r->vs      = z->img_v_max / z->img_comp[k].v;\n            r->ystep   = r->vs >> 1;\n            r->w_lores = (z->s->img_x + r->hs-1) / r->hs;\n            r->ypos    = 0;\n            r->line0   = r->line1 = z->img_comp[k].data;\n\n            if      (r->hs == 1 && r->vs == 1) r->resample = resample_row_1;\n            else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2;\n            else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2;\n            else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel;\n            else                               r->resample = stbi__resample_row_generic;\n        }\n\n        // can't error after this so, this is safe\n        output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1);\n        if (!output) {\n            stbi__cleanup_jpeg(z);\n            return stbi__errpuc(\"outofmem\", \"Out of memory\");\n        }\n\n        // now go ahead and resample\n        for (j=0; j < z->s->img_y; ++j) {\n            stbi_uc *out = output + n * z->s->img_x * j;\n            for (k=0; k < decode_n; ++k) {\n                stbi__resample *r = &res_comp[k];\n                int y_bot = r->ystep >= (r->vs >> 1);\n                coutput[k] = r->resample(z->img_comp[k].linebuf,\n                                         y_bot ? r->line1 : r->line0,\n                                         y_bot ? r->line0 : r->line1,\n                                         r->w_lores, r->hs);\n                if (++r->ystep >= r->vs) {\n                    r->ystep = 0;\n                    r->line0 = r->line1;\n                    if (++r->ypos < z->img_comp[k].y)\n                        r->line1 += z->img_comp[k].w2;\n                }\n            }\n            if (n >= 3) {\n                stbi_uc *y = coutput[0];\n                if (z->s->img_n == 3) {\n                    if (is_rgb) {\n                        for (i=0; i < z->s->img_x; ++i) {\n                            out[0] = y[i];\n                            out[1] = coutput[1][i];\n                            out[2] = coutput[2][i];\n                            out[3] = 255;\n                            out += n;\n                        }\n                    } else {\n                        z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n);\n                    }\n                } else if (z->s->img_n == 4) {\n                    if (z->app14_color_transform == 0) { // CMYK\n                        for (i=0; i < z->s->img_x; ++i) {\n                            stbi_uc m = coutput[3][i];\n                            out[0] = stbi__blinn_8x8(coutput[0][i], m);\n                            out[1] = stbi__blinn_8x8(coutput[1][i], m);\n                            out[2] = stbi__blinn_8x8(coutput[2][i], m);\n                            out[3] = 255;\n                            out += n;\n                        }\n                    } else if (z->app14_color_transform == 2) { // YCCK\n                        z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n);\n                        for (i=0; i < z->s->img_x; ++i) {\n                            stbi_uc m = coutput[3][i];\n                            out[0] = stbi__blinn_8x8(255 - out[0], m);\n                            out[1] = stbi__blinn_8x8(255 - out[1], m);\n                            out[2] = stbi__blinn_8x8(255 - out[2], m);\n                            out += n;\n                        }\n                    } else { // YCbCr + alpha?  Ignore the fourth channel for now\n                        z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n);\n                    }\n                } else\n                    for (i=0; i < z->s->img_x; ++i) {\n                        out[0] = out[1] = out[2] = y[i];\n                        out[3] = 255; // not used if n==3\n                        out += n;\n                    }\n            } else {\n                if (is_rgb) {\n                    if (n == 1)\n                        for (i=0; i < z->s->img_x; ++i)\n                            *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]);\n                    else {\n                        for (i=0; i < z->s->img_x; ++i, out += 2) {\n                            out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]);\n                            out[1] = 255;\n                        }\n                    }\n                } else if (z->s->img_n == 4 && z->app14_color_transform == 0) {\n                    for (i=0; i < z->s->img_x; ++i) {\n                        stbi_uc m = coutput[3][i];\n                        stbi_uc r = stbi__blinn_8x8(coutput[0][i], m);\n                        stbi_uc g = stbi__blinn_8x8(coutput[1][i], m);\n                        stbi_uc b = stbi__blinn_8x8(coutput[2][i], m);\n                        out[0] = stbi__compute_y(r, g, b);\n                        out[1] = 255;\n                        out += n;\n                    }\n                } else if (z->s->img_n == 4 && z->app14_color_transform == 2) {\n                    for (i=0; i < z->s->img_x; ++i) {\n                        out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]);\n                        out[1] = 255;\n                        out += n;\n                    }\n                } else {\n                    stbi_uc *y = coutput[0];\n                    if (n == 1)\n                        for (i=0; i < z->s->img_x; ++i) out[i] = y[i];\n                    else\n                        for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255;\n                }\n            }\n        }\n        stbi__cleanup_jpeg(z);\n        *out_x = z->s->img_x;\n        *out_y = z->s->img_y;\n        if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output\n        return output;\n    }\n}\n\nstatic void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) {\n    unsigned char* result;\n    stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg));\n    STBI_NOTUSED(ri);\n    j->s = s;\n    stbi__setup_jpeg(j);\n    result = load_jpeg_image(j, x,y,comp,req_comp);\n    STBI_FREE(j);\n    return result;\n}\n\nstatic int stbi__jpeg_test(stbi__context *s) {\n    int r;\n    stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg));\n    j->s = s;\n    stbi__setup_jpeg(j);\n    r = stbi__decode_jpeg_header(j, STBI__SCAN_type);\n    stbi__rewind(s);\n    STBI_FREE(j);\n    return r;\n}\n\nstatic int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) {\n    if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) {\n        stbi__rewind( j->s );\n        return 0;\n    }\n    if (x) *x = j->s->img_x;\n    if (y) *y = j->s->img_y;\n    if (comp) *comp = j->s->img_n >= 3 ? 3 : 1;\n    return 1;\n}\n\nstatic int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) {\n    int result;\n    stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg)));\n    j->s = s;\n    result = stbi__jpeg_info_raw(j, x, y, comp);\n    STBI_FREE(j);\n    return result;\n}\n#endif\n\n// public domain zlib decode    v0.2  Sean Barrett 2006-11-18\n//    simple implementation\n//      - all input must be provided in an upfront buffer\n//      - all output is written to a single output buffer (can malloc/realloc)\n//    performance\n//      - fast huffman\n\n#ifndef STBI_NO_ZLIB\n\n// fast-way is faster to check than jpeg huffman, but slow way is slower\n#define STBI__ZFAST_BITS  9 // accelerate all cases in default tables\n#define STBI__ZFAST_MASK  ((1 << STBI__ZFAST_BITS) - 1)\n\n// zlib-style huffman encoding\n// (jpegs packs from left, zlib from right, so can't share code)\ntypedef struct {\n    stbi__uint16 fast[1 << STBI__ZFAST_BITS];\n    stbi__uint16 firstcode[16];\n    int maxcode[17];\n    stbi__uint16 firstsymbol[16];\n    stbi_uc  size[288];\n    stbi__uint16 value[288];\n} stbi__zhuffman;\n\nstbi_inline static int stbi__bitreverse16(int n) {\n    n = ((n & 0xAAAA) >>  1) | ((n & 0x5555) << 1);\n    n = ((n & 0xCCCC) >>  2) | ((n & 0x3333) << 2);\n    n = ((n & 0xF0F0) >>  4) | ((n & 0x0F0F) << 4);\n    n = ((n & 0xFF00) >>  8) | ((n & 0x00FF) << 8);\n    return n;\n}\n\nstbi_inline static int stbi__bit_reverse(int v, int bits) {\n    STBI_ASSERT(bits <= 16);\n    // to bit reverse n bits, reverse 16 and shift\n    // e.g. 11 bits, bit reverse and shift away 5\n    return stbi__bitreverse16(v) >> (16-bits);\n}\n\nstatic int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) {\n    int i,k=0;\n    int code, next_code[16], sizes[17];\n\n    // DEFLATE spec for generating codes\n    memset(sizes, 0, sizeof(sizes));\n    memset(z->fast, 0, sizeof(z->fast));\n    for (i=0; i < num; ++i)\n        ++sizes[sizelist[i]];\n    sizes[0] = 0;\n    for (i=1; i < 16; ++i)\n        if (sizes[i] > (1 << i))\n            return stbi__err(\"bad sizes\", \"Corrupt PNG\");\n    code = 0;\n    for (i=1; i < 16; ++i) {\n        next_code[i] = code;\n        z->firstcode[i] = (stbi__uint16) code;\n        z->firstsymbol[i] = (stbi__uint16) k;\n        code = (code + sizes[i]);\n        if (sizes[i])\n            if (code-1 >= (1 << i)) return stbi__err(\"bad codelengths\",\"Corrupt PNG\");\n        z->maxcode[i] = code << (16-i); // preshift for inner loop\n        code <<= 1;\n        k += sizes[i];\n    }\n    z->maxcode[16] = 0x10000; // sentinel\n    for (i=0; i < num; ++i) {\n        int s = sizelist[i];\n        if (s) {\n            int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s];\n            stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i);\n            z->size [c] = (stbi_uc     ) s;\n            z->value[c] = (stbi__uint16) i;\n            if (s <= STBI__ZFAST_BITS) {\n                int j = stbi__bit_reverse(next_code[s],s);\n                while (j < (1 << STBI__ZFAST_BITS)) {\n                    z->fast[j] = fastv;\n                    j += (1 << s);\n                }\n            }\n            ++next_code[s];\n        }\n    }\n    return 1;\n}\n\n// zlib-from-memory implementation for PNG reading\n//    because PNG allows splitting the zlib stream arbitrarily,\n//    and it's annoying structurally to have PNG call ZLIB call PNG,\n//    we require PNG read all the IDATs and combine them into a single\n//    memory buffer\n\ntypedef struct {\n    stbi_uc *zbuffer, *zbuffer_end;\n    int num_bits;\n    stbi__uint32 code_buffer;\n\n    char *zout;\n    char *zout_start;\n    char *zout_end;\n    int   z_expandable;\n\n    stbi__zhuffman z_length, z_distance;\n} stbi__zbuf;\n\nstbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) {\n    if (z->zbuffer >= z->zbuffer_end) return 0;\n    return *z->zbuffer++;\n}\n\nstatic void stbi__fill_bits(stbi__zbuf *z) {\n    do {\n        STBI_ASSERT(z->code_buffer < (1U << z->num_bits));\n        z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits;\n        z->num_bits += 8;\n    } while (z->num_bits <= 24);\n}\n\nstbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) {\n    unsigned int k;\n    if (z->num_bits < n) stbi__fill_bits(z);\n    k = z->code_buffer & ((1 << n) - 1);\n    z->code_buffer >>= n;\n    z->num_bits -= n;\n    return k;\n}\n\nstatic int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) {\n    int b,s,k;\n    // not resolved by fast table, so compute it the slow way\n    // use jpeg approach, which requires MSbits at top\n    k = stbi__bit_reverse(a->code_buffer, 16);\n    for (s=STBI__ZFAST_BITS+1; ; ++s)\n        if (k < z->maxcode[s])\n            break;\n    if (s == 16) return -1; // invalid code!\n    // code size is s, so:\n    b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s];\n    STBI_ASSERT(z->size[b] == s);\n    a->code_buffer >>= s;\n    a->num_bits -= s;\n    return z->value[b];\n}\n\nstbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) {\n    int b,s;\n    if (a->num_bits < 16) stbi__fill_bits(a);\n    b = z->fast[a->code_buffer & STBI__ZFAST_MASK];\n    if (b) {\n        s = b >> 9;\n        a->code_buffer >>= s;\n        a->num_bits -= s;\n        return b & 511;\n    }\n    return stbi__zhuffman_decode_slowpath(a, z);\n}\n\nstatic int stbi__zexpand(stbi__zbuf *z, char *zout, int n) { // need to make room for n bytes\n    char *q;\n    int cur, limit, old_limit;\n    z->zout = zout;\n    if (!z->z_expandable) return stbi__err(\"output buffer limit\",\"Corrupt PNG\");\n    cur   = (int) (z->zout     - z->zout_start);\n    limit = old_limit = (int) (z->zout_end - z->zout_start);\n    while (cur + n > limit)\n        limit *= 2;\n    q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit);\n    STBI_NOTUSED(old_limit);\n    if (q == NULL) return stbi__err(\"outofmem\", \"Out of memory\");\n    z->zout_start = q;\n    z->zout       = q + cur;\n    z->zout_end   = q + limit;\n    return 1;\n}\n\nstatic const int stbi__zlength_base[31] = {\n    3,4,5,6,7,8,9,10,11,13,\n    15,17,19,23,27,31,35,43,51,59,\n    67,83,99,115,131,163,195,227,258,0,0\n};\n\nstatic const int stbi__zlength_extra[31]=\n{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 };\n\nstatic const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,\n                                          257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0\n                                        };\n\nstatic const int stbi__zdist_extra[32] =\n{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};\n\nstatic int stbi__parse_huffman_block(stbi__zbuf *a) {\n    char *zout = a->zout;\n    for(;;) {\n        int z = stbi__zhuffman_decode(a, &a->z_length);\n        if (z < 256) {\n            if (z < 0) return stbi__err(\"bad huffman code\",\"Corrupt PNG\"); // error in huffman codes\n            if (zout >= a->zout_end) {\n                if (!stbi__zexpand(a, zout, 1)) return 0;\n                zout = a->zout;\n            }\n            *zout++ = (char) z;\n        } else {\n            stbi_uc *p;\n            int len,dist;\n            if (z == 256) {\n                a->zout = zout;\n                return 1;\n            }\n            z -= 257;\n            len = stbi__zlength_base[z];\n            if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]);\n            z = stbi__zhuffman_decode(a, &a->z_distance);\n            if (z < 0) return stbi__err(\"bad huffman code\",\"Corrupt PNG\");\n            dist = stbi__zdist_base[z];\n            if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]);\n            if (zout - a->zout_start < dist) return stbi__err(\"bad dist\",\"Corrupt PNG\");\n            if (zout + len > a->zout_end) {\n                if (!stbi__zexpand(a, zout, len)) return 0;\n                zout = a->zout;\n            }\n            p = (stbi_uc *) (zout - dist);\n            if (dist == 1) { // run of one byte; common in images.\n                stbi_uc v = *p;\n                if (len) {\n                    do *zout++ = v;\n                    while (--len);\n                }\n            } else {\n                if (len) {\n                    do *zout++ = *p++;\n                    while (--len);\n                }\n            }\n        }\n    }\n}\n\nstatic int stbi__compute_huffman_codes(stbi__zbuf *a) {\n    static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 };\n    stbi__zhuffman z_codelength;\n    stbi_uc lencodes[286+32+137];//padding for maximum single op\n    stbi_uc codelength_sizes[19];\n    int i,n;\n\n    int hlit  = stbi__zreceive(a,5) + 257;\n    int hdist = stbi__zreceive(a,5) + 1;\n    int hclen = stbi__zreceive(a,4) + 4;\n    int ntot  = hlit + hdist;\n\n    memset(codelength_sizes, 0, sizeof(codelength_sizes));\n    for (i=0; i < hclen; ++i) {\n        int s = stbi__zreceive(a,3);\n        codelength_sizes[length_dezigzag[i]] = (stbi_uc) s;\n    }\n    if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0;\n\n    n = 0;\n    while (n < ntot) {\n        int c = stbi__zhuffman_decode(a, &z_codelength);\n        if (c < 0 || c >= 19) return stbi__err(\"bad codelengths\", \"Corrupt PNG\");\n        if (c < 16)\n            lencodes[n++] = (stbi_uc) c;\n        else {\n            stbi_uc fill = 0;\n            if (c == 16) {\n                c = stbi__zreceive(a,2)+3;\n                if (n == 0) return stbi__err(\"bad codelengths\", \"Corrupt PNG\");\n                fill = lencodes[n-1];\n            } else if (c == 17)\n                c = stbi__zreceive(a,3)+3;\n            else {\n                STBI_ASSERT(c == 18);\n                c = stbi__zreceive(a,7)+11;\n            }\n            if (ntot - n < c) return stbi__err(\"bad codelengths\", \"Corrupt PNG\");\n            memset(lencodes+n, fill, c);\n            n += c;\n        }\n    }\n    if (n != ntot) return stbi__err(\"bad codelengths\",\"Corrupt PNG\");\n    if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0;\n    if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0;\n    return 1;\n}\n\nstatic int stbi__parse_uncompressed_block(stbi__zbuf *a) {\n    stbi_uc header[4];\n    int len,nlen,k;\n    if (a->num_bits & 7)\n        stbi__zreceive(a, a->num_bits & 7); // discard\n    // drain the bit-packed data into header\n    k = 0;\n    while (a->num_bits > 0) {\n        header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check\n        a->code_buffer >>= 8;\n        a->num_bits -= 8;\n    }\n    STBI_ASSERT(a->num_bits == 0);\n    // now fill header the normal way\n    while (k < 4)\n        header[k++] = stbi__zget8(a);\n    len  = header[1] * 256 + header[0];\n    nlen = header[3] * 256 + header[2];\n    if (nlen != (len ^ 0xffff)) return stbi__err(\"zlib corrupt\",\"Corrupt PNG\");\n    if (a->zbuffer + len > a->zbuffer_end) return stbi__err(\"read past buffer\",\"Corrupt PNG\");\n    if (a->zout + len > a->zout_end)\n        if (!stbi__zexpand(a, a->zout, len)) return 0;\n    memcpy(a->zout, a->zbuffer, len);\n    a->zbuffer += len;\n    a->zout += len;\n    return 1;\n}\n\nstatic int stbi__parse_zlib_header(stbi__zbuf *a) {\n    int cmf   = stbi__zget8(a);\n    int cm    = cmf & 15;\n    /* int cinfo = cmf >> 4; */\n    int flg   = stbi__zget8(a);\n    if ((cmf*256+flg) % 31 != 0) return stbi__err(\"bad zlib header\",\"Corrupt PNG\"); // zlib spec\n    if (flg & 32) return stbi__err(\"no preset dict\",\"Corrupt PNG\"); // preset dictionary not allowed in png\n    if (cm != 8) return stbi__err(\"bad compression\",\"Corrupt PNG\"); // DEFLATE required for png\n    // window = 1 << (8 + cinfo)... but who cares, we fully buffer output\n    return 1;\n}\n\nstatic const stbi_uc stbi__zdefault_length[288] = {\n    8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,\n    8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,\n    8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,\n    8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,\n    8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,\n    9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,\n    9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,\n    9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,\n    7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8\n};\nstatic const stbi_uc stbi__zdefault_distance[32] = {\n    5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5\n};\n/*\nInit algorithm:\n{\n   int i;   // use <= to match clearly with spec\n   for (i=0; i <= 143; ++i)     stbi__zdefault_length[i]   = 8;\n   for (   ; i <= 255; ++i)     stbi__zdefault_length[i]   = 9;\n   for (   ; i <= 279; ++i)     stbi__zdefault_length[i]   = 7;\n   for (   ; i <= 287; ++i)     stbi__zdefault_length[i]   = 8;\n\n   for (i=0; i <=  31; ++i)     stbi__zdefault_distance[i] = 5;\n}\n*/\n\nstatic int stbi__parse_zlib(stbi__zbuf *a, int parse_header) {\n    int final, type;\n    if (parse_header)\n        if (!stbi__parse_zlib_header(a)) return 0;\n    a->num_bits = 0;\n    a->code_buffer = 0;\n    do {\n        final = stbi__zreceive(a,1);\n        type = stbi__zreceive(a,2);\n        if (type == 0) {\n            if (!stbi__parse_uncompressed_block(a)) return 0;\n        } else if (type == 3) {\n            return 0;\n        } else {\n            if (type == 1) {\n                // use fixed code lengths\n                if (!stbi__zbuild_huffman(&a->z_length, stbi__zdefault_length, 288)) return 0;\n                if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance,  32)) return 0;\n            } else {\n                if (!stbi__compute_huffman_codes(a)) return 0;\n            }\n            if (!stbi__parse_huffman_block(a)) return 0;\n        }\n    } while (!final);\n    return 1;\n}\n\nstatic int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) {\n    a->zout_start = obuf;\n    a->zout       = obuf;\n    a->zout_end   = obuf + olen;\n    a->z_expandable = exp;\n\n    return stbi__parse_zlib(a, parse_header);\n}\n\nSTBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) {\n    stbi__zbuf a;\n    char *p = (char *) stbi__malloc(initial_size);\n    if (p == NULL) return NULL;\n    a.zbuffer = (stbi_uc *) buffer;\n    a.zbuffer_end = (stbi_uc *) buffer + len;\n    if (stbi__do_zlib(&a, p, initial_size, 1, 1)) {\n        if (outlen) *outlen = (int) (a.zout - a.zout_start);\n        return a.zout_start;\n    } else {\n        STBI_FREE(a.zout_start);\n        return NULL;\n    }\n}\n\nSTBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) {\n    return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen);\n}\n\nSTBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) {\n    stbi__zbuf a;\n    char *p = (char *) stbi__malloc(initial_size);\n    if (p == NULL) return NULL;\n    a.zbuffer = (stbi_uc *) buffer;\n    a.zbuffer_end = (stbi_uc *) buffer + len;\n    if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) {\n        if (outlen) *outlen = (int) (a.zout - a.zout_start);\n        return a.zout_start;\n    } else {\n        STBI_FREE(a.zout_start);\n        return NULL;\n    }\n}\n\nSTBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) {\n    stbi__zbuf a;\n    a.zbuffer = (stbi_uc *) ibuffer;\n    a.zbuffer_end = (stbi_uc *) ibuffer + ilen;\n    if (stbi__do_zlib(&a, obuffer, olen, 0, 1))\n        return (int) (a.zout - a.zout_start);\n    else\n        return -1;\n}\n\nSTBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) {\n    stbi__zbuf a;\n    char *p = (char *) stbi__malloc(16384);\n    if (p == NULL) return NULL;\n    a.zbuffer = (stbi_uc *) buffer;\n    a.zbuffer_end = (stbi_uc *) buffer+len;\n    if (stbi__do_zlib(&a, p, 16384, 1, 0)) {\n        if (outlen) *outlen = (int) (a.zout - a.zout_start);\n        return a.zout_start;\n    } else {\n        STBI_FREE(a.zout_start);\n        return NULL;\n    }\n}\n\nSTBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) {\n    stbi__zbuf a;\n    a.zbuffer = (stbi_uc *) ibuffer;\n    a.zbuffer_end = (stbi_uc *) ibuffer + ilen;\n    if (stbi__do_zlib(&a, obuffer, olen, 0, 0))\n        return (int) (a.zout - a.zout_start);\n    else\n        return -1;\n}\n#endif\n\n// public domain \"baseline\" PNG decoder   v0.10  Sean Barrett 2006-11-18\n//    simple implementation\n//      - only 8-bit samples\n//      - no CRC checking\n//      - allocates lots of intermediate memory\n//        - avoids problem of streaming data between subsystems\n//        - avoids explicit window management\n//    performance\n//      - uses stb_zlib, a PD zlib implementation with fast huffman decoding\n\n#ifndef STBI_NO_PNG\ntypedef struct {\n    stbi__uint32 length;\n    stbi__uint32 type;\n} stbi__pngchunk;\n\nstatic stbi__pngchunk stbi__get_chunk_header(stbi__context *s) {\n    stbi__pngchunk c;\n    c.length = stbi__get32be(s);\n    c.type   = stbi__get32be(s);\n    return c;\n}\n\nstatic int stbi__check_png_header(stbi__context *s) {\n    static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 };\n    int i;\n    for (i=0; i < 8; ++i)\n        if (stbi__get8(s) != png_sig[i]) return stbi__err(\"bad png sig\",\"Not a PNG\");\n    return 1;\n}\n\ntypedef struct {\n    stbi__context *s;\n    stbi_uc *idata, *expanded, *out;\n    int depth;\n} stbi__png;\n\n\nenum {\n    STBI__F_none=0,\n    STBI__F_sub=1,\n    STBI__F_up=2,\n    STBI__F_avg=3,\n    STBI__F_paeth=4,\n    // synthetic filters used for first scanline to avoid needing a dummy row of 0s\n    STBI__F_avg_first,\n    STBI__F_paeth_first\n};\n\nstatic stbi_uc first_row_filter[5] = {\n    STBI__F_none,\n    STBI__F_sub,\n    STBI__F_none,\n    STBI__F_avg_first,\n    STBI__F_paeth_first\n};\n\nstatic int stbi__paeth(int a, int b, int c) {\n    int p = a + b - c;\n    int pa = abs(p-a);\n    int pb = abs(p-b);\n    int pc = abs(p-c);\n    if (pa <= pb && pa <= pc) return a;\n    if (pb <= pc) return b;\n    return c;\n}\n\nstatic const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 };\n\n// create the png data from post-deflated data\nstatic int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) {\n    int bytes = (depth == 16? 2 : 1);\n    stbi__context *s = a->s;\n    stbi__uint32 i,j,stride = x*out_n*bytes;\n    stbi__uint32 img_len, img_width_bytes;\n    int k;\n    int img_n = s->img_n; // copy it into a local for later\n\n    int output_bytes = out_n*bytes;\n    int filter_bytes = img_n*bytes;\n    int width = x;\n\n    STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1);\n    a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into\n    if (!a->out) return stbi__err(\"outofmem\", \"Out of memory\");\n\n    if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err(\"too large\", \"Corrupt PNG\");\n    img_width_bytes = (((img_n * x * depth) + 7) >> 3);\n    img_len = (img_width_bytes + 1) * y;\n\n    // we used to check for exact match between raw_len and img_len on non-interlaced PNGs,\n    // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros),\n    // so just check for raw_len < img_len always.\n    if (raw_len < img_len) return stbi__err(\"not enough pixels\",\"Corrupt PNG\");\n\n    for (j=0; j < y; ++j) {\n        stbi_uc *cur = a->out + stride*j;\n        stbi_uc *prior;\n        int filter = *raw++;\n\n        if (filter > 4)\n            return stbi__err(\"invalid filter\",\"Corrupt PNG\");\n\n        if (depth < 8) {\n            STBI_ASSERT(img_width_bytes <= x);\n            cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place\n            filter_bytes = 1;\n            width = img_width_bytes;\n        }\n        prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above\n\n        // if first row, use special filter that doesn't sample previous row\n        if (j == 0) filter = first_row_filter[filter];\n\n        // handle first byte explicitly\n        for (k=0; k < filter_bytes; ++k) {\n            switch (filter) {\n            case STBI__F_none       :\n                cur[k] = raw[k];\n                break;\n            case STBI__F_sub        :\n                cur[k] = raw[k];\n                break;\n            case STBI__F_up         :\n                cur[k] = STBI__BYTECAST(raw[k] + prior[k]);\n                break;\n            case STBI__F_avg        :\n                cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1));\n                break;\n            case STBI__F_paeth      :\n                cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0));\n                break;\n            case STBI__F_avg_first  :\n                cur[k] = raw[k];\n                break;\n            case STBI__F_paeth_first:\n                cur[k] = raw[k];\n                break;\n            }\n        }\n\n        if (depth == 8) {\n            if (img_n != out_n)\n                cur[img_n] = 255; // first pixel\n            raw += img_n;\n            cur += out_n;\n            prior += out_n;\n        } else if (depth == 16) {\n            if (img_n != out_n) {\n                cur[filter_bytes]   = 255; // first pixel top byte\n                cur[filter_bytes+1] = 255; // first pixel bottom byte\n            }\n            raw += filter_bytes;\n            cur += output_bytes;\n            prior += output_bytes;\n        } else {\n            raw += 1;\n            cur += 1;\n            prior += 1;\n        }\n\n        // this is a little gross, so that we don't switch per-pixel or per-component\n        if (depth < 8 || img_n == out_n) {\n            int nk = (width - 1)*filter_bytes;\n#define STBI__CASE(f) \\\n             case f:     \\\n                for (k=0; k < nk; ++k)\n            switch (filter) {\n            // \"none\" filter turns into a memcpy here; make that explicit.\n            case STBI__F_none:\n                memcpy(cur, raw, nk);\n                break;\n                STBI__CASE(STBI__F_sub)          {\n                    cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]);\n                }\n                break;\n                STBI__CASE(STBI__F_up)           {\n                    cur[k] = STBI__BYTECAST(raw[k] + prior[k]);\n                }\n                break;\n                STBI__CASE(STBI__F_avg)          {\n                    cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1));\n                }\n                break;\n                STBI__CASE(STBI__F_paeth)        {\n                    cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes]));\n                }\n                break;\n                STBI__CASE(STBI__F_avg_first)    {\n                    cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1));\n                }\n                break;\n                STBI__CASE(STBI__F_paeth_first)  {\n                    cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0));\n                }\n                break;\n            }\n#undef STBI__CASE\n            raw += nk;\n        } else {\n            STBI_ASSERT(img_n+1 == out_n);\n#define STBI__CASE(f) \\\n             case f:     \\\n                for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \\\n                   for (k=0; k < filter_bytes; ++k)\n            switch (filter) {\n                STBI__CASE(STBI__F_none)         {\n                    cur[k] = raw[k];\n                }\n                break;\n                STBI__CASE(STBI__F_sub)          {\n                    cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]);\n                }\n                break;\n                STBI__CASE(STBI__F_up)           {\n                    cur[k] = STBI__BYTECAST(raw[k] + prior[k]);\n                }\n                break;\n                STBI__CASE(STBI__F_avg)          {\n                    cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1));\n                }\n                break;\n                STBI__CASE(STBI__F_paeth)        {\n                    cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes]));\n                }\n                break;\n                STBI__CASE(STBI__F_avg_first)    {\n                    cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1));\n                }\n                break;\n                STBI__CASE(STBI__F_paeth_first)  {\n                    cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0));\n                }\n                break;\n            }\n#undef STBI__CASE\n\n            // the loop above sets the high byte of the pixels' alpha, but for\n            // 16 bit png files we also need the low byte set. we'll do that here.\n            if (depth == 16) {\n                cur = a->out + stride*j; // start at the beginning of the row again\n                for (i=0; i < x; ++i,cur+=output_bytes) {\n                    cur[filter_bytes+1] = 255;\n                }\n            }\n        }\n    }\n\n    // we make a separate pass to expand bits to pixels; for performance,\n    // this could run two scanlines behind the above code, so it won't\n    // intefere with filtering but will still be in the cache.\n    if (depth < 8) {\n        for (j=0; j < y; ++j) {\n            stbi_uc *cur = a->out + stride*j;\n            stbi_uc *in  = a->out + stride*j + x*out_n - img_width_bytes;\n            // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit\n            // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop\n            stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range\n\n            // note that the final byte might overshoot and write more data than desired.\n            // we can allocate enough data that this never writes out of memory, but it\n            // could also overwrite the next scanline. can it overwrite non-empty data\n            // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel.\n            // so we need to explicitly clamp the final ones\n\n            if (depth == 4) {\n                for (k=x*img_n; k >= 2; k-=2, ++in) {\n                    *cur++ = scale * ((*in >> 4)       );\n                    *cur++ = scale * ((*in     ) & 0x0f);\n                }\n                if (k > 0) *cur++ = scale * ((*in >> 4)       );\n            } else if (depth == 2) {\n                for (k=x*img_n; k >= 4; k-=4, ++in) {\n                    *cur++ = scale * ((*in >> 6)       );\n                    *cur++ = scale * ((*in >> 4) & 0x03);\n                    *cur++ = scale * ((*in >> 2) & 0x03);\n                    *cur++ = scale * ((*in     ) & 0x03);\n                }\n                if (k > 0) *cur++ = scale * ((*in >> 6)       );\n                if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03);\n                if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03);\n            } else if (depth == 1) {\n                for (k=x*img_n; k >= 8; k-=8, ++in) {\n                    *cur++ = scale * ((*in >> 7)       );\n                    *cur++ = scale * ((*in >> 6) & 0x01);\n                    *cur++ = scale * ((*in >> 5) & 0x01);\n                    *cur++ = scale * ((*in >> 4) & 0x01);\n                    *cur++ = scale * ((*in >> 3) & 0x01);\n                    *cur++ = scale * ((*in >> 2) & 0x01);\n                    *cur++ = scale * ((*in >> 1) & 0x01);\n                    *cur++ = scale * ((*in     ) & 0x01);\n                }\n                if (k > 0) *cur++ = scale * ((*in >> 7)       );\n                if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01);\n                if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01);\n                if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01);\n                if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01);\n                if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01);\n                if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01);\n            }\n            if (img_n != out_n) {\n                int q;\n                // insert alpha = 255\n                cur = a->out + stride*j;\n                if (img_n == 1) {\n                    for (q=x-1; q >= 0; --q) {\n                        cur[q*2+1] = 255;\n                        cur[q*2+0] = cur[q];\n                    }\n                } else {\n                    STBI_ASSERT(img_n == 3);\n                    for (q=x-1; q >= 0; --q) {\n                        cur[q*4+3] = 255;\n                        cur[q*4+2] = cur[q*3+2];\n                        cur[q*4+1] = cur[q*3+1];\n                        cur[q*4+0] = cur[q*3+0];\n                    }\n                }\n            }\n        }\n    } else if (depth == 16) {\n        // force the image data from big-endian to platform-native.\n        // this is done in a separate pass due to the decoding relying\n        // on the data being untouched, but could probably be done\n        // per-line during decode if care is taken.\n        stbi_uc *cur = a->out;\n        stbi__uint16 *cur16 = (stbi__uint16*)cur;\n\n        for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) {\n            *cur16 = (cur[0] << 8) | cur[1];\n        }\n    }\n\n    return 1;\n}\n\nstatic int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) {\n    int bytes = (depth == 16 ? 2 : 1);\n    int out_bytes = out_n * bytes;\n    stbi_uc *final;\n    int p;\n    if (!interlaced)\n        return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color);\n\n    // de-interlacing\n    final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0);\n    for (p=0; p < 7; ++p) {\n        int xorig[] = { 0,4,0,2,0,1,0 };\n        int yorig[] = { 0,0,4,0,2,0,1 };\n        int xspc[]  = { 8,8,4,4,2,2,1 };\n        int yspc[]  = { 8,8,8,4,4,2,2 };\n        int i,j,x,y;\n        // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1\n        x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p];\n        y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p];\n        if (x && y) {\n            stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y;\n            if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) {\n                STBI_FREE(final);\n                return 0;\n            }\n            for (j=0; j < y; ++j) {\n                for (i=0; i < x; ++i) {\n                    int out_y = j*yspc[p]+yorig[p];\n                    int out_x = i*xspc[p]+xorig[p];\n                    memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes,\n                           a->out + (j*x+i)*out_bytes, out_bytes);\n                }\n            }\n            STBI_FREE(a->out);\n            image_data += img_len;\n            image_data_len -= img_len;\n        }\n    }\n    a->out = final;\n\n    return 1;\n}\n\nstatic int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) {\n    stbi__context *s = z->s;\n    stbi__uint32 i, pixel_count = s->img_x * s->img_y;\n    stbi_uc *p = z->out;\n\n    // compute color-based transparency, assuming we've\n    // already got 255 as the alpha value in the output\n    STBI_ASSERT(out_n == 2 || out_n == 4);\n\n    if (out_n == 2) {\n        for (i=0; i < pixel_count; ++i) {\n            p[1] = (p[0] == tc[0] ? 0 : 255);\n            p += 2;\n        }\n    } else {\n        for (i=0; i < pixel_count; ++i) {\n            if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2])\n                p[3] = 0;\n            p += 4;\n        }\n    }\n    return 1;\n}\n\nstatic int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) {\n    stbi__context *s = z->s;\n    stbi__uint32 i, pixel_count = s->img_x * s->img_y;\n    stbi__uint16 *p = (stbi__uint16*) z->out;\n\n    // compute color-based transparency, assuming we've\n    // already got 65535 as the alpha value in the output\n    STBI_ASSERT(out_n == 2 || out_n == 4);\n\n    if (out_n == 2) {\n        for (i = 0; i < pixel_count; ++i) {\n            p[1] = (p[0] == tc[0] ? 0 : 65535);\n            p += 2;\n        }\n    } else {\n        for (i = 0; i < pixel_count; ++i) {\n            if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2])\n                p[3] = 0;\n            p += 4;\n        }\n    }\n    return 1;\n}\n\nstatic int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) {\n    stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y;\n    stbi_uc *p, *temp_out, *orig = a->out;\n\n    p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0);\n    if (p == NULL) return stbi__err(\"outofmem\", \"Out of memory\");\n\n    // between here and free(out) below, exitting would leak\n    temp_out = p;\n\n    if (pal_img_n == 3) {\n        for (i=0; i < pixel_count; ++i) {\n            int n = orig[i]*4;\n            p[0] = palette[n  ];\n            p[1] = palette[n+1];\n            p[2] = palette[n+2];\n            p += 3;\n        }\n    } else {\n        for (i=0; i < pixel_count; ++i) {\n            int n = orig[i]*4;\n            p[0] = palette[n  ];\n            p[1] = palette[n+1];\n            p[2] = palette[n+2];\n            p[3] = palette[n+3];\n            p += 4;\n        }\n    }\n    STBI_FREE(a->out);\n    a->out = temp_out;\n\n    STBI_NOTUSED(len);\n\n    return 1;\n}\n\nstatic int stbi__unpremultiply_on_load = 0;\nstatic int stbi__de_iphone_flag = 0;\n\nSTBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) {\n    stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply;\n}\n\nSTBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) {\n    stbi__de_iphone_flag = flag_true_if_should_convert;\n}\n\nstatic void stbi__de_iphone(stbi__png *z) {\n    stbi__context *s = z->s;\n    stbi__uint32 i, pixel_count = s->img_x * s->img_y;\n    stbi_uc *p = z->out;\n\n    if (s->img_out_n == 3) {  // convert bgr to rgb\n        for (i=0; i < pixel_count; ++i) {\n            stbi_uc t = p[0];\n            p[0] = p[2];\n            p[2] = t;\n            p += 3;\n        }\n    } else {\n        STBI_ASSERT(s->img_out_n == 4);\n        if (stbi__unpremultiply_on_load) {\n            // convert bgr to rgb and unpremultiply\n            for (i=0; i < pixel_count; ++i) {\n                stbi_uc a = p[3];\n                stbi_uc t = p[0];\n                if (a) {\n                    stbi_uc half = a / 2;\n                    p[0] = (p[2] * 255 + half) / a;\n                    p[1] = (p[1] * 255 + half) / a;\n                    p[2] = ( t   * 255 + half) / a;\n                } else {\n                    p[0] = p[2];\n                    p[2] = t;\n                }\n                p += 4;\n            }\n        } else {\n            // convert bgr to rgb\n            for (i=0; i < pixel_count; ++i) {\n                stbi_uc t = p[0];\n                p[0] = p[2];\n                p[2] = t;\n                p += 4;\n            }\n        }\n    }\n}\n\n#define STBI__PNG_TYPE(a,b,c,d)  (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d))\n\nstatic int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) {\n    stbi_uc palette[1024], pal_img_n=0;\n    stbi_uc has_trans=0, tc[3];\n    stbi__uint16 tc16[3];\n    stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0;\n    int first=1,k,interlace=0, color=0, is_iphone=0;\n    stbi__context *s = z->s;\n\n    z->expanded = NULL;\n    z->idata = NULL;\n    z->out = NULL;\n\n    if (!stbi__check_png_header(s)) return 0;\n\n    if (scan == STBI__SCAN_type) return 1;\n\n    for (;;) {\n        stbi__pngchunk c = stbi__get_chunk_header(s);\n        switch (c.type) {\n        case STBI__PNG_TYPE('C','g','B','I'):\n            is_iphone = 1;\n            stbi__skip(s, c.length);\n            break;\n        case STBI__PNG_TYPE('I','H','D','R'): {\n            int comp,filter;\n            if (!first) return stbi__err(\"multiple IHDR\",\"Corrupt PNG\");\n            first = 0;\n            if (c.length != 13) return stbi__err(\"bad IHDR len\",\"Corrupt PNG\");\n            s->img_x = stbi__get32be(s);\n            if (s->img_x > (1 << 24)) return stbi__err(\"too large\",\"Very large image (corrupt?)\");\n            s->img_y = stbi__get32be(s);\n            if (s->img_y > (1 << 24)) return stbi__err(\"too large\",\"Very large image (corrupt?)\");\n            z->depth = stbi__get8(s);\n            if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16)  return stbi__err(\"1/2/4/8/16-bit only\",\"PNG not supported: 1/2/4/8/16-bit only\");\n            color = stbi__get8(s);\n            if (color > 6)         return stbi__err(\"bad ctype\",\"Corrupt PNG\");\n            if (color == 3 && z->depth == 16)                  return stbi__err(\"bad ctype\",\"Corrupt PNG\");\n            if (color == 3) pal_img_n = 3;\n            else if (color & 1) return stbi__err(\"bad ctype\",\"Corrupt PNG\");\n            comp  = stbi__get8(s);\n            if (comp) return stbi__err(\"bad comp method\",\"Corrupt PNG\");\n            filter= stbi__get8(s);\n            if (filter) return stbi__err(\"bad filter method\",\"Corrupt PNG\");\n            interlace = stbi__get8(s);\n            if (interlace>1) return stbi__err(\"bad interlace method\",\"Corrupt PNG\");\n            if (!s->img_x || !s->img_y) return stbi__err(\"0-pixel image\",\"Corrupt PNG\");\n            if (!pal_img_n) {\n                s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0);\n                if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err(\"too large\", \"Image too large to decode\");\n                if (scan == STBI__SCAN_header) return 1;\n            } else {\n                // if paletted, then pal_n is our final components, and\n                // img_n is # components to decompress/filter.\n                s->img_n = 1;\n                if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err(\"too large\",\"Corrupt PNG\");\n                // if SCAN_header, have to scan to see if we have a tRNS\n            }\n            break;\n        }\n\n        case STBI__PNG_TYPE('P','L','T','E'):  {\n            if (first) return stbi__err(\"first not IHDR\", \"Corrupt PNG\");\n            if (c.length > 256*3) return stbi__err(\"invalid PLTE\",\"Corrupt PNG\");\n            pal_len = c.length / 3;\n            if (pal_len * 3 != c.length) return stbi__err(\"invalid PLTE\",\"Corrupt PNG\");\n            for (i=0; i < pal_len; ++i) {\n                palette[i*4+0] = stbi__get8(s);\n                palette[i*4+1] = stbi__get8(s);\n                palette[i*4+2] = stbi__get8(s);\n                palette[i*4+3] = 255;\n            }\n            break;\n        }\n\n        case STBI__PNG_TYPE('t','R','N','S'): {\n            if (first) return stbi__err(\"first not IHDR\", \"Corrupt PNG\");\n            if (z->idata) return stbi__err(\"tRNS after IDAT\",\"Corrupt PNG\");\n            if (pal_img_n) {\n                if (scan == STBI__SCAN_header) {\n                    s->img_n = 4;\n                    return 1;\n                }\n                if (pal_len == 0) return stbi__err(\"tRNS before PLTE\",\"Corrupt PNG\");\n                if (c.length > pal_len) return stbi__err(\"bad tRNS len\",\"Corrupt PNG\");\n                pal_img_n = 4;\n                for (i=0; i < c.length; ++i)\n                    palette[i*4+3] = stbi__get8(s);\n            } else {\n                if (!(s->img_n & 1)) return stbi__err(\"tRNS with alpha\",\"Corrupt PNG\");\n                if (c.length != (stbi__uint32) s->img_n*2) return stbi__err(\"bad tRNS len\",\"Corrupt PNG\");\n                has_trans = 1;\n                if (z->depth == 16) {\n                    for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is\n                } else {\n                    for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger\n                }\n            }\n            break;\n        }\n\n        case STBI__PNG_TYPE('I','D','A','T'): {\n            if (first) return stbi__err(\"first not IHDR\", \"Corrupt PNG\");\n            if (pal_img_n && !pal_len) return stbi__err(\"no PLTE\",\"Corrupt PNG\");\n            if (scan == STBI__SCAN_header) {\n                s->img_n = pal_img_n;\n                return 1;\n            }\n            if ((int)(ioff + c.length) < (int)ioff) return 0;\n            if (ioff + c.length > idata_limit) {\n                stbi__uint32 idata_limit_old = idata_limit;\n                stbi_uc *p;\n                if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096;\n                while (ioff + c.length > idata_limit)\n                    idata_limit *= 2;\n                STBI_NOTUSED(idata_limit_old);\n                p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit);\n                if (p == NULL) return stbi__err(\"outofmem\", \"Out of memory\");\n                z->idata = p;\n            }\n            if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err(\"outofdata\",\"Corrupt PNG\");\n            ioff += c.length;\n            break;\n        }\n\n        case STBI__PNG_TYPE('I','E','N','D'): {\n            stbi__uint32 raw_len, bpl;\n            if (first) return stbi__err(\"first not IHDR\", \"Corrupt PNG\");\n            if (scan != STBI__SCAN_load) return 1;\n            if (z->idata == NULL) return stbi__err(\"no IDAT\",\"Corrupt PNG\");\n            // initial guess for decoded data size to avoid unnecessary reallocs\n            bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component\n            raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */;\n            z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone);\n            if (z->expanded == NULL) return 0; // zlib should set error\n            STBI_FREE(z->idata);\n            z->idata = NULL;\n            if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans)\n                s->img_out_n = s->img_n+1;\n            else\n                s->img_out_n = s->img_n;\n            if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0;\n            if (has_trans) {\n                if (z->depth == 16) {\n                    if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0;\n                } else {\n                    if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0;\n                }\n            }\n            if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2)\n                stbi__de_iphone(z);\n            if (pal_img_n) {\n                // pal_img_n == 3 or 4\n                s->img_n = pal_img_n; // record the actual colors we had\n                s->img_out_n = pal_img_n;\n                if (req_comp >= 3) s->img_out_n = req_comp;\n                if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n))\n                    return 0;\n            } else if (has_trans) {\n                // non-paletted image with tRNS -> source image has (constant) alpha\n                ++s->img_n;\n            }\n            STBI_FREE(z->expanded);\n            z->expanded = NULL;\n            return 1;\n        }\n\n        default:\n            // if critical, fail\n            if (first) return stbi__err(\"first not IHDR\", \"Corrupt PNG\");\n            if ((c.type & (1 << 29)) == 0) {\n#ifndef STBI_NO_FAILURE_STRINGS\n                // not threadsafe\n                static char invalid_chunk[] = \"XXXX PNG chunk not known\";\n                invalid_chunk[0] = STBI__BYTECAST(c.type >> 24);\n                invalid_chunk[1] = STBI__BYTECAST(c.type >> 16);\n                invalid_chunk[2] = STBI__BYTECAST(c.type >>  8);\n                invalid_chunk[3] = STBI__BYTECAST(c.type >>  0);\n#endif\n                return stbi__err(invalid_chunk, \"PNG not supported: unknown PNG chunk type\");\n            }\n            stbi__skip(s, c.length);\n            break;\n        }\n        // end of PNG chunk, read and skip CRC\n        stbi__get32be(s);\n    }\n}\n\nstatic void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) {\n    void *result=NULL;\n    if (req_comp < 0 || req_comp > 4) return stbi__errpuc(\"bad req_comp\", \"Internal error\");\n    if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) {\n        if (p->depth < 8)\n            ri->bits_per_channel = 8;\n        else\n            ri->bits_per_channel = p->depth;\n        result = p->out;\n        p->out = NULL;\n        if (req_comp && req_comp != p->s->img_out_n) {\n            if (ri->bits_per_channel == 8)\n                result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y);\n            else\n                result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y);\n            p->s->img_out_n = req_comp;\n            if (result == NULL) return result;\n        }\n        *x = p->s->img_x;\n        *y = p->s->img_y;\n        if (n) *n = p->s->img_n;\n    }\n    STBI_FREE(p->out);\n    p->out      = NULL;\n    STBI_FREE(p->expanded);\n    p->expanded = NULL;\n    STBI_FREE(p->idata);\n    p->idata    = NULL;\n\n    return result;\n}\n\nstatic void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) {\n    stbi__png p;\n    p.s = s;\n    return stbi__do_png(&p, x,y,comp,req_comp, ri);\n}\n\nstatic int stbi__png_test(stbi__context *s) {\n    int r;\n    r = stbi__check_png_header(s);\n    stbi__rewind(s);\n    return r;\n}\n\nstatic int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) {\n    if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) {\n        stbi__rewind( p->s );\n        return 0;\n    }\n    if (x) *x = p->s->img_x;\n    if (y) *y = p->s->img_y;\n    if (comp) *comp = p->s->img_n;\n    return 1;\n}\n\nstatic int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) {\n    stbi__png p;\n    p.s = s;\n    return stbi__png_info_raw(&p, x, y, comp);\n}\n\nstatic int stbi__png_is16(stbi__context *s) {\n    stbi__png p;\n    p.s = s;\n    if (!stbi__png_info_raw(&p, NULL, NULL, NULL))\n        return 0;\n    if (p.depth != 16) {\n        stbi__rewind(p.s);\n        return 0;\n    }\n    return 1;\n}\n#endif\n\n// Microsoft/Windows BMP image\n\n#ifndef STBI_NO_BMP\nstatic int stbi__bmp_test_raw(stbi__context *s) {\n    int r;\n    int sz;\n    if (stbi__get8(s) != 'B') return 0;\n    if (stbi__get8(s) != 'M') return 0;\n    stbi__get32le(s); // discard filesize\n    stbi__get16le(s); // discard reserved\n    stbi__get16le(s); // discard reserved\n    stbi__get32le(s); // discard data offset\n    sz = stbi__get32le(s);\n    r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124);\n    return r;\n}\n\nstatic int stbi__bmp_test(stbi__context *s) {\n    int r = stbi__bmp_test_raw(s);\n    stbi__rewind(s);\n    return r;\n}\n\n\n// returns 0..31 for the highest set bit\nstatic int stbi__high_bit(unsigned int z) {\n    int n=0;\n    if (z == 0) return -1;\n    if (z >= 0x10000) n += 16, z >>= 16;\n    if (z >= 0x00100) n +=  8, z >>=  8;\n    if (z >= 0x00010) n +=  4, z >>=  4;\n    if (z >= 0x00004) n +=  2, z >>=  2;\n    if (z >= 0x00002) n +=  1, z >>=  1;\n    return n;\n}\n\nstatic int stbi__bitcount(unsigned int a) {\n    a = (a & 0x55555555) + ((a >>  1) & 0x55555555); // max 2\n    a = (a & 0x33333333) + ((a >>  2) & 0x33333333); // max 4\n    a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits\n    a = (a + (a >> 8)); // max 16 per 8 bits\n    a = (a + (a >> 16)); // max 32 per 8 bits\n    return a & 0xff;\n}\n\n// extract an arbitrarily-aligned N-bit value (N=bits)\n// from v, and then make it 8-bits long and fractionally\n// extend it to full full range.\nstatic int stbi__shiftsigned(int v, int shift, int bits) {\n    static unsigned int mul_table[9] = {\n        0,\n        0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/,\n        0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/,\n    };\n    static unsigned int shift_table[9] = {\n        0, 0,0,1,0,2,4,6,0,\n    };\n    if (shift < 0)\n        v <<= -shift;\n    else\n        v >>= shift;\n    STBI_ASSERT(v >= 0 && v < 256);\n    v >>= (8-bits);\n    STBI_ASSERT(bits >= 0 && bits <= 8);\n    return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits];\n}\n\ntypedef struct {\n    int bpp, offset, hsz;\n    unsigned int mr,mg,mb,ma, all_a;\n} stbi__bmp_data;\n\nstatic void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) {\n    int hsz;\n    if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc(\"not BMP\", \"Corrupt BMP\");\n    stbi__get32le(s); // discard filesize\n    stbi__get16le(s); // discard reserved\n    stbi__get16le(s); // discard reserved\n    info->offset = stbi__get32le(s);\n    info->hsz = hsz = stbi__get32le(s);\n    info->mr = info->mg = info->mb = info->ma = 0;\n\n    if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc(\"unknown BMP\", \"BMP type not supported: unknown\");\n    if (hsz == 12) {\n        s->img_x = stbi__get16le(s);\n        s->img_y = stbi__get16le(s);\n    } else {\n        s->img_x = stbi__get32le(s);\n        s->img_y = stbi__get32le(s);\n    }\n    if (stbi__get16le(s) != 1) return stbi__errpuc(\"bad BMP\", \"bad BMP\");\n    info->bpp = stbi__get16le(s);\n    if (hsz != 12) {\n        int compress = stbi__get32le(s);\n        if (compress == 1 || compress == 2) return stbi__errpuc(\"BMP RLE\", \"BMP type not supported: RLE\");\n        stbi__get32le(s); // discard sizeof\n        stbi__get32le(s); // discard hres\n        stbi__get32le(s); // discard vres\n        stbi__get32le(s); // discard colorsused\n        stbi__get32le(s); // discard max important\n        if (hsz == 40 || hsz == 56) {\n            if (hsz == 56) {\n                stbi__get32le(s);\n                stbi__get32le(s);\n                stbi__get32le(s);\n                stbi__get32le(s);\n            }\n            if (info->bpp == 16 || info->bpp == 32) {\n                if (compress == 0) {\n                    if (info->bpp == 32) {\n                        info->mr = 0xffu << 16;\n                        info->mg = 0xffu <<  8;\n                        info->mb = 0xffu <<  0;\n                        info->ma = 0xffu << 24;\n                        info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0\n                    } else {\n                        info->mr = 31u << 10;\n                        info->mg = 31u <<  5;\n                        info->mb = 31u <<  0;\n                    }\n                } else if (compress == 3) {\n                    info->mr = stbi__get32le(s);\n                    info->mg = stbi__get32le(s);\n                    info->mb = stbi__get32le(s);\n                    // not documented, but generated by photoshop and handled by mspaint\n                    if (info->mr == info->mg && info->mg == info->mb) {\n                        // ?!?!?\n                        return stbi__errpuc(\"bad BMP\", \"bad BMP\");\n                    }\n                } else\n                    return stbi__errpuc(\"bad BMP\", \"bad BMP\");\n            }\n        } else {\n            int i;\n            if (hsz != 108 && hsz != 124)\n                return stbi__errpuc(\"bad BMP\", \"bad BMP\");\n            info->mr = stbi__get32le(s);\n            info->mg = stbi__get32le(s);\n            info->mb = stbi__get32le(s);\n            info->ma = stbi__get32le(s);\n            stbi__get32le(s); // discard color space\n            for (i=0; i < 12; ++i)\n                stbi__get32le(s); // discard color space parameters\n            if (hsz == 124) {\n                stbi__get32le(s); // discard rendering intent\n                stbi__get32le(s); // discard offset of profile data\n                stbi__get32le(s); // discard size of profile data\n                stbi__get32le(s); // discard reserved\n            }\n        }\n    }\n    return (void *) 1;\n}\n\n\nstatic void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) {\n    stbi_uc *out;\n    unsigned int mr=0,mg=0,mb=0,ma=0, all_a;\n    stbi_uc pal[256][4];\n    int psize=0,i,j,width;\n    int flip_vertically, pad, target;\n    stbi__bmp_data info;\n    STBI_NOTUSED(ri);\n\n    info.all_a = 255;\n    if (stbi__bmp_parse_header(s, &info) == NULL)\n        return NULL; // error code already set\n\n    flip_vertically = ((int) s->img_y) > 0;\n    s->img_y = abs((int) s->img_y);\n\n    mr = info.mr;\n    mg = info.mg;\n    mb = info.mb;\n    ma = info.ma;\n    all_a = info.all_a;\n\n    if (info.hsz == 12) {\n        if (info.bpp < 24)\n            psize = (info.offset - 14 - 24) / 3;\n    } else {\n        if (info.bpp < 16)\n            psize = (info.offset - 14 - info.hsz) >> 2;\n    }\n\n    s->img_n = ma ? 4 : 3;\n    if (req_comp && req_comp >= 3) // we can directly decode 3 or 4\n        target = req_comp;\n    else\n        target = s->img_n; // if they want monochrome, we'll post-convert\n\n    // sanity-check size\n    if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0))\n        return stbi__errpuc(\"too large\", \"Corrupt BMP\");\n\n    out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0);\n    if (!out) return stbi__errpuc(\"outofmem\", \"Out of memory\");\n    if (info.bpp < 16) {\n        int z=0;\n        if (psize == 0 || psize > 256) {\n            STBI_FREE(out);\n            return stbi__errpuc(\"invalid\", \"Corrupt BMP\");\n        }\n        for (i=0; i < psize; ++i) {\n            pal[i][2] = stbi__get8(s);\n            pal[i][1] = stbi__get8(s);\n            pal[i][0] = stbi__get8(s);\n            if (info.hsz != 12) stbi__get8(s);\n            pal[i][3] = 255;\n        }\n        stbi__skip(s, info.offset - 14 - info.hsz - psize * (info.hsz == 12 ? 3 : 4));\n        if (info.bpp == 1) width = (s->img_x + 7) >> 3;\n        else if (info.bpp == 4) width = (s->img_x + 1) >> 1;\n        else if (info.bpp == 8) width = s->img_x;\n        else {\n            STBI_FREE(out);\n            return stbi__errpuc(\"bad bpp\", \"Corrupt BMP\");\n        }\n        pad = (-width)&3;\n        if (info.bpp == 1) {\n            for (j=0; j < (int) s->img_y; ++j) {\n                int bit_offset = 7, v = stbi__get8(s);\n                for (i=0; i < (int) s->img_x; ++i) {\n                    int color = (v>>bit_offset)&0x1;\n                    out[z++] = pal[color][0];\n                    out[z++] = pal[color][1];\n                    out[z++] = pal[color][2];\n                    if((--bit_offset) < 0) {\n                        bit_offset = 7;\n                        v = stbi__get8(s);\n                    }\n                }\n                stbi__skip(s, pad);\n            }\n        } else {\n            for (j=0; j < (int) s->img_y; ++j) {\n                for (i=0; i < (int) s->img_x; i += 2) {\n                    int v=stbi__get8(s),v2=0;\n                    if (info.bpp == 4) {\n                        v2 = v & 15;\n                        v >>= 4;\n                    }\n                    out[z++] = pal[v][0];\n                    out[z++] = pal[v][1];\n                    out[z++] = pal[v][2];\n                    if (target == 4) out[z++] = 255;\n                    if (i+1 == (int) s->img_x) break;\n                    v = (info.bpp == 8) ? stbi__get8(s) : v2;\n                    out[z++] = pal[v][0];\n                    out[z++] = pal[v][1];\n                    out[z++] = pal[v][2];\n                    if (target == 4) out[z++] = 255;\n                }\n                stbi__skip(s, pad);\n            }\n        }\n    } else {\n        int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0;\n        int z = 0;\n        int easy=0;\n        stbi__skip(s, info.offset - 14 - info.hsz);\n        if (info.bpp == 24) width = 3 * s->img_x;\n        else if (info.bpp == 16) width = 2*s->img_x;\n        else /* bpp = 32 and pad = 0 */ width=0;\n        pad = (-width) & 3;\n        if (info.bpp == 24) {\n            easy = 1;\n        } else if (info.bpp == 32) {\n            if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000)\n                easy = 2;\n        }\n        if (!easy) {\n            if (!mr || !mg || !mb) {\n                STBI_FREE(out);\n                return stbi__errpuc(\"bad masks\", \"Corrupt BMP\");\n            }\n            // right shift amt to put high bit in position #7\n            rshift = stbi__high_bit(mr)-7;\n            rcount = stbi__bitcount(mr);\n            gshift = stbi__high_bit(mg)-7;\n            gcount = stbi__bitcount(mg);\n            bshift = stbi__high_bit(mb)-7;\n            bcount = stbi__bitcount(mb);\n            ashift = stbi__high_bit(ma)-7;\n            acount = stbi__bitcount(ma);\n        }\n        for (j=0; j < (int) s->img_y; ++j) {\n            if (easy) {\n                for (i=0; i < (int) s->img_x; ++i) {\n                    unsigned char a;\n                    out[z+2] = stbi__get8(s);\n                    out[z+1] = stbi__get8(s);\n                    out[z+0] = stbi__get8(s);\n                    z += 3;\n                    a = (easy == 2 ? stbi__get8(s) : 255);\n                    all_a |= a;\n                    if (target == 4) out[z++] = a;\n                }\n            } else {\n                int bpp = info.bpp;\n                for (i=0; i < (int) s->img_x; ++i) {\n                    stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s));\n                    unsigned int a;\n                    out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount));\n                    out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount));\n                    out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount));\n                    a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255);\n                    all_a |= a;\n                    if (target == 4) out[z++] = STBI__BYTECAST(a);\n                }\n            }\n            stbi__skip(s, pad);\n        }\n    }\n\n    // if alpha channel is all 0s, replace with all 255s\n    if (target == 4 && all_a == 0)\n        for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4)\n            out[i] = 255;\n\n    if (flip_vertically) {\n        stbi_uc t;\n        for (j=0; j < (int) s->img_y>>1; ++j) {\n            stbi_uc *p1 = out +      j     *s->img_x*target;\n            stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target;\n            for (i=0; i < (int) s->img_x*target; ++i) {\n                t = p1[i], p1[i] = p2[i], p2[i] = t;\n            }\n        }\n    }\n\n    if (req_comp && req_comp != target) {\n        out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y);\n        if (out == NULL) return out; // stbi__convert_format frees input on failure\n    }\n\n    *x = s->img_x;\n    *y = s->img_y;\n    if (comp) *comp = s->img_n;\n    return out;\n}\n#endif\n\n// Targa Truevision - TGA\n// by Jonathan Dummer\n#ifndef STBI_NO_TGA\n// returns STBI_rgb or whatever, 0 on error\nstatic int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) {\n    // only RGB or RGBA (incl. 16bit) or grey allowed\n    if (is_rgb16) *is_rgb16 = 0;\n    switch(bits_per_pixel) {\n    case 8:\n        return STBI_grey;\n    case 16:\n        if(is_grey) return STBI_grey_alpha;\n    // fallthrough\n    case 15:\n        if(is_rgb16) *is_rgb16 = 1;\n        return STBI_rgb;\n    case 24: // fallthrough\n    case 32:\n        return bits_per_pixel/8;\n    default:\n        return 0;\n    }\n}\n\nstatic int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) {\n    int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp;\n    int sz, tga_colormap_type;\n    stbi__get8(s);                   // discard Offset\n    tga_colormap_type = stbi__get8(s); // colormap type\n    if( tga_colormap_type > 1 ) {\n        stbi__rewind(s);\n        return 0;      // only RGB or indexed allowed\n    }\n    tga_image_type = stbi__get8(s); // image type\n    if ( tga_colormap_type == 1 ) { // colormapped (paletted) image\n        if (tga_image_type != 1 && tga_image_type != 9) {\n            stbi__rewind(s);\n            return 0;\n        }\n        stbi__skip(s,4);       // skip index of first colormap entry and number of entries\n        sz = stbi__get8(s);    //   check bits per palette color entry\n        if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) {\n            stbi__rewind(s);\n            return 0;\n        }\n        stbi__skip(s,4);       // skip image x and y origin\n        tga_colormap_bpp = sz;\n    } else { // \"normal\" image w/o colormap - only RGB or grey allowed, +/- RLE\n        if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) {\n            stbi__rewind(s);\n            return 0; // only RGB or grey allowed, +/- RLE\n        }\n        stbi__skip(s,9); // skip colormap specification and image x/y origin\n        tga_colormap_bpp = 0;\n    }\n    tga_w = stbi__get16le(s);\n    if( tga_w < 1 ) {\n        stbi__rewind(s);\n        return 0;   // test width\n    }\n    tga_h = stbi__get16le(s);\n    if( tga_h < 1 ) {\n        stbi__rewind(s);\n        return 0;   // test height\n    }\n    tga_bits_per_pixel = stbi__get8(s); // bits per pixel\n    stbi__get8(s); // ignore alpha bits\n    if (tga_colormap_bpp != 0) {\n        if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) {\n            // when using a colormap, tga_bits_per_pixel is the size of the indexes\n            // I don't think anything but 8 or 16bit indexes makes sense\n            stbi__rewind(s);\n            return 0;\n        }\n        tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL);\n    } else {\n        tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL);\n    }\n    if(!tga_comp) {\n        stbi__rewind(s);\n        return 0;\n    }\n    if (x) *x = tga_w;\n    if (y) *y = tga_h;\n    if (comp) *comp = tga_comp;\n    return 1;                   // seems to have passed everything\n}\n\nstatic int stbi__tga_test(stbi__context *s) {\n    int res = 0;\n    int sz, tga_color_type;\n    stbi__get8(s);      //   discard Offset\n    tga_color_type = stbi__get8(s);   //   color type\n    if ( tga_color_type > 1 ) goto errorEnd;   //   only RGB or indexed allowed\n    sz = stbi__get8(s);   //   image type\n    if ( tga_color_type == 1 ) { // colormapped (paletted) image\n        if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9\n        stbi__skip(s,4);       // skip index of first colormap entry and number of entries\n        sz = stbi__get8(s);    //   check bits per palette color entry\n        if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd;\n        stbi__skip(s,4);       // skip image x and y origin\n    } else { // \"normal\" image w/o colormap\n        if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE\n        stbi__skip(s,9); // skip colormap specification and image x/y origin\n    }\n    if ( stbi__get16le(s) < 1 ) goto errorEnd;      //   test width\n    if ( stbi__get16le(s) < 1 ) goto errorEnd;      //   test height\n    sz = stbi__get8(s);   //   bits per pixel\n    if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index\n    if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd;\n\n    res = 1; // if we got this far, everything's good and we can return 1 instead of 0\n\nerrorEnd:\n    stbi__rewind(s);\n    return res;\n}\n\n// read 16bit value and convert to 24bit RGB\nstatic void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) {\n    stbi__uint16 px = (stbi__uint16)stbi__get16le(s);\n    stbi__uint16 fiveBitMask = 31;\n    // we have 3 channels with 5bits each\n    int r = (px >> 10) & fiveBitMask;\n    int g = (px >> 5) & fiveBitMask;\n    int b = px & fiveBitMask;\n    // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later\n    out[0] = (stbi_uc)((r * 255)/31);\n    out[1] = (stbi_uc)((g * 255)/31);\n    out[2] = (stbi_uc)((b * 255)/31);\n\n    // some people claim that the most significant bit might be used for alpha\n    // (possibly if an alpha-bit is set in the \"image descriptor byte\")\n    // but that only made 16bit test images completely translucent..\n    // so let's treat all 15 and 16bit TGAs as RGB with no alpha.\n}\n\nstatic void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) {\n    //   read in the TGA header stuff\n    int tga_offset = stbi__get8(s);\n    int tga_indexed = stbi__get8(s);\n    int tga_image_type = stbi__get8(s);\n    int tga_is_RLE = 0;\n    int tga_palette_start = stbi__get16le(s);\n    int tga_palette_len = stbi__get16le(s);\n    int tga_palette_bits = stbi__get8(s);\n    int tga_x_origin = stbi__get16le(s);\n    int tga_y_origin = stbi__get16le(s);\n    int tga_width = stbi__get16le(s);\n    int tga_height = stbi__get16le(s);\n    int tga_bits_per_pixel = stbi__get8(s);\n    int tga_comp, tga_rgb16=0;\n    int tga_inverted = stbi__get8(s);\n    // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?)\n    //   image data\n    unsigned char *tga_data;\n    unsigned char *tga_palette = NULL;\n    int i, j;\n    unsigned char raw_data[4] = {0};\n    int RLE_count = 0;\n    int RLE_repeating = 0;\n    int read_next_pixel = 1;\n    STBI_NOTUSED(ri);\n\n    //   do a tiny bit of precessing\n    if ( tga_image_type >= 8 ) {\n        tga_image_type -= 8;\n        tga_is_RLE = 1;\n    }\n    tga_inverted = 1 - ((tga_inverted >> 5) & 1);\n\n    //   If I'm paletted, then I'll use the number of bits from the palette\n    if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16);\n    else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16);\n\n    if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency\n        return stbi__errpuc(\"bad format\", \"Can't find out TGA pixelformat\");\n\n    //   tga info\n    *x = tga_width;\n    *y = tga_height;\n    if (comp) *comp = tga_comp;\n\n    if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0))\n        return stbi__errpuc(\"too large\", \"Corrupt TGA\");\n\n    tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0);\n    if (!tga_data) return stbi__errpuc(\"outofmem\", \"Out of memory\");\n\n    // skip to the data's starting position (offset usually = 0)\n    stbi__skip(s, tga_offset );\n\n    if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) {\n        for (i=0; i < tga_height; ++i) {\n            int row = tga_inverted ? tga_height -i - 1 : i;\n            stbi_uc *tga_row = tga_data + row*tga_width*tga_comp;\n            stbi__getn(s, tga_row, tga_width * tga_comp);\n        }\n    } else  {\n        //   do I need to load a palette?\n        if ( tga_indexed) {\n            //   any data to skip? (offset usually = 0)\n            stbi__skip(s, tga_palette_start );\n            //   load the palette\n            tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0);\n            if (!tga_palette) {\n                STBI_FREE(tga_data);\n                return stbi__errpuc(\"outofmem\", \"Out of memory\");\n            }\n            if (tga_rgb16) {\n                stbi_uc *pal_entry = tga_palette;\n                STBI_ASSERT(tga_comp == STBI_rgb);\n                for (i=0; i < tga_palette_len; ++i) {\n                    stbi__tga_read_rgb16(s, pal_entry);\n                    pal_entry += tga_comp;\n                }\n            } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) {\n                STBI_FREE(tga_data);\n                STBI_FREE(tga_palette);\n                return stbi__errpuc(\"bad palette\", \"Corrupt TGA\");\n            }\n        }\n        //   load the data\n        for (i=0; i < tga_width * tga_height; ++i) {\n            //   if I'm in RLE mode, do I need to get a RLE stbi__pngchunk?\n            if ( tga_is_RLE ) {\n                if ( RLE_count == 0 ) {\n                    //   yep, get the next byte as a RLE command\n                    int RLE_cmd = stbi__get8(s);\n                    RLE_count = 1 + (RLE_cmd & 127);\n                    RLE_repeating = RLE_cmd >> 7;\n                    read_next_pixel = 1;\n                } else if ( !RLE_repeating ) {\n                    read_next_pixel = 1;\n                }\n            } else {\n                read_next_pixel = 1;\n            }\n            //   OK, if I need to read a pixel, do it now\n            if ( read_next_pixel ) {\n                //   load however much data we did have\n                if ( tga_indexed ) {\n                    // read in index, then perform the lookup\n                    int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s);\n                    if ( pal_idx >= tga_palette_len ) {\n                        // invalid index\n                        pal_idx = 0;\n                    }\n                    pal_idx *= tga_comp;\n                    for (j = 0; j < tga_comp; ++j) {\n                        raw_data[j] = tga_palette[pal_idx+j];\n                    }\n                } else if(tga_rgb16) {\n                    STBI_ASSERT(tga_comp == STBI_rgb);\n                    stbi__tga_read_rgb16(s, raw_data);\n                } else {\n                    //   read in the data raw\n                    for (j = 0; j < tga_comp; ++j) {\n                        raw_data[j] = stbi__get8(s);\n                    }\n                }\n                //   clear the reading flag for the next pixel\n                read_next_pixel = 0;\n            } // end of reading a pixel\n\n            // copy data\n            for (j = 0; j < tga_comp; ++j)\n                tga_data[i*tga_comp+j] = raw_data[j];\n\n            //   in case we're in RLE mode, keep counting down\n            --RLE_count;\n        }\n        //   do I need to invert the image?\n        if ( tga_inverted ) {\n            for (j = 0; j*2 < tga_height; ++j) {\n                int index1 = j * tga_width * tga_comp;\n                int index2 = (tga_height - 1 - j) * tga_width * tga_comp;\n                for (i = tga_width * tga_comp; i > 0; --i) {\n                    unsigned char temp = tga_data[index1];\n                    tga_data[index1] = tga_data[index2];\n                    tga_data[index2] = temp;\n                    ++index1;\n                    ++index2;\n                }\n            }\n        }\n        //   clear my palette, if I had one\n        if ( tga_palette != NULL ) {\n            STBI_FREE( tga_palette );\n        }\n    }\n\n    // swap RGB - if the source data was RGB16, it already is in the right order\n    if (tga_comp >= 3 && !tga_rgb16) {\n        unsigned char* tga_pixel = tga_data;\n        for (i=0; i < tga_width * tga_height; ++i) {\n            unsigned char temp = tga_pixel[0];\n            tga_pixel[0] = tga_pixel[2];\n            tga_pixel[2] = temp;\n            tga_pixel += tga_comp;\n        }\n    }\n\n    // convert to target component count\n    if (req_comp && req_comp != tga_comp)\n        tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height);\n\n    //   the things I do to get rid of an error message, and yet keep\n    //   Microsoft's C compilers happy... [8^(\n    tga_palette_start = tga_palette_len = tga_palette_bits =\n            tga_x_origin = tga_y_origin = 0;\n    //   OK, done\n    return tga_data;\n}\n#endif\n\n// *************************************************************************************************\n// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB\n\n#ifndef STBI_NO_PSD\nstatic int stbi__psd_test(stbi__context *s) {\n    int r = (stbi__get32be(s) == 0x38425053);\n    stbi__rewind(s);\n    return r;\n}\n\nstatic int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) {\n    int count, nleft, len;\n\n    count = 0;\n    while ((nleft = pixelCount - count) > 0) {\n        len = stbi__get8(s);\n        if (len == 128) {\n            // No-op.\n        } else if (len < 128) {\n            // Copy next len+1 bytes literally.\n            len++;\n            if (len > nleft) return 0; // corrupt data\n            count += len;\n            while (len) {\n                *p = stbi__get8(s);\n                p += 4;\n                len--;\n            }\n        } else if (len > 128) {\n            stbi_uc   val;\n            // Next -len+1 bytes in the dest are replicated from next source byte.\n            // (Interpret len as a negative 8-bit int.)\n            len = 257 - len;\n            if (len > nleft) return 0; // corrupt data\n            val = stbi__get8(s);\n            count += len;\n            while (len) {\n                *p = val;\n                p += 4;\n                len--;\n            }\n        }\n    }\n\n    return 1;\n}\n\nstatic void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) {\n    int pixelCount;\n    int channelCount, compression;\n    int channel, i;\n    int bitdepth;\n    int w,h;\n    stbi_uc *out;\n    STBI_NOTUSED(ri);\n\n    // Check identifier\n    if (stbi__get32be(s) != 0x38425053)   // \"8BPS\"\n        return stbi__errpuc(\"not PSD\", \"Corrupt PSD image\");\n\n    // Check file type version.\n    if (stbi__get16be(s) != 1)\n        return stbi__errpuc(\"wrong version\", \"Unsupported version of PSD image\");\n\n    // Skip 6 reserved bytes.\n    stbi__skip(s, 6 );\n\n    // Read the number of channels (R, G, B, A, etc).\n    channelCount = stbi__get16be(s);\n    if (channelCount < 0 || channelCount > 16)\n        return stbi__errpuc(\"wrong channel count\", \"Unsupported number of channels in PSD image\");\n\n    // Read the rows and columns of the image.\n    h = stbi__get32be(s);\n    w = stbi__get32be(s);\n\n    // Make sure the depth is 8 bits.\n    bitdepth = stbi__get16be(s);\n    if (bitdepth != 8 && bitdepth != 16)\n        return stbi__errpuc(\"unsupported bit depth\", \"PSD bit depth is not 8 or 16 bit\");\n\n    // Make sure the color mode is RGB.\n    // Valid options are:\n    //   0: Bitmap\n    //   1: Grayscale\n    //   2: Indexed color\n    //   3: RGB color\n    //   4: CMYK color\n    //   7: Multichannel\n    //   8: Duotone\n    //   9: Lab color\n    if (stbi__get16be(s) != 3)\n        return stbi__errpuc(\"wrong color format\", \"PSD is not in RGB color format\");\n\n    // Skip the Mode Data.  (It's the palette for indexed color; other info for other modes.)\n    stbi__skip(s,stbi__get32be(s) );\n\n    // Skip the image resources.  (resolution, pen tool paths, etc)\n    stbi__skip(s, stbi__get32be(s) );\n\n    // Skip the reserved data.\n    stbi__skip(s, stbi__get32be(s) );\n\n    // Find out if the data is compressed.\n    // Known values:\n    //   0: no compression\n    //   1: RLE compressed\n    compression = stbi__get16be(s);\n    if (compression > 1)\n        return stbi__errpuc(\"bad compression\", \"PSD has an unknown compression format\");\n\n    // Check size\n    if (!stbi__mad3sizes_valid(4, w, h, 0))\n        return stbi__errpuc(\"too large\", \"Corrupt PSD\");\n\n    // Create the destination image.\n\n    if (!compression && bitdepth == 16 && bpc == 16) {\n        out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0);\n        ri->bits_per_channel = 16;\n    } else\n        out = (stbi_uc *) stbi__malloc(4 * w*h);\n\n    if (!out) return stbi__errpuc(\"outofmem\", \"Out of memory\");\n    pixelCount = w*h;\n\n    // Initialize the data to zero.\n    //memset( out, 0, pixelCount * 4 );\n\n    // Finally, the image data.\n    if (compression) {\n        // RLE as used by .PSD and .TIFF\n        // Loop until you get the number of unpacked bytes you are expecting:\n        //     Read the next source byte into n.\n        //     If n is between 0 and 127 inclusive, copy the next n+1 bytes literally.\n        //     Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times.\n        //     Else if n is 128, noop.\n        // Endloop\n\n        // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data,\n        // which we're going to just skip.\n        stbi__skip(s, h * channelCount * 2 );\n\n        // Read the RLE data by channel.\n        for (channel = 0; channel < 4; channel++) {\n            stbi_uc *p;\n\n            p = out+channel;\n            if (channel >= channelCount) {\n                // Fill this channel with default data.\n                for (i = 0; i < pixelCount; i++, p += 4)\n                    *p = (channel == 3 ? 255 : 0);\n            } else {\n                // Read the RLE data.\n                if (!stbi__psd_decode_rle(s, p, pixelCount)) {\n                    STBI_FREE(out);\n                    return stbi__errpuc(\"corrupt\", \"bad RLE data\");\n                }\n            }\n        }\n\n    } else {\n        // We're at the raw image data.  It's each channel in order (Red, Green, Blue, Alpha, ...)\n        // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image.\n\n        // Read the data by channel.\n        for (channel = 0; channel < 4; channel++) {\n            if (channel >= channelCount) {\n                // Fill this channel with default data.\n                if (bitdepth == 16 && bpc == 16) {\n                    stbi__uint16 *q = ((stbi__uint16 *) out) + channel;\n                    stbi__uint16 val = channel == 3 ? 65535 : 0;\n                    for (i = 0; i < pixelCount; i++, q += 4)\n                        *q = val;\n                } else {\n                    stbi_uc *p = out+channel;\n                    stbi_uc val = channel == 3 ? 255 : 0;\n                    for (i = 0; i < pixelCount; i++, p += 4)\n                        *p = val;\n                }\n            } else {\n                if (ri->bits_per_channel == 16) {    // output bpc\n                    stbi__uint16 *q = ((stbi__uint16 *) out) + channel;\n                    for (i = 0; i < pixelCount; i++, q += 4)\n                        *q = (stbi__uint16) stbi__get16be(s);\n                } else {\n                    stbi_uc *p = out+channel;\n                    if (bitdepth == 16) {  // input bpc\n                        for (i = 0; i < pixelCount; i++, p += 4)\n                            *p = (stbi_uc) (stbi__get16be(s) >> 8);\n                    } else {\n                        for (i = 0; i < pixelCount; i++, p += 4)\n                            *p = stbi__get8(s);\n                    }\n                }\n            }\n        }\n    }\n\n    // remove weird white matte from PSD\n    if (channelCount >= 4) {\n        if (ri->bits_per_channel == 16) {\n            for (i=0; i < w*h; ++i) {\n                stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i;\n                if (pixel[3] != 0 && pixel[3] != 65535) {\n                    float a = pixel[3] / 65535.0f;\n                    float ra = 1.0f / a;\n                    float inv_a = 65535.0f * (1 - ra);\n                    pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a);\n                    pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a);\n                    pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a);\n                }\n            }\n        } else {\n            for (i=0; i < w*h; ++i) {\n                unsigned char *pixel = out + 4*i;\n                if (pixel[3] != 0 && pixel[3] != 255) {\n                    float a = pixel[3] / 255.0f;\n                    float ra = 1.0f / a;\n                    float inv_a = 255.0f * (1 - ra);\n                    pixel[0] = (unsigned char) (pixel[0]*ra + inv_a);\n                    pixel[1] = (unsigned char) (pixel[1]*ra + inv_a);\n                    pixel[2] = (unsigned char) (pixel[2]*ra + inv_a);\n                }\n            }\n        }\n    }\n\n    // convert to desired output format\n    if (req_comp && req_comp != 4) {\n        if (ri->bits_per_channel == 16)\n            out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h);\n        else\n            out = stbi__convert_format(out, 4, req_comp, w, h);\n        if (out == NULL) return out; // stbi__convert_format frees input on failure\n    }\n\n    if (comp) *comp = 4;\n    *y = h;\n    *x = w;\n\n    return out;\n}\n#endif\n\n// *************************************************************************************************\n// Softimage PIC loader\n// by Tom Seddon\n//\n// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format\n// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/\n\n#ifndef STBI_NO_PIC\nstatic int stbi__pic_is4(stbi__context *s,const char *str) {\n    int i;\n    for (i=0; i<4; ++i)\n        if (stbi__get8(s) != (stbi_uc)str[i])\n            return 0;\n\n    return 1;\n}\n\nstatic int stbi__pic_test_core(stbi__context *s) {\n    int i;\n\n    if (!stbi__pic_is4(s,\"\\x53\\x80\\xF6\\x34\"))\n        return 0;\n\n    for(i=0; i<84; ++i)\n        stbi__get8(s);\n\n    if (!stbi__pic_is4(s,\"PICT\"))\n        return 0;\n\n    return 1;\n}\n\ntypedef struct {\n    stbi_uc size,type,channel;\n} stbi__pic_packet;\n\nstatic stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) {\n    int mask=0x80, i;\n\n    for (i=0; i<4; ++i, mask>>=1) {\n        if (channel & mask) {\n            if (stbi__at_eof(s)) return stbi__errpuc(\"bad file\",\"PIC file too short\");\n            dest[i]=stbi__get8(s);\n        }\n    }\n\n    return dest;\n}\n\nstatic void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) {\n    int mask=0x80,i;\n\n    for (i=0; i<4; ++i, mask>>=1)\n        if (channel&mask)\n            dest[i]=src[i];\n}\n\nstatic stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) {\n    int act_comp=0,num_packets=0,y,chained;\n    stbi__pic_packet packets[10];\n\n    // this will (should...) cater for even some bizarre stuff like having data\n    // for the same channel in multiple packets.\n    do {\n        stbi__pic_packet *packet;\n\n        if (num_packets==sizeof(packets)/sizeof(packets[0]))\n            return stbi__errpuc(\"bad format\",\"too many packets\");\n\n        packet = &packets[num_packets++];\n\n        chained = stbi__get8(s);\n        packet->size    = stbi__get8(s);\n        packet->type    = stbi__get8(s);\n        packet->channel = stbi__get8(s);\n\n        act_comp |= packet->channel;\n\n        if (stbi__at_eof(s))          return stbi__errpuc(\"bad file\",\"file too short (reading packets)\");\n        if (packet->size != 8)  return stbi__errpuc(\"bad format\",\"packet isn't 8bpp\");\n    } while (chained);\n\n    *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel?\n\n    for(y=0; y<height; ++y) {\n        int packet_idx;\n\n        for(packet_idx=0; packet_idx < num_packets; ++packet_idx) {\n            stbi__pic_packet *packet = &packets[packet_idx];\n            stbi_uc *dest = result+y*width*4;\n\n            switch (packet->type) {\n            default:\n                return stbi__errpuc(\"bad format\",\"packet has bad compression type\");\n\n            case 0: {//uncompressed\n                int x;\n\n                for(x=0; x<width; ++x, dest+=4)\n                    if (!stbi__readval(s,packet->channel,dest))\n                        return 0;\n                break;\n            }\n\n            case 1: { //Pure RLE\n                int left=width, i;\n\n                while (left>0) {\n                    stbi_uc count,value[4];\n\n                    count=stbi__get8(s);\n                    if (stbi__at_eof(s))   return stbi__errpuc(\"bad file\",\"file too short (pure read count)\");\n\n                    if (count > left)\n                        count = (stbi_uc) left;\n\n                    if (!stbi__readval(s,packet->channel,value))  return 0;\n\n                    for(i=0; i<count; ++i,dest+=4)\n                        stbi__copyval(packet->channel,dest,value);\n                    left -= count;\n                }\n            }\n            break;\n\n            case 2: {//Mixed RLE\n                int left=width;\n                while (left>0) {\n                    int count = stbi__get8(s), i;\n                    if (stbi__at_eof(s))  return stbi__errpuc(\"bad file\",\"file too short (mixed read count)\");\n\n                    if (count >= 128) { // Repeated\n                        stbi_uc value[4];\n\n                        if (count==128)\n                            count = stbi__get16be(s);\n                        else\n                            count -= 127;\n                        if (count > left)\n                            return stbi__errpuc(\"bad file\",\"scanline overrun\");\n\n                        if (!stbi__readval(s,packet->channel,value))\n                            return 0;\n\n                        for(i=0; i<count; ++i, dest += 4)\n                            stbi__copyval(packet->channel,dest,value);\n                    } else { // Raw\n                        ++count;\n                        if (count>left) return stbi__errpuc(\"bad file\",\"scanline overrun\");\n\n                        for(i=0; i<count; ++i, dest+=4)\n                            if (!stbi__readval(s,packet->channel,dest))\n                                return 0;\n                    }\n                    left-=count;\n                }\n                break;\n            }\n            }\n        }\n    }\n\n    return result;\n}\n\nstatic void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) {\n    stbi_uc *result;\n    int i, x,y, internal_comp;\n    STBI_NOTUSED(ri);\n\n    if (!comp) comp = &internal_comp;\n\n    for (i=0; i<92; ++i)\n        stbi__get8(s);\n\n    x = stbi__get16be(s);\n    y = stbi__get16be(s);\n    if (stbi__at_eof(s))  return stbi__errpuc(\"bad file\",\"file too short (pic header)\");\n    if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc(\"too large\", \"PIC image too large to decode\");\n\n    stbi__get32be(s); //skip `ratio'\n    stbi__get16be(s); //skip `fields'\n    stbi__get16be(s); //skip `pad'\n\n    // intermediate buffer is RGBA\n    result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0);\n    memset(result, 0xff, x*y*4);\n\n    if (!stbi__pic_load_core(s,x,y,comp, result)) {\n        STBI_FREE(result);\n        result=0;\n    }\n    *px = x;\n    *py = y;\n    if (req_comp == 0) req_comp = *comp;\n    result=stbi__convert_format(result,4,req_comp,x,y);\n\n    return result;\n}\n\nstatic int stbi__pic_test(stbi__context *s) {\n    int r = stbi__pic_test_core(s);\n    stbi__rewind(s);\n    return r;\n}\n#endif\n\n// *************************************************************************************************\n// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb\n\n#ifndef STBI_NO_GIF\ntypedef struct {\n    stbi__int16 prefix;\n    stbi_uc first;\n    stbi_uc suffix;\n} stbi__gif_lzw;\n\ntypedef struct {\n    int w,h;\n    stbi_uc *out;                 // output buffer (always 4 components)\n    stbi_uc *background;          // The current \"background\" as far as a gif is concerned\n    stbi_uc *history;\n    int flags, bgindex, ratio, transparent, eflags;\n    stbi_uc  pal[256][4];\n    stbi_uc lpal[256][4];\n    stbi__gif_lzw codes[8192];\n    stbi_uc *color_table;\n    int parse, step;\n    int lflags;\n    int start_x, start_y;\n    int max_x, max_y;\n    int cur_x, cur_y;\n    int line_size;\n    int delay;\n} stbi__gif;\n\nstatic int stbi__gif_test_raw(stbi__context *s) {\n    int sz;\n    if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0;\n    sz = stbi__get8(s);\n    if (sz != '9' && sz != '7') return 0;\n    if (stbi__get8(s) != 'a') return 0;\n    return 1;\n}\n\nstatic int stbi__gif_test(stbi__context *s) {\n    int r = stbi__gif_test_raw(s);\n    stbi__rewind(s);\n    return r;\n}\n\nstatic void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) {\n    int i;\n    for (i=0; i < num_entries; ++i) {\n        pal[i][2] = stbi__get8(s);\n        pal[i][1] = stbi__get8(s);\n        pal[i][0] = stbi__get8(s);\n        pal[i][3] = transp == i ? 0 : 255;\n    }\n}\n\nstatic int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) {\n    stbi_uc version;\n    if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8')\n        return stbi__err(\"not GIF\", \"Corrupt GIF\");\n\n    version = stbi__get8(s);\n    if (version != '7' && version != '9')    return stbi__err(\"not GIF\", \"Corrupt GIF\");\n    if (stbi__get8(s) != 'a')                return stbi__err(\"not GIF\", \"Corrupt GIF\");\n\n    stbi__g_failure_reason = \"\";\n    g->w = stbi__get16le(s);\n    g->h = stbi__get16le(s);\n    g->flags = stbi__get8(s);\n    g->bgindex = stbi__get8(s);\n    g->ratio = stbi__get8(s);\n    g->transparent = -1;\n\n    if (comp != 0) *comp = 4;  // can't actually tell whether it's 3 or 4 until we parse the comments\n\n    if (is_info) return 1;\n\n    if (g->flags & 0x80)\n        stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1);\n\n    return 1;\n}\n\nstatic int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) {\n    stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif));\n    if (!stbi__gif_header(s, g, comp, 1)) {\n        STBI_FREE(g);\n        stbi__rewind( s );\n        return 0;\n    }\n    if (x) *x = g->w;\n    if (y) *y = g->h;\n    STBI_FREE(g);\n    return 1;\n}\n\nstatic void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) {\n    stbi_uc *p, *c;\n    int idx;\n\n    // recurse to decode the prefixes, since the linked-list is backwards,\n    // and working backwards through an interleaved image would be nasty\n    if (g->codes[code].prefix >= 0)\n        stbi__out_gif_code(g, g->codes[code].prefix);\n\n    if (g->cur_y >= g->max_y) return;\n\n    idx = g->cur_x + g->cur_y;\n    p = &g->out[idx];\n    g->history[idx / 4] = 1;\n\n    c = &g->color_table[g->codes[code].suffix * 4];\n    if (c[3] > 128) { // don't render transparent pixels;\n        p[0] = c[2];\n        p[1] = c[1];\n        p[2] = c[0];\n        p[3] = c[3];\n    }\n    g->cur_x += 4;\n\n    if (g->cur_x >= g->max_x) {\n        g->cur_x = g->start_x;\n        g->cur_y += g->step;\n\n        while (g->cur_y >= g->max_y && g->parse > 0) {\n            g->step = (1 << g->parse) * g->line_size;\n            g->cur_y = g->start_y + (g->step >> 1);\n            --g->parse;\n        }\n    }\n}\n\nstatic stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) {\n    stbi_uc lzw_cs;\n    stbi__int32 len, init_code;\n    stbi__uint32 first;\n    stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear;\n    stbi__gif_lzw *p;\n\n    lzw_cs = stbi__get8(s);\n    if (lzw_cs > 12) return NULL;\n    clear = 1 << lzw_cs;\n    first = 1;\n    codesize = lzw_cs + 1;\n    codemask = (1 << codesize) - 1;\n    bits = 0;\n    valid_bits = 0;\n    for (init_code = 0; init_code < clear; init_code++) {\n        g->codes[init_code].prefix = -1;\n        g->codes[init_code].first = (stbi_uc) init_code;\n        g->codes[init_code].suffix = (stbi_uc) init_code;\n    }\n\n    // support no starting clear code\n    avail = clear+2;\n    oldcode = -1;\n\n    len = 0;\n    for(;;) {\n        if (valid_bits < codesize) {\n            if (len == 0) {\n                len = stbi__get8(s); // start new block\n                if (len == 0)\n                    return g->out;\n            }\n            --len;\n            bits |= (stbi__int32) stbi__get8(s) << valid_bits;\n            valid_bits += 8;\n        } else {\n            stbi__int32 code = bits & codemask;\n            bits >>= codesize;\n            valid_bits -= codesize;\n            // @OPTIMIZE: is there some way we can accelerate the non-clear path?\n            if (code == clear) {  // clear code\n                codesize = lzw_cs + 1;\n                codemask = (1 << codesize) - 1;\n                avail = clear + 2;\n                oldcode = -1;\n                first = 0;\n            } else if (code == clear + 1) { // end of stream code\n                stbi__skip(s, len);\n                while ((len = stbi__get8(s)) > 0)\n                    stbi__skip(s,len);\n                return g->out;\n            } else if (code <= avail) {\n                if (first) {\n                    return stbi__errpuc(\"no clear code\", \"Corrupt GIF\");\n                }\n\n                if (oldcode >= 0) {\n                    p = &g->codes[avail++];\n                    if (avail > 8192) {\n                        return stbi__errpuc(\"too many codes\", \"Corrupt GIF\");\n                    }\n\n                    p->prefix = (stbi__int16) oldcode;\n                    p->first = g->codes[oldcode].first;\n                    p->suffix = (code == avail) ? p->first : g->codes[code].first;\n                } else if (code == avail)\n                    return stbi__errpuc(\"illegal code in raster\", \"Corrupt GIF\");\n\n                stbi__out_gif_code(g, (stbi__uint16) code);\n\n                if ((avail & codemask) == 0 && avail <= 0x0FFF) {\n                    codesize++;\n                    codemask = (1 << codesize) - 1;\n                }\n\n                oldcode = code;\n            } else {\n                return stbi__errpuc(\"illegal code in raster\", \"Corrupt GIF\");\n            }\n        }\n    }\n}\n\n// this function is designed to support animated gifs, although stb_image doesn't support it\n// two back is the image from two frames ago, used for a very specific disposal format\nstatic stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) {\n    int dispose;\n    int first_frame;\n    int pi;\n    int pcount;\n\n    // on first frame, any non-written pixels get the background colour (non-transparent)\n    first_frame = 0;\n    if (g->out == 0) {\n        if (!stbi__gif_header(s, g, comp,0))     return 0; // stbi__g_failure_reason set by stbi__gif_header\n        g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h);\n        g->background = (stbi_uc *) stbi__malloc(4 * g->w * g->h);\n        g->history = (stbi_uc *) stbi__malloc(g->w * g->h);\n        if (g->out == 0)                      return stbi__errpuc(\"outofmem\", \"Out of memory\");\n\n        // image is treated as \"tranparent\" at the start - ie, nothing overwrites the current background;\n        // background colour is only used for pixels that are not rendered first frame, after that \"background\"\n        // color refers to teh color that was there the previous frame.\n        memset( g->out, 0x00, 4 * g->w * g->h );\n        memset( g->background, 0x00, 4 * g->w * g->h ); // state of the background (starts transparent)\n        memset( g->history, 0x00, g->w * g->h );        // pixels that were affected previous frame\n        first_frame = 1;\n    } else {\n        // second frame - how do we dispoase of the previous one?\n        dispose = (g->eflags & 0x1C) >> 2;\n        pcount = g->w * g->h;\n\n        if ((dispose == 3) && (two_back == 0)) {\n            dispose = 2; // if I don't have an image to revert back to, default to the old background\n        }\n\n        if (dispose == 3) { // use previous graphic\n            for (pi = 0; pi < pcount; ++pi) {\n                if (g->history[pi]) {\n                    memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 );\n                }\n            }\n        } else if (dispose == 2) {\n            // restore what was changed last frame to background before that frame;\n            for (pi = 0; pi < pcount; ++pi) {\n                if (g->history[pi]) {\n                    memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 );\n                }\n            }\n        } else {\n            // This is a non-disposal case eithe way, so just\n            // leave the pixels as is, and they will become the new background\n            // 1: do not dispose\n            // 0:  not specified.\n        }\n\n        // background is what out is after the undoing of the previou frame;\n        memcpy( g->background, g->out, 4 * g->w * g->h );\n    }\n\n    // clear my history;\n    memset( g->history, 0x00, g->w * g->h );        // pixels that were affected previous frame\n\n    for (;;) {\n        int tag = stbi__get8(s);\n        switch (tag) {\n        case 0x2C: { /* Image Descriptor */\n            stbi__int32 x, y, w, h;\n            stbi_uc *o;\n\n            x = stbi__get16le(s);\n            y = stbi__get16le(s);\n            w = stbi__get16le(s);\n            h = stbi__get16le(s);\n            if (((x + w) > (g->w)) || ((y + h) > (g->h)))\n                return stbi__errpuc(\"bad Image Descriptor\", \"Corrupt GIF\");\n\n            g->line_size = g->w * 4;\n            g->start_x = x * 4;\n            g->start_y = y * g->line_size;\n            g->max_x   = g->start_x + w * 4;\n            g->max_y   = g->start_y + h * g->line_size;\n            g->cur_x   = g->start_x;\n            g->cur_y   = g->start_y;\n\n            g->lflags = stbi__get8(s);\n\n            if (g->lflags & 0x40) {\n                g->step = 8 * g->line_size; // first interlaced spacing\n                g->parse = 3;\n            } else {\n                g->step = g->line_size;\n                g->parse = 0;\n            }\n\n            if (g->lflags & 0x80) {\n                stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1);\n                g->color_table = (stbi_uc *) g->lpal;\n            } else if (g->flags & 0x80) {\n                g->color_table = (stbi_uc *) g->pal;\n            } else\n                return stbi__errpuc(\"missing color table\", \"Corrupt GIF\");\n\n            o = stbi__process_gif_raster(s, g);\n            if (o == NULL) return NULL;\n\n            // if this was the first frame,\n            pcount = g->w * g->h;\n            if (first_frame && (g->bgindex > 0)) {\n                // if first frame, any pixel not drawn to gets the background color\n                for (pi = 0; pi < pcount; ++pi) {\n                    if (g->history[pi] == 0) {\n                        g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be;\n                        memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 );\n                    }\n                }\n            }\n\n            return o;\n        }\n\n        case 0x21: { // Comment Extension.\n            int len;\n            int ext = stbi__get8(s);\n            if (ext == 0xF9) { // Graphic Control Extension.\n                len = stbi__get8(s);\n                if (len == 4) {\n                    g->eflags = stbi__get8(s);\n                    g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths.\n\n                    // unset old transparent\n                    if (g->transparent >= 0) {\n                        g->pal[g->transparent][3] = 255;\n                    }\n                    if (g->eflags & 0x01) {\n                        g->transparent = stbi__get8(s);\n                        if (g->transparent >= 0) {\n                            g->pal[g->transparent][3] = 0;\n                        }\n                    } else {\n                        // don't need transparent\n                        stbi__skip(s, 1);\n                        g->transparent = -1;\n                    }\n                } else {\n                    stbi__skip(s, len);\n                    break;\n                }\n            }\n            while ((len = stbi__get8(s)) != 0) {\n                stbi__skip(s, len);\n            }\n            break;\n        }\n\n        case 0x3B: // gif stream termination code\n            return (stbi_uc *) s; // using '1' causes warning on some compilers\n\n        default:\n            return stbi__errpuc(\"unknown code\", \"Corrupt GIF\");\n        }\n    }\n}\n\nstatic void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) {\n    if (stbi__gif_test(s)) {\n        int layers = 0;\n        stbi_uc *u = 0;\n        stbi_uc *out = 0;\n        stbi_uc *two_back = 0;\n        stbi__gif g;\n        int stride;\n        memset(&g, 0, sizeof(g));\n        if (delays) {\n            *delays = 0;\n        }\n\n        do {\n            u = stbi__gif_load_next(s, &g, comp, req_comp, two_back);\n            if (u == (stbi_uc *) s) u = 0;  // end of animated gif marker\n\n            if (u) {\n                *x = g.w;\n                *y = g.h;\n                ++layers;\n                stride = g.w * g.h * 4;\n\n                if (out) {\n                    out = (stbi_uc*) STBI_REALLOC( out, layers * stride );\n                    if (delays) {\n                        *delays = (int*) STBI_REALLOC( *delays, sizeof(int) * layers );\n                    }\n                } else {\n                    out = (stbi_uc*)stbi__malloc( layers * stride );\n                    if (delays) {\n                        *delays = (int*) stbi__malloc( layers * sizeof(int) );\n                    }\n                }\n                memcpy( out + ((layers - 1) * stride), u, stride );\n                if (layers >= 2) {\n                    two_back = out - 2 * stride;\n                }\n\n                if (delays) {\n                    (*delays)[layers - 1U] = g.delay;\n                }\n            }\n        } while (u != 0);\n\n        // free temp buffer;\n        STBI_FREE(g.out);\n        STBI_FREE(g.history);\n        STBI_FREE(g.background);\n\n        // do the final conversion after loading everything;\n        if (req_comp && req_comp != 4)\n            out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h);\n\n        *z = layers;\n        return out;\n    } else {\n        return stbi__errpuc(\"not GIF\", \"Image was not as a gif type.\");\n    }\n}\n\nstatic void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) {\n    stbi_uc *u = 0;\n    stbi__gif g;\n    memset(&g, 0, sizeof(g));\n\n    u = stbi__gif_load_next(s, &g, comp, req_comp, 0);\n    if (u == (stbi_uc *) s) u = 0;  // end of animated gif marker\n    if (u) {\n        *x = g.w;\n        *y = g.h;\n\n        // moved conversion to after successful load so that the same\n        // can be done for multiple frames.\n        if (req_comp && req_comp != 4)\n            u = stbi__convert_format(u, 4, req_comp, g.w, g.h);\n    }\n\n    // free buffers needed for multiple frame loading;\n    STBI_FREE(g.history);\n    STBI_FREE(g.background);\n\n    return u;\n}\n\nstatic int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) {\n    return stbi__gif_info_raw(s,x,y,comp);\n}\n#endif\n\n// *************************************************************************************************\n// Radiance RGBE HDR loader\n// originally by Nicolas Schulz\n#ifndef STBI_NO_HDR\nstatic int stbi__hdr_test_core(stbi__context *s, const char *signature) {\n    int i;\n    for (i=0; signature[i]; ++i)\n        if (stbi__get8(s) != signature[i])\n            return 0;\n    stbi__rewind(s);\n    return 1;\n}\n\nstatic int stbi__hdr_test(stbi__context* s) {\n    int r = stbi__hdr_test_core(s, \"#?RADIANCE\\n\");\n    stbi__rewind(s);\n    if(!r) {\n        r = stbi__hdr_test_core(s, \"#?RGBE\\n\");\n        stbi__rewind(s);\n    }\n    return r;\n}\n\n#define STBI__HDR_BUFLEN  1024\nstatic char *stbi__hdr_gettoken(stbi__context *z, char *buffer) {\n    int len=0;\n    char c = '\\0';\n\n    c = (char) stbi__get8(z);\n\n    while (!stbi__at_eof(z) && c != '\\n') {\n        buffer[len++] = c;\n        if (len == STBI__HDR_BUFLEN-1) {\n            // flush to end of line\n            while (!stbi__at_eof(z) && stbi__get8(z) != '\\n')\n                ;\n            break;\n        }\n        c = (char) stbi__get8(z);\n    }\n\n    buffer[len] = 0;\n    return buffer;\n}\n\nstatic void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) {\n    if ( input[3] != 0 ) {\n        float f1;\n        // Exponent\n        f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8));\n        if (req_comp <= 2)\n            output[0] = (input[0] + input[1] + input[2]) * f1 / 3;\n        else {\n            output[0] = input[0] * f1;\n            output[1] = input[1] * f1;\n            output[2] = input[2] * f1;\n        }\n        if (req_comp == 2) output[1] = 1;\n        if (req_comp == 4) output[3] = 1;\n    } else {\n        switch (req_comp) {\n        case 4:\n            output[3] = 1; /* fallthrough */\n        case 3:\n            output[0] = output[1] = output[2] = 0;\n            break;\n        case 2:\n            output[1] = 1; /* fallthrough */\n        case 1:\n            output[0] = 0;\n            break;\n        }\n    }\n}\n\nstatic float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) {\n    char buffer[STBI__HDR_BUFLEN];\n    char *token;\n    int valid = 0;\n    int width, height;\n    stbi_uc *scanline;\n    float *hdr_data;\n    int len;\n    unsigned char count, value;\n    int i, j, k, c1,c2, z;\n    const char *headerToken;\n    STBI_NOTUSED(ri);\n\n    // Check identifier\n    headerToken = stbi__hdr_gettoken(s,buffer);\n    if (strcmp(headerToken, \"#?RADIANCE\") != 0 && strcmp(headerToken, \"#?RGBE\") != 0)\n        return stbi__errpf(\"not HDR\", \"Corrupt HDR image\");\n\n    // Parse header\n    for(;;) {\n        token = stbi__hdr_gettoken(s,buffer);\n        if (token[0] == 0) break;\n        if (strcmp(token, \"FORMAT=32-bit_rle_rgbe\") == 0) valid = 1;\n    }\n\n    if (!valid)    return stbi__errpf(\"unsupported format\", \"Unsupported HDR format\");\n\n    // Parse width and height\n    // can't use sscanf() if we're not using stdio!\n    token = stbi__hdr_gettoken(s,buffer);\n    if (strncmp(token, \"-Y \", 3))  return stbi__errpf(\"unsupported data layout\", \"Unsupported HDR format\");\n    token += 3;\n    height = (int) strtol(token, &token, 10);\n    while (*token == ' ') ++token;\n    if (strncmp(token, \"+X \", 3))  return stbi__errpf(\"unsupported data layout\", \"Unsupported HDR format\");\n    token += 3;\n    width = (int) strtol(token, NULL, 10);\n\n    *x = width;\n    *y = height;\n\n    if (comp) *comp = 3;\n    if (req_comp == 0) req_comp = 3;\n\n    if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0))\n        return stbi__errpf(\"too large\", \"HDR image is too large\");\n\n    // Read data\n    hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0);\n    if (!hdr_data)\n        return stbi__errpf(\"outofmem\", \"Out of memory\");\n\n    // Load image data\n    // image data is stored as some number of sca\n    if ( width < 8 || width >= 32768) {\n        // Read flat data\n        for (j=0; j < height; ++j) {\n            for (i=0; i < width; ++i) {\n                stbi_uc rgbe[4];\nmain_decode_loop:\n                stbi__getn(s, rgbe, 4);\n                stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp);\n            }\n        }\n    } else {\n        // Read RLE-encoded data\n        scanline = NULL;\n\n        for (j = 0; j < height; ++j) {\n            c1 = stbi__get8(s);\n            c2 = stbi__get8(s);\n            len = stbi__get8(s);\n            if (c1 != 2 || c2 != 2 || (len & 0x80)) {\n                // not run-length encoded, so we have to actually use THIS data as a decoded\n                // pixel (note this can't be a valid pixel--one of RGB must be >= 128)\n                stbi_uc rgbe[4];\n                rgbe[0] = (stbi_uc) c1;\n                rgbe[1] = (stbi_uc) c2;\n                rgbe[2] = (stbi_uc) len;\n                rgbe[3] = (stbi_uc) stbi__get8(s);\n                stbi__hdr_convert(hdr_data, rgbe, req_comp);\n                i = 1;\n                j = 0;\n                STBI_FREE(scanline);\n                goto main_decode_loop; // yes, this makes no sense\n            }\n            len <<= 8;\n            len |= stbi__get8(s);\n            if (len != width) {\n                STBI_FREE(hdr_data);\n                STBI_FREE(scanline);\n                return stbi__errpf(\"invalid decoded scanline length\", \"corrupt HDR\");\n            }\n            if (scanline == NULL) {\n                scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0);\n                if (!scanline) {\n                    STBI_FREE(hdr_data);\n                    return stbi__errpf(\"outofmem\", \"Out of memory\");\n                }\n            }\n\n            for (k = 0; k < 4; ++k) {\n                int nleft;\n                i = 0;\n                while ((nleft = width - i) > 0) {\n                    count = stbi__get8(s);\n                    if (count > 128) {\n                        // Run\n                        value = stbi__get8(s);\n                        count -= 128;\n                        if (count > nleft) {\n                            STBI_FREE(hdr_data);\n                            STBI_FREE(scanline);\n                            return stbi__errpf(\"corrupt\", \"bad RLE data in HDR\");\n                        }\n                        for (z = 0; z < count; ++z)\n                            scanline[i++ * 4 + k] = value;\n                    } else {\n                        // Dump\n                        if (count > nleft) {\n                            STBI_FREE(hdr_data);\n                            STBI_FREE(scanline);\n                            return stbi__errpf(\"corrupt\", \"bad RLE data in HDR\");\n                        }\n                        for (z = 0; z < count; ++z)\n                            scanline[i++ * 4 + k] = stbi__get8(s);\n                    }\n                }\n            }\n            for (i=0; i < width; ++i)\n                stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp);\n        }\n        if (scanline)\n            STBI_FREE(scanline);\n    }\n\n    return hdr_data;\n}\n\nstatic int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) {\n    char buffer[STBI__HDR_BUFLEN];\n    char *token;\n    int valid = 0;\n    int dummy;\n\n    if (!x) x = &dummy;\n    if (!y) y = &dummy;\n    if (!comp) comp = &dummy;\n\n    if (stbi__hdr_test(s) == 0) {\n        stbi__rewind( s );\n        return 0;\n    }\n\n    for(;;) {\n        token = stbi__hdr_gettoken(s,buffer);\n        if (token[0] == 0) break;\n        if (strcmp(token, \"FORMAT=32-bit_rle_rgbe\") == 0) valid = 1;\n    }\n\n    if (!valid) {\n        stbi__rewind( s );\n        return 0;\n    }\n    token = stbi__hdr_gettoken(s,buffer);\n    if (strncmp(token, \"-Y \", 3)) {\n        stbi__rewind( s );\n        return 0;\n    }\n    token += 3;\n    *y = (int) strtol(token, &token, 10);\n    while (*token == ' ') ++token;\n    if (strncmp(token, \"+X \", 3)) {\n        stbi__rewind( s );\n        return 0;\n    }\n    token += 3;\n    *x = (int) strtol(token, NULL, 10);\n    *comp = 3;\n    return 1;\n}\n#endif // STBI_NO_HDR\n\n#ifndef STBI_NO_BMP\nstatic int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) {\n    void *p;\n    stbi__bmp_data info;\n\n    info.all_a = 255;\n    p = stbi__bmp_parse_header(s, &info);\n    stbi__rewind( s );\n    if (p == NULL)\n        return 0;\n    if (x) *x = s->img_x;\n    if (y) *y = s->img_y;\n    if (comp) *comp = info.ma ? 4 : 3;\n    return 1;\n}\n#endif\n\n#ifndef STBI_NO_PSD\nstatic int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) {\n    int channelCount, dummy, depth;\n    if (!x) x = &dummy;\n    if (!y) y = &dummy;\n    if (!comp) comp = &dummy;\n    if (stbi__get32be(s) != 0x38425053) {\n        stbi__rewind( s );\n        return 0;\n    }\n    if (stbi__get16be(s) != 1) {\n        stbi__rewind( s );\n        return 0;\n    }\n    stbi__skip(s, 6);\n    channelCount = stbi__get16be(s);\n    if (channelCount < 0 || channelCount > 16) {\n        stbi__rewind( s );\n        return 0;\n    }\n    *y = stbi__get32be(s);\n    *x = stbi__get32be(s);\n    depth = stbi__get16be(s);\n    if (depth != 8 && depth != 16) {\n        stbi__rewind( s );\n        return 0;\n    }\n    if (stbi__get16be(s) != 3) {\n        stbi__rewind( s );\n        return 0;\n    }\n    *comp = 4;\n    return 1;\n}\n\nstatic int stbi__psd_is16(stbi__context *s) {\n    int channelCount, depth;\n    if (stbi__get32be(s) != 0x38425053) {\n        stbi__rewind( s );\n        return 0;\n    }\n    if (stbi__get16be(s) != 1) {\n        stbi__rewind( s );\n        return 0;\n    }\n    stbi__skip(s, 6);\n    channelCount = stbi__get16be(s);\n    if (channelCount < 0 || channelCount > 16) {\n        stbi__rewind( s );\n        return 0;\n    }\n    (void) stbi__get32be(s);\n    (void) stbi__get32be(s);\n    depth = stbi__get16be(s);\n    if (depth != 16) {\n        stbi__rewind( s );\n        return 0;\n    }\n    return 1;\n}\n#endif\n\n#ifndef STBI_NO_PIC\nstatic int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) {\n    int act_comp=0,num_packets=0,chained,dummy;\n    stbi__pic_packet packets[10];\n\n    if (!x) x = &dummy;\n    if (!y) y = &dummy;\n    if (!comp) comp = &dummy;\n\n    if (!stbi__pic_is4(s,\"\\x53\\x80\\xF6\\x34\")) {\n        stbi__rewind(s);\n        return 0;\n    }\n\n    stbi__skip(s, 88);\n\n    *x = stbi__get16be(s);\n    *y = stbi__get16be(s);\n    if (stbi__at_eof(s)) {\n        stbi__rewind( s);\n        return 0;\n    }\n    if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) {\n        stbi__rewind( s );\n        return 0;\n    }\n\n    stbi__skip(s, 8);\n\n    do {\n        stbi__pic_packet *packet;\n\n        if (num_packets==sizeof(packets)/sizeof(packets[0]))\n            return 0;\n\n        packet = &packets[num_packets++];\n        chained = stbi__get8(s);\n        packet->size    = stbi__get8(s);\n        packet->type    = stbi__get8(s);\n        packet->channel = stbi__get8(s);\n        act_comp |= packet->channel;\n\n        if (stbi__at_eof(s)) {\n            stbi__rewind( s );\n            return 0;\n        }\n        if (packet->size != 8) {\n            stbi__rewind( s );\n            return 0;\n        }\n    } while (chained);\n\n    *comp = (act_comp & 0x10 ? 4 : 3);\n\n    return 1;\n}\n#endif\n\n// *************************************************************************************************\n// Portable Gray Map and Portable Pixel Map loader\n// by Ken Miller\n//\n// PGM: http://netpbm.sourceforge.net/doc/pgm.html\n// PPM: http://netpbm.sourceforge.net/doc/ppm.html\n//\n// Known limitations:\n//    Does not support comments in the header section\n//    Does not support ASCII image data (formats P2 and P3)\n//    Does not support 16-bit-per-channel\n\n#ifndef STBI_NO_PNM\n\nstatic int      stbi__pnm_test(stbi__context *s) {\n    char p, t;\n    p = (char) stbi__get8(s);\n    t = (char) stbi__get8(s);\n    if (p != 'P' || (t != '5' && t != '6')) {\n        stbi__rewind( s );\n        return 0;\n    }\n    return 1;\n}\n\nstatic void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) {\n    stbi_uc *out;\n    STBI_NOTUSED(ri);\n\n    if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n))\n        return 0;\n\n    *x = s->img_x;\n    *y = s->img_y;\n    if (comp) *comp = s->img_n;\n\n    if (!stbi__mad3sizes_valid(s->img_n, s->img_x, s->img_y, 0))\n        return stbi__errpuc(\"too large\", \"PNM too large\");\n\n    out = (stbi_uc *) stbi__malloc_mad3(s->img_n, s->img_x, s->img_y, 0);\n    if (!out) return stbi__errpuc(\"outofmem\", \"Out of memory\");\n    stbi__getn(s, out, s->img_n * s->img_x * s->img_y);\n\n    if (req_comp && req_comp != s->img_n) {\n        out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y);\n        if (out == NULL) return out; // stbi__convert_format frees input on failure\n    }\n    return out;\n}\n\nstatic int      stbi__pnm_isspace(char c) {\n    return c == ' ' || c == '\\t' || c == '\\n' || c == '\\v' || c == '\\f' || c == '\\r';\n}\n\nstatic void     stbi__pnm_skip_whitespace(stbi__context *s, char *c) {\n    for (;;) {\n        while (!stbi__at_eof(s) && stbi__pnm_isspace(*c))\n            *c = (char) stbi__get8(s);\n\n        if (stbi__at_eof(s) || *c != '#')\n            break;\n\n        while (!stbi__at_eof(s) && *c != '\\n' && *c != '\\r' )\n            *c = (char) stbi__get8(s);\n    }\n}\n\nstatic int      stbi__pnm_isdigit(char c) {\n    return c >= '0' && c <= '9';\n}\n\nstatic int      stbi__pnm_getinteger(stbi__context *s, char *c) {\n    int value = 0;\n\n    while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) {\n        value = value*10 + (*c - '0');\n        *c = (char) stbi__get8(s);\n    }\n\n    return value;\n}\n\nstatic int      stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) {\n    int maxv, dummy;\n    char c, p, t;\n\n    if (!x) x = &dummy;\n    if (!y) y = &dummy;\n    if (!comp) comp = &dummy;\n\n    stbi__rewind(s);\n\n    // Get identifier\n    p = (char) stbi__get8(s);\n    t = (char) stbi__get8(s);\n    if (p != 'P' || (t != '5' && t != '6')) {\n        stbi__rewind(s);\n        return 0;\n    }\n\n    *comp = (t == '6') ? 3 : 1;  // '5' is 1-component .pgm; '6' is 3-component .ppm\n\n    c = (char) stbi__get8(s);\n    stbi__pnm_skip_whitespace(s, &c);\n\n    *x = stbi__pnm_getinteger(s, &c); // read width\n    stbi__pnm_skip_whitespace(s, &c);\n\n    *y = stbi__pnm_getinteger(s, &c); // read height\n    stbi__pnm_skip_whitespace(s, &c);\n\n    maxv = stbi__pnm_getinteger(s, &c);  // read max value\n\n    if (maxv > 255)\n        return stbi__err(\"max value > 255\", \"PPM image not 8-bit\");\n    else\n        return 1;\n}\n#endif\n\nstatic int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) {\n#ifndef STBI_NO_JPEG\n    if (stbi__jpeg_info(s, x, y, comp)) return 1;\n#endif\n\n#ifndef STBI_NO_PNG\n    if (stbi__png_info(s, x, y, comp))  return 1;\n#endif\n\n#ifndef STBI_NO_GIF\n    if (stbi__gif_info(s, x, y, comp))  return 1;\n#endif\n\n#ifndef STBI_NO_BMP\n    if (stbi__bmp_info(s, x, y, comp))  return 1;\n#endif\n\n#ifndef STBI_NO_PSD\n    if (stbi__psd_info(s, x, y, comp))  return 1;\n#endif\n\n#ifndef STBI_NO_PIC\n    if (stbi__pic_info(s, x, y, comp))  return 1;\n#endif\n\n#ifndef STBI_NO_PNM\n    if (stbi__pnm_info(s, x, y, comp))  return 1;\n#endif\n\n#ifndef STBI_NO_HDR\n    if (stbi__hdr_info(s, x, y, comp))  return 1;\n#endif\n\n    // test tga last because it's a crappy test!\n#ifndef STBI_NO_TGA\n    if (stbi__tga_info(s, x, y, comp))\n        return 1;\n#endif\n    return stbi__err(\"unknown image type\", \"Image not of any known type, or corrupt\");\n}\n\nstatic int stbi__is_16_main(stbi__context *s) {\n#ifndef STBI_NO_PNG\n    if (stbi__png_is16(s))  return 1;\n#endif\n\n#ifndef STBI_NO_PSD\n    if (stbi__psd_is16(s))  return 1;\n#endif\n\n    return 0;\n}\n\n#ifndef STBI_NO_STDIO\nSTBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) {\n    FILE *f = stbi__fopen(filename, \"rb\");\n    int result;\n    if (!f) return stbi__err(\"can't fopen\", \"Unable to open file\");\n    result = stbi_info_from_file(f, x, y, comp);\n    fclose(f);\n    return result;\n}\n\nSTBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) {\n    int r;\n    stbi__context s;\n    long pos = ftell(f);\n    stbi__start_file(&s, f);\n    r = stbi__info_main(&s,x,y,comp);\n    fseek(f,pos,SEEK_SET);\n    return r;\n}\n\nSTBIDEF int stbi_is_16_bit(char const *filename) {\n    FILE *f = stbi__fopen(filename, \"rb\");\n    int result;\n    if (!f) return stbi__err(\"can't fopen\", \"Unable to open file\");\n    result = stbi_is_16_bit_from_file(f);\n    fclose(f);\n    return result;\n}\n\nSTBIDEF int stbi_is_16_bit_from_file(FILE *f) {\n    int r;\n    stbi__context s;\n    long pos = ftell(f);\n    stbi__start_file(&s, f);\n    r = stbi__is_16_main(&s);\n    fseek(f,pos,SEEK_SET);\n    return r;\n}\n#endif // !STBI_NO_STDIO\n\nSTBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) {\n    stbi__context s;\n    stbi__start_mem(&s,buffer,len);\n    return stbi__info_main(&s,x,y,comp);\n}\n\nSTBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) {\n    stbi__context s;\n    stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user);\n    return stbi__info_main(&s,x,y,comp);\n}\n\nSTBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) {\n    stbi__context s;\n    stbi__start_mem(&s,buffer,len);\n    return stbi__is_16_main(&s);\n}\n\nSTBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) {\n    stbi__context s;\n    stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user);\n    return stbi__is_16_main(&s);\n}\n\n#endif // STB_IMAGE_IMPLEMENTATION\n\n/*\n   revision history:\n      2.19  (2018-02-11) fix warning\n      2.18  (2018-01-30) fix warnings\n      2.17  (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug\n                         1-bit BMP\n                         *_is_16_bit api\n                         avoid warnings\n      2.16  (2017-07-23) all functions have 16-bit variants;\n                         STBI_NO_STDIO works again;\n                         compilation fixes;\n                         fix rounding in unpremultiply;\n                         optimize vertical flip;\n                         disable raw_len validation;\n                         documentation fixes\n      2.15  (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode;\n                         warning fixes; disable run-time SSE detection on gcc;\n                         uniform handling of optional \"return\" values;\n                         thread-safe initialization of zlib tables\n      2.14  (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs\n      2.13  (2016-11-29) add 16-bit API, only supported for PNG right now\n      2.12  (2016-04-02) fix typo in 2.11 PSD fix that caused crashes\n      2.11  (2016-04-02) allocate large structures on the stack\n                         remove white matting for transparent PSD\n                         fix reported channel count for PNG & BMP\n                         re-enable SSE2 in non-gcc 64-bit\n                         support RGB-formatted JPEG\n                         read 16-bit PNGs (only as 8-bit)\n      2.10  (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED\n      2.09  (2016-01-16) allow comments in PNM files\n                         16-bit-per-pixel TGA (not bit-per-component)\n                         info() for TGA could break due to .hdr handling\n                         info() for BMP to shares code instead of sloppy parse\n                         can use STBI_REALLOC_SIZED if allocator doesn't support realloc\n                         code cleanup\n      2.08  (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA\n      2.07  (2015-09-13) fix compiler warnings\n                         partial animated GIF support\n                         limited 16-bpc PSD support\n                         #ifdef unused functions\n                         bug with < 92 byte PIC,PNM,HDR,TGA\n      2.06  (2015-04-19) fix bug where PSD returns wrong '*comp' value\n      2.05  (2015-04-19) fix bug in progressive JPEG handling, fix warning\n      2.04  (2015-04-15) try to re-enable SIMD on MinGW 64-bit\n      2.03  (2015-04-12) extra corruption checking (mmozeiko)\n                         stbi_set_flip_vertically_on_load (nguillemot)\n                         fix NEON support; fix mingw support\n      2.02  (2015-01-19) fix incorrect assert, fix warning\n      2.01  (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2\n      2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG\n      2.00  (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg)\n                         progressive JPEG (stb)\n                         PGM/PPM support (Ken Miller)\n                         STBI_MALLOC,STBI_REALLOC,STBI_FREE\n                         GIF bugfix -- seemingly never worked\n                         STBI_NO_*, STBI_ONLY_*\n      1.48  (2014-12-14) fix incorrectly-named assert()\n      1.47  (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb)\n                         optimize PNG (ryg)\n                         fix bug in interlaced PNG with user-specified channel count (stb)\n      1.46  (2014-08-26)\n              fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG\n      1.45  (2014-08-16)\n              fix MSVC-ARM internal compiler error by wrapping malloc\n      1.44  (2014-08-07)\n              various warning fixes from Ronny Chevalier\n      1.43  (2014-07-15)\n              fix MSVC-only compiler problem in code changed in 1.42\n      1.42  (2014-07-09)\n              don't define _CRT_SECURE_NO_WARNINGS (affects user code)\n              fixes to stbi__cleanup_jpeg path\n              added STBI_ASSERT to avoid requiring assert.h\n      1.41  (2014-06-25)\n              fix search&replace from 1.36 that messed up comments/error messages\n      1.40  (2014-06-22)\n              fix gcc struct-initialization warning\n      1.39  (2014-06-15)\n              fix to TGA optimization when req_comp != number of components in TGA;\n              fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite)\n              add support for BMP version 5 (more ignored fields)\n      1.38  (2014-06-06)\n              suppress MSVC warnings on integer casts truncating values\n              fix accidental rename of 'skip' field of I/O\n      1.37  (2014-06-04)\n              remove duplicate typedef\n      1.36  (2014-06-03)\n              convert to header file single-file library\n              if de-iphone isn't set, load iphone images color-swapped instead of returning NULL\n      1.35  (2014-05-27)\n              various warnings\n              fix broken STBI_SIMD path\n              fix bug where stbi_load_from_file no longer left file pointer in correct place\n              fix broken non-easy path for 32-bit BMP (possibly never used)\n              TGA optimization by Arseny Kapoulkine\n      1.34  (unknown)\n              use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case\n      1.33  (2011-07-14)\n              make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements\n      1.32  (2011-07-13)\n              support for \"info\" function for all supported filetypes (SpartanJ)\n      1.31  (2011-06-20)\n              a few more leak fixes, bug in PNG handling (SpartanJ)\n      1.30  (2011-06-11)\n              added ability to load files via callbacks to accomidate custom input streams (Ben Wenger)\n              removed deprecated format-specific test/load functions\n              removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway\n              error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha)\n              fix inefficiency in decoding 32-bit BMP (David Woo)\n      1.29  (2010-08-16)\n              various warning fixes from Aurelien Pocheville\n      1.28  (2010-08-01)\n              fix bug in GIF palette transparency (SpartanJ)\n      1.27  (2010-08-01)\n              cast-to-stbi_uc to fix warnings\n      1.26  (2010-07-24)\n              fix bug in file buffering for PNG reported by SpartanJ\n      1.25  (2010-07-17)\n              refix trans_data warning (Won Chun)\n      1.24  (2010-07-12)\n              perf improvements reading from files on platforms with lock-heavy fgetc()\n              minor perf improvements for jpeg\n              deprecated type-specific functions so we'll get feedback if they're needed\n              attempt to fix trans_data warning (Won Chun)\n      1.23    fixed bug in iPhone support\n      1.22  (2010-07-10)\n              removed image *writing* support\n              stbi_info support from Jetro Lauha\n              GIF support from Jean-Marc Lienher\n              iPhone PNG-extensions from James Brown\n              warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva)\n      1.21    fix use of 'stbi_uc' in header (reported by jon blow)\n      1.20    added support for Softimage PIC, by Tom Seddon\n      1.19    bug in interlaced PNG corruption check (found by ryg)\n      1.18  (2008-08-02)\n              fix a threading bug (local mutable static)\n      1.17    support interlaced PNG\n      1.16    major bugfix - stbi__convert_format converted one too many pixels\n      1.15    initialize some fields for thread safety\n      1.14    fix threadsafe conversion bug\n              header-file-only version (#define STBI_HEADER_FILE_ONLY before including)\n      1.13    threadsafe\n      1.12    const qualifiers in the API\n      1.11    Support installable IDCT, colorspace conversion routines\n      1.10    Fixes for 64-bit (don't use \"unsigned long\")\n              optimized upsampling by Fabian \"ryg\" Giesen\n      1.09    Fix format-conversion for PSD code (bad global variables!)\n      1.08    Thatcher Ulrich's PSD code integrated by Nicolas Schulz\n      1.07    attempt to fix C++ warning/errors again\n      1.06    attempt to fix C++ warning/errors again\n      1.05    fix TGA loading to return correct *comp and use good luminance calc\n      1.04    default float alpha is 1, not 255; use 'void *' for stbi_image_free\n      1.03    bugfixes to STBI_NO_STDIO, STBI_NO_HDR\n      1.02    support for (subset of) HDR files, float interface for preferred access to them\n      1.01    fix bug: possible bug in handling right-side up bmps... not sure\n              fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all\n      1.00    interface to zlib that skips zlib header\n      0.99    correct handling of alpha in palette\n      0.98    TGA loader by lonesock; dynamically add loaders (untested)\n      0.97    jpeg errors on too large a file; also catch another malloc failure\n      0.96    fix detection of invalid v value - particleman@mollyrocket forum\n      0.95    during header scan, seek to markers in case of padding\n      0.94    STBI_NO_STDIO to disable stdio usage; rename all #defines the same\n      0.93    handle jpegtran output; verbose errors\n      0.92    read 4,8,16,24,32-bit BMP files of several formats\n      0.91    output 24-bit Windows 3.0 BMP files\n      0.90    fix a few more warnings; bump version number to approach 1.0\n      0.61    bugfixes due to Marc LeBlanc, Christopher Lloyd\n      0.60    fix compiling as c++\n      0.59    fix warnings: merge Dave Moore's -Wall fixes\n      0.58    fix bug: zlib uncompressed mode len/nlen was wrong endian\n      0.57    fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available\n      0.56    fix bug: zlib uncompressed mode len vs. nlen\n      0.55    fix bug: restart_interval not initialized to 0\n      0.54    allow NULL for 'int *comp'\n      0.53    fix bug in png 3->4; speedup png decoding\n      0.52    png handles req_comp=3,4 directly; minor cleanup; jpeg comments\n      0.51    obey req_comp requests, 1-component jpegs return as 1-component,\n              on 'test' only check type, not whether we support this variant\n      0.50  (2006-11-19)\n              first released version\n*/\n\n\n/*\n------------------------------------------------------------------------------\nThis software is available under 2 licenses -- choose whichever you prefer.\n------------------------------------------------------------------------------\nALTERNATIVE A - MIT License\nCopyright (c) 2017 Sean Barrett\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso, subject to the following conditions:\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n------------------------------------------------------------------------------\nALTERNATIVE B - Public Domain (www.unlicense.org)\nThis is free and unencumbered software released into the public domain.\nAnyone is free to copy, modify, publish, use, compile, sell, or distribute this\nsoftware, either in source code form or as a compiled binary, for any purpose,\ncommercial or non-commercial, and by any means.\nIn jurisdictions that recognize copyright laws, the author or authors of this\nsoftware dedicate any and all copyright interest in the software to the public\ndomain. We make this dedication for the benefit of the public at large and to\nthe detriment of our heirs and successors. We intend this dedication to be an\novert act of relinquishment in perpetuity of all present and future rights to\nthis software under copyright law.\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\nACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n------------------------------------------------------------------------------\n*/\n"
  },
  {
    "path": "js.json",
    "content": "{\n  \"source\": \"./package.json\",\n  \"scripts\": {\n    \"run\": \"esy '@js' x bash -c 'http-server #{self.bin}'\"\n  },\n  \"override\": {\n    \"build\": [\"dune build --root . -j4\"],\n    \"install\": [\n      \"esy-installer Revery.install\",\n      \"esy-installer ReveryExampleJs.install\"\n    ],\n    \"dependencies\": {\n      \"@opam/js_of_ocaml\": \"*\",\n      \"@opam/js_of_ocaml-compiler\": \"*\",\n      \"@opam/js_of_ocaml-lwt\": \"*\",\n      \"http-server\": \"*\"\n    }\n  }\n}\n"
  },
  {
    "path": "lsan.supp",
    "content": "# Add suppression for libfontconfig, which has known leaks\nleak:*libfontconfig*\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"revery\",\n  \"version\": \"0.32.0\",\n  \"description\": \"App toolkit built with Reason\",\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/bryphe/revery/issues\"\n  },\n  \"scripts\": {\n    \"test\": \"esy b dune runtest\",\n    \"format\": \"esy #{os == 'windows' ? 'b' : ''} bash .ci/format.sh #{os}\",\n    \"run\": \"esy @examples x Examples\"\n  },\n  \"homepage\": \"https://github.com/bryphe/revery#readme\",\n  \"esy\": {\n    \"buildEnv\": {\n      \"PKG_CONFIG_PATH\": \"/usr/lib64/pkgconfig:$PKG_CONFIG_PATH\"\n    },\n    \"exportedEnv\": {\n      \"PKG_CONFIG_PATH\": {\n        \"val\": \"/usr/lib64/pkgconfig:$PKG_CONFIG_PATH\",\n        \"scope\": \"global\"\n      }\n    },\n    \"build\": \"dune build -p reason-harfbuzz,reason-skia,reason-sdl2,Revery\",\n    \"install\": [\n      \"esy-installer reason-harfbuzz.install\",\n      \"esy-installer reason-skia.install\",\n      \"esy-installer reason-sdl2.install\",\n      \"esy-installer Revery.install\",\n      \"bash -c \\\"#{os == 'windows' ? 'cp /usr/x86_64-w64-mingw32/sys-root/mingw/bin/*.dll \\\\'$cur__bin\\\\'': ':'}\\\"\",\n      \"bash -c \\\"cp #{esy-skia.bin}/skia.dll \\\\'$cur__bin\\\\' #{os == 'windows' ? '' : '2>/dev/null || true'}\\\"\",\n      \"bash -c \\\"cp #{esy-sdl2.bin}/*.dll \\\\'$cur__bin\\\\' #{os == 'windows' ? '' : '2>/dev/null || true'}\\\"\",\n      \"bash -c \\\"cp #{esy-angle-prebuilt.bin}/*.dll \\\\'$cur__bin\\\\' #{os == 'windows' ? '' : '2>/dev/null || true'}\\\"\"\n    ]\n  },\n  \"dependencies\": {\n    \"@opam/bos\": \"0.2.0\",\n    \"@opam/ctypes\": \"0.15.1\",\n    \"@opam/dune\": \"^2.5.0\",\n    \"@opam/dune-configurator\": \"*\",\n    \"@opam/reason\": \"^3.6.2\",\n    \"@opam/lru\": \"bryphe/lru:lru.opam#2708c70\",\n    \"@opam/lwt\": \"^5.0.0\",\n    \"@opam/lwt_ppx\": \"^1.1.0\",\n    \"@opam/markup\": \"0.8.2\",\n    \"@opam/ppx_deriving\": \"*\",\n    \"@opam/ppx_optcomp\": \"v0.14.0\",\n    \"@opam/omd\": \"ocaml/omd:omd.opam#1535e3c\",\n    \"@opam/uucp\": \"*\",\n    \"@opam/uutf\": \"*\",\n    \"@opam/charInfo_width\": \"*\",\n    \"@brisk/brisk-reconciler\": \"briskml/brisk-reconciler#c9d5c4c\",\n    \"esy-angle-prebuilt\": \"1.0.0\",\n    \"esy-freetype2\": \"^2.9.1008\",\n    \"@onivim/reason-native-crash-utils\": \"^1.0.2\",\n    \"@revery/esy-harfbuzz\": \"^2.6.8002\",\n    \"esy-skia\": \"revery-ui/esy-skia#1c81aac\",\n    \"esy-sdl2\": \"^2.0.14000\",\n    \"flex\": \"jordwalke/flex#6ff12fe\",\n    \"reperf\": \"^1.5.1\",\n    \"rench\": \"^1.10.0\",\n    \"rebez\": \"jchavarri/rebez#03fa3b7\",\n    \"@revery/timber\": \"^2.0.0\",\n    \"@reason-native/rely\": \"^3.2.1\"\n  },\n  \"resolutions\": {\n    \"@esy-ocaml/reason\": \"EduardoRFS/reason:reason.json#35aa4df3de0daa60bdc1133dcf97855decac48f7\"\n  },\n  \"devDependencies\": {\n    \"ocaml\": \"4.12.x\",\n    \"esy-astyle\": \"zbaylin/esy-astyle#59bc21a\",\n    \"@opam/ocaml-lsp-server\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/reason-harfbuzz/examples/harfbuzz-cli/HarfbuzzCli.re",
    "content": "open Harfbuzz;\n\nPrintexc.record_backtrace(true);\n\nlet isNative =\n  switch (Sys.backend_type) {\n  | Native => true\n  | Bytecode => true\n  | _ => false\n  };\n\nlet getExecutingDirectory = () =>\n  isNative ? Filename.dirname(Sys.argv[0]) ++ Filename.dir_sep : \"\";\n\nlet run = () => {\n  let show = ({glyphId, cluster}: hb_shape) =>\n    Printf.sprintf(\"GlyphID: %d Cluster: %d\", glyphId, cluster);\n\n  let renderString = (~features=[], font, str) => {\n    let shapes = Harfbuzz.hb_shape(~features, font, str);\n    print_endline(\"-- \" ++ str ++ \" --\");\n    Array.iter(s => {print_endline(\"- SHAPE: \" ++ show(s))}, shapes);\n    print_endline(\"----\");\n  };\n\n  let compiledVersion = hb_version_string_compiled();\n  let runtimeVersion = hb_version_string_runtime();\n  print_endline(\n    \"** Harfbuzz CLI, compiled version: \"\n    ++ compiledVersion\n    ++ \", runtime version: \"\n    ++ runtimeVersion\n    ++ \" **\\n\",\n  );\n\n  print_endline(\"__ Font Path: Roboto Regular __\");\n  let result = hb_face_from_path(\"test/collateral/FiraCode-Regular.ttf\");\n  let features = [\n    Harfbuzz.{tag: \"dlig\", value: 1, start: `Start, stop: `End},\n  ];\n  switch (result) {\n  | Error(msg) => failwith(msg)\n  | Ok(font) =>\n    renderString(font, \"abc\");\n    renderString(font, \"Harfbuzz\");\n    renderString(font, \"ff\");\n    renderString(~features, font, \"ff\");\n    renderString(font, \"κόσμε\");\n    renderString(font, \"\\t\");\n  };\n};\n\nrun();\n"
  },
  {
    "path": "packages/reason-harfbuzz/examples/harfbuzz-cli/dune",
    "content": "(executable\n (name HarfbuzzCli)\n (package ReveryExamples)\n (public_name HarfbuzzCli)\n (libraries bigarray harfbuzz reason-native-crash-utils.asan))\n\n(install\n (section bin)\n (package ReveryExamples)\n (files run-harfbuzz.sh))\n"
  },
  {
    "path": "packages/reason-harfbuzz/examples/harfbuzz-cli/run-harfbuzz.sh",
    "content": "set -e\n\nCWD=$(dirname $0)\nif [[ $1 == 'windows' ]]; then executable=\"HarfbuzzCli.exe\"; else executable=\"HarfbuzzCli\"; fi\n\nLSAN_OPTIONS=suppressions=lsan.supp $CWD/$executable\n"
  },
  {
    "path": "packages/reason-harfbuzz/src/Harfbuzz.re",
    "content": "type hb_shape = {\n  glyphId: int,\n  cluster: int,\n};\n\nmodule Internal = {\n  type face;\n  type feature = {\n    tag: string,\n    value: int,\n    start: int,\n    stop: int,\n  };\n  external hb_face_from_path: string => result(face, string) =\n    \"rehb_face_from_path\";\n  external hb_face_from_data: (string, int) => result(face, string) =\n    \"rehb_face_from_bytes\";\n  external hb_destroy_face: face => unit = \"rehb_destroy_face\";\n  external hb_shape:\n    (face, string, array(feature), int, int) => array(hb_shape) =\n    \"rehb_shape\";\n\n  // hb-version\n  external hb_version_string_compiled: unit => string =\n    \"rehb_version_string_compiled\";\n  external hb_version_string_runtime: unit => string =\n    \"rehb_version_string_runtime\";\n};\n\ntype position = [ | `Start | `End | `Position(int)];\ntype feature = {\n  tag: string,\n  value: int,\n  start: position,\n  stop: position,\n};\n\ntype hb_face = {face: Internal.face};\n\nlet positionToInt = position =>\n  switch (position) {\n  | `Position(n) => n\n  | `Start => 0\n  | `End => (-1)\n  };\n\nlet hb_face_from_path = str => {\n  switch (Internal.hb_face_from_path(str)) {\n  | Error(msg) => Error(msg)\n  | Ok(face) =>\n    let ret = {face: face};\n\n    let finalise = ({face}) => Internal.hb_destroy_face(face);\n\n    Gc.finalise(finalise, ret);\n    Ok(ret);\n  };\n};\n\nlet hb_shape = (~features=[], ~start=`Start, ~stop=`End, {face}, str) => {\n  let arr =\n    features\n    |> List.map(\n         feat => {\n           tag: feat.tag,\n           value: feat.value,\n           start: positionToInt(feat.start),\n           stop: positionToInt(feat.stop),\n         }: feature => Internal.feature,\n       )\n    |> Array.of_list;\n  let startPosition = positionToInt(start);\n  let length =\n    switch (stop) {\n    | `Position(n) => n - startPosition\n    | `Start => 0\n    | `End => (-1)\n    };\n\n  Internal.hb_shape(face, str, arr, startPosition, length);\n};\nlet hb_new_face = str => hb_face_from_path(str);\n\nlet hb_face_from_data = bytes => {\n  switch (Internal.hb_face_from_data(bytes, String.length(bytes))) {\n  | Error(_) as e => e\n  | Ok(face) =>\n    let ret = {face: face};\n\n    let finalise = ({face}) => Internal.hb_destroy_face(face);\n\n    Gc.finalise(finalise, ret);\n    Ok(ret);\n  };\n};\n\nlet hb_version_string_compiled = Internal.hb_version_string_compiled;\nlet hb_version_string_runtime = Internal.hb_version_string_runtime;\n"
  },
  {
    "path": "packages/reason-harfbuzz/src/Harfbuzz.rei",
    "content": "type hb_face;\n\ntype hb_shape = {\n  glyphId: int,\n  cluster: int,\n};\n\ntype position = [ | `Start | `End | `Position(int)];\ntype feature = {\n  tag: string,\n  value: int,\n  start: position,\n  stop: position,\n};\n\nlet hb_face_from_path: string => result(hb_face, string);\nlet hb_face_from_data: string => result(hb_face, string);\n\n[@ocaml.deprecated \"Deprecated in favor of hb_face_from_path\"]\nlet hb_new_face: string => result(hb_face, string);\n\nlet hb_shape:\n  (\n    ~features: list(feature)=?,\n    ~start: position=?,\n    ~stop: position=?,\n    hb_face,\n    string\n  ) =>\n  array(hb_shape);\n\nlet hb_version_string_compiled: unit => string;\nlet hb_version_string_runtime: unit => string;\n"
  },
  {
    "path": "packages/reason-harfbuzz/src/config/discover.re",
    "content": "module Configurator = Configurator.V1;\n\ntype os =\n  | Android\n  | IOS\n  | Linux\n  | Mac\n  | Windows;\n\nlet detect_system_header = {|\n  #if __APPLE__\n    #include <TargetConditionals.h>\n    #if TARGET_OS_IPHONE\n      #define PLATFORM_NAME \"ios\"\n    #else\n      #define PLATFORM_NAME \"mac\"\n    #endif\n  #elif __linux__\n    #if __ANDROID__\n      #define PLATFORM_NAME \"android\"\n    #else\n      #define PLATFORM_NAME \"linux\"\n    #endif\n  #elif WIN32\n    #define PLATFORM_NAME \"windows\"\n  #endif\n|};\n\nlet get_os = t => {\n  let header = {\n    let file = Filename.temp_file(\"discover\", \"os.h\");\n    let fd = open_out(file);\n    output_string(fd, detect_system_header);\n    close_out(fd);\n    file;\n  };\n  let platform =\n    Configurator.C_define.import(\n      t,\n      ~includes=[header],\n      [(\"PLATFORM_NAME\", String)],\n    );\n  switch (platform) {\n  | [(_, String(\"android\"))] => Android\n  | [(_, String(\"ios\"))] => IOS\n  | [(_, String(\"linux\"))] => Linux\n  | [(_, String(\"mac\"))] => Mac\n  | [(_, String(\"windows\"))] => Windows\n  | _ => failwith(\"Unknown operating system\")\n  };\n};\n\nlet c_flags = [\"-fPIC\", \"-I\", Sys.getenv(\"HARFBUZZ_INCLUDE_PATH\")];\n\nlet ccopt = s => [\"-ccopt\", s];\nlet cclib = s => [\"-cclib\", s];\n\nlet lib_path_flags = [] @ ccopt(\"-L\" ++ Sys.getenv(\"HARFBUZZ_LIB_PATH\"));\n\nlet extraFlags = os =>\n  switch (os) {\n  | Windows => cclib(\"-lpthread\")\n  | Linux => ccopt(\"-L/usr/lib\")\n  | _ => []\n  };\n\nlet flags = os => [] @ cclib(\"-lharfbuzz\") @ extraFlags(os) @ lib_path_flags;\n\nlet cxx_flags = os =>\n  switch (os) {\n  | Windows => c_flags @ [\"-fno-exceptions\", \"-fno-rtti\", \"-lstdc++\"]\n  | _ => c_flags\n  };\n\nConfigurator.main(~name=\"reason-harfbuzz\", conf => {\n  let os = get_os(conf);\n  Configurator.Flags.write_sexp(\"c_flags.sexp\", c_flags);\n  Configurator.Flags.write_sexp(\"cxx_flags.sexp\", cxx_flags(os));\n  Configurator.Flags.write_sexp(\"flags.sexp\", flags(os));\n});\n"
  },
  {
    "path": "packages/reason-harfbuzz/src/config/dune",
    "content": "(executable\n (name discover)\n (libraries dune-configurator))\n"
  },
  {
    "path": "packages/reason-harfbuzz/src/dune",
    "content": "(library\n (name harfbuzz)\n (public_name reason-harfbuzz)\n (foreign_stubs\n  (language c)\n  (flags\n   (:include c_flags.sexp)))\n (foreign_stubs\n  (language cxx)\n  (names harfbuzz)\n  (flags\n   (:include cxx_flags.sexp)))\n (library_flags\n  (:include flags.sexp)))\n\n(rule\n (targets c_flags.sexp cxx_flags.sexp flags.sexp)\n (deps\n  (:discover config/discover.exe))\n (action\n  (run %{discover})))\n"
  },
  {
    "path": "packages/reason-harfbuzz/src/harfbuzz.cpp",
    "content": "#include <stdio.h>\n#include <cstring>\n\n#include <caml/alloc.h>\n#include <caml/bigarray.h>\n#include <caml/callback.h>\n#include <caml/memory.h>\n#include <caml/mlvalues.h>\n\n#include <hb-ot.h>\n#include <hb.h>\n\n\n/* #define TEST_FONT \"E:/FiraCode-Regular.ttf\" */\n/* #define TEST_FONT \"E:/Hasklig-Medium.otf\" */\n/* #define TEST_FONT \"E:/Cassandra.ttf\" */\n/* #define TEST_FONT \"E:/Lato-Regular.ttf\" */\n\nextern \"C\" {\n\n// hb_font_t*\n\n    CAMLprim value Val_success(value v) {\n        CAMLparam1(v);\n        CAMLlocal1(some);\n        some = caml_alloc(1, 0);\n        Store_field(some, 0, v);\n        CAMLreturn(some);\n    }\n\n    CAMLprim value Val_error(const char *szMsg) {\n        CAMLparam0();\n        CAMLlocal1(error);\n        error = caml_alloc(1, 1);\n        Store_field(error, 0, caml_copy_string(szMsg));\n        CAMLreturn(error);\n    }\n\n    /* Use native open type implementation to load font\n      https://github.com/harfbuzz/harfbuzz/issues/255 */\n    hb_font_t *get_font_ot(char *data, int length, int size) {\n        hb_blob_t *blob =\n            hb_blob_create(data, length, HB_MEMORY_MODE_WRITABLE, (void *)data, free);\n        hb_face_t *face = hb_face_create(blob, 0);\n\n        hb_blob_destroy(blob); // face will keep a reference to blob\n\n        hb_font_t *font = hb_font_create(face);\n        hb_face_destroy(face); // font will keep a reference to face\n\n        hb_ot_font_set_funcs(font);\n        hb_font_set_scale(font, size, size);\n\n        return font;\n    }\n    CAMLprim value rehb_destroy_face(value vFont) {\n        CAMLparam1(vFont);\n\n        hb_font_t *pFont = (hb_font_t*)vFont;\n        if (pFont) {\n            hb_font_destroy(pFont);\n        }\n\n        CAMLreturn(Val_unit);\n    }\n\n    CAMLprim value rehb_face_from_path(value vString) {\n        CAMLparam1(vString);\n        CAMLlocal1(ret);\n\n        const char *szFont = String_val(vString);\n\n        FILE *file = fopen(szFont, \"rb\");\n\n        if (!file) {\n            CAMLreturn(Val_error(\"File does not exist\"));\n        }\n\n        fseek(file, 0, SEEK_END);\n        unsigned int length = ftell(file);\n        fseek(file, 0, SEEK_SET);\n\n        char *data = (char *)malloc(length);\n        fread(data, length, 1, file);\n        fclose(file);\n\n        hb_font_t *hb_font;\n        hb_font = get_font_ot(data, length, 12 /*iSize*/ * 64);\n\n        if (!hb_font) {\n            ret = Val_error(\"Unable to load font\");\n        } else {\n            ret = Val_success((value)hb_font);\n        }\n        CAMLreturn(ret);\n    }\n\n    CAMLprim value rehb_face_from_bytes(value vPtr, value vLength) {\n        CAMLparam2(vPtr, vLength);\n        CAMLlocal1(ret);\n\n        char *caml_data = Bp_val(vPtr);\n        int length = Int_val(vLength);\n\n        char *data = (char *)malloc(length * sizeof(char));\n        std::memcpy(data, caml_data, length);\n\n        hb_font_t *hb_font;\n        hb_font = get_font_ot(data, length, 12 /*iSize*/ * 64);\n\n        if (!hb_font) {\n            ret = Val_error(\"Unable to load font\");\n        } else {\n            ret = Val_success((value)hb_font);\n        }\n        CAMLreturn(ret);\n    }\n\n    static value createShapeTuple(unsigned int codepoint, unsigned int cluster) {\n        CAMLparam0();\n\n        CAMLlocal1(ret);\n        ret = caml_alloc(2, 0);\n        Store_field(ret, 0, Val_int(codepoint));\n        Store_field(ret, 1, Val_int(cluster));\n        CAMLreturn(ret);\n    }\n\n    CAMLprim value rehb_shape(value vFace, value vString, value vFeatures, value vStart, value vLen) {\n        CAMLparam5(vFace, vString, vFeatures, vStart, vLen);\n        CAMLlocal2(ret, feat);\n\n        int start = Int_val(vStart);\n        int len = Int_val(vLen);\n\n        int featuresLen = Wosize_val(vFeatures);\n        hb_feature_t *features = (hb_feature_t *)malloc(featuresLen * sizeof(hb_feature_t));\n        for (int i = 0; i < featuresLen; i++) {\n            feat = Field(vFeatures, i);\n            const char *tag = String_val(Field(feat, 0));\n            features[i].tag = HB_TAG(tag[0], tag[1], tag[2], tag[3]);\n            features[i].value = Int_val(Field(feat, 1));\n            features[i].start = Int_val(Field(feat, 2));\n            features[i].end = Int_val(Field(feat, 3));\n        }\n\n        hb_font_t *hb_font = (hb_font_t *)vFace;\n\n\n        hb_buffer_t *hb_buffer;\n        hb_buffer = hb_buffer_create();\n        hb_buffer_add_utf8(hb_buffer, String_val(vString), -1, start, len);\n        hb_buffer_guess_segment_properties(hb_buffer);\n\n        hb_shape(hb_font, hb_buffer, features, featuresLen);\n\n        unsigned int glyph_count;\n        hb_glyph_info_t *info = hb_buffer_get_glyph_infos(hb_buffer, &glyph_count);\n\n        ret = caml_alloc(glyph_count, 0);\n\n        for (int i = 0; i < glyph_count; i++) {\n            Store_field(ret, i, createShapeTuple(info[i].codepoint, info[i].cluster));\n        }\n        free(features);\n        hb_buffer_destroy(hb_buffer);\n        CAMLreturn(ret);\n    }\n\n    CAMLprim value rehb_version_string_compiled() {\n        CAMLparam0();\n        CAMLlocal1(ret);\n\n        ret = caml_copy_string(HB_VERSION_STRING);\n\n        CAMLreturn(ret);\n    }\n\n    CAMLprim value rehb_version_string_runtime() {\n        CAMLparam0();\n        CAMLlocal1(ret);\n\n        ret = caml_copy_string(hb_version_string());\n\n        CAMLreturn(ret);\n    }\n}\n"
  },
  {
    "path": "packages/reason-harfbuzz/test/FeaturesTest.re",
    "content": "open Harfbuzz;\nopen TestFramework;\n\ndescribe(\"Features\", ({test, _}) => {\n  test(\"default (ff)\", ({expect, _}) => {\n    let expectedResult = [|\n      {glyphId: 74, cluster: 0},\n      {glyphId: 74, cluster: 1},\n    |];\n    let shapes = hb_shape(font, \"ff\");\n\n    expect.equal(expectedResult, shapes);\n  });\n\n  test(\"default (fi)\", ({expect, _}) => {\n    let expectedResult = [|{glyphId: 444, cluster: 0}|];\n    let shapes = hb_shape(font, \"fi\");\n\n    expect.equal(expectedResult, shapes);\n  });\n\n  test(\"discretionary ligatures enabled (ff)\", ({expect, _}) => {\n    let expectedResult = [|{glyphId: 443, cluster: 0}|];\n    let features = [{tag: \"dlig\", value: 1, start: `Start, stop: `End}];\n    let shapes = hb_shape(~features, font, \"ff\");\n\n    expect.equal(shapes, expectedResult);\n  });\n\n  test(\"standard ligatures disabled (fi)\", ({expect, _}) => {\n    let expectedResult = [|\n      {glyphId: 74, cluster: 0},\n      {glyphId: 77, cluster: 1},\n    |];\n    let features = [{tag: \"liga\", value: 0, start: `Start, stop: `End}];\n    let shapes = hb_shape(~features, font, \"fi\");\n\n    expect.equal(shapes, expectedResult);\n  });\n});\n"
  },
  {
    "path": "packages/reason-harfbuzz/test/ShapingTest.re",
    "content": "open Harfbuzz;\nopen TestFramework;\n\ndescribe(\"Shaping\", ({test, _}) => {\n  test(\"whole string\", ({expect, _}) => {\n    let expectedResult = [|\n      {glyphId: 69, cluster: 0},\n      {glyphId: 70, cluster: 1},\n      {glyphId: 71, cluster: 2},\n    |];\n\n    let shapes = hb_shape(font, \"abc\");\n\n    expect.equal(expectedResult, shapes);\n  });\n\n  test(\"substring\", ({expect, _}) => {\n    let expectedResult = [|{glyphId: 70, cluster: 1}|];\n    let shapes =\n      hb_shape(font, \"abc\", ~start=`Position(1), ~stop=`Position(2));\n\n    expect.equal(expectedResult, shapes);\n  });\n\n  test(\"substring UTF-8\", ({expect, _}) => {\n    let expectedResult = [|{glyphId: 1007, cluster: 1}|];\n    let shapes =\n      hb_shape(font, \"aҙc\", ~start=`Position(1), ~stop=`Position(3));\n\n    expect.equal(expectedResult, shapes);\n  });\n});\n"
  },
  {
    "path": "packages/reason-harfbuzz/test/TestFramework.re",
    "content": "include Rely.Make({\n  let config =\n    Rely.TestFrameworkConfig.initialize({\n      snapshotDir: \"./__snapshots__\",\n      projectDir: \"\",\n    });\n});\n\nlet font =\n  Harfbuzz.hb_face_from_path(\"./examples/Roboto-Regular.ttf\") |> Result.get_ok;\n"
  },
  {
    "path": "packages/reason-harfbuzz/test/dune",
    "content": "(library\n (name Harfbuzz_Test)\n (ocamlopt_flags -linkall -g)\n (libraries rely.lib rely.internal reason-harfbuzz))\n"
  },
  {
    "path": "packages/reason-sdl2/src/Float32Array.re",
    "content": "type t = Bigarray.Array1.t(float, Bigarray.float32_elt, Bigarray.c_layout);\n\nlet of_array = array =>\n  Bigarray.Array1.of_array(Bigarray.Float32, Bigarray.C_layout, array);\n"
  },
  {
    "path": "packages/reason-sdl2/src/Uint16Array.re",
    "content": "type t =\n  Bigarray.Array1.t(int, Bigarray.int16_unsigned_elt, Bigarray.c_layout);\n\nlet of_array = array =>\n  Bigarray.Array1.of_array(Bigarray.Int16_unsigned, Bigarray.C_layout, array);\n"
  },
  {
    "path": "packages/reason-sdl2/src/config/discover.re",
    "content": "module Configurator = Configurator.V1;\n\ntype os =\n  | Android\n  | IOS\n  | Linux\n  | Mac\n  | Windows;\n\nlet detect_system_header = {|\n  #if __APPLE__\n    #include <TargetConditionals.h>\n    #if TARGET_OS_IPHONE\n      #define PLATFORM_NAME \"ios\"\n    #else\n      #define PLATFORM_NAME \"mac\"\n    #endif\n  #elif __linux__\n    #if __ANDROID__\n      #define PLATFORM_NAME \"android\"\n    #else\n      #define PLATFORM_NAME \"linux\"\n    #endif\n  #elif WIN32\n    #define PLATFORM_NAME \"windows\"\n  #endif\n|};\nlet get_os = t => {\n  let header = {\n    let file = Filename.temp_file(\"discover\", \"os.h\");\n    let fd = open_out(file);\n    output_string(fd, detect_system_header);\n    close_out(fd);\n    file;\n  };\n  let platform =\n    Configurator.C_define.import(\n      t,\n      ~includes=[header],\n      [(\"PLATFORM_NAME\", String)],\n    );\n  switch (platform) {\n  | [(_, String(\"android\"))] => Android\n  | [(_, String(\"ios\"))] => IOS\n  | [(_, String(\"linux\"))] => Linux\n  | [(_, String(\"mac\"))] => Mac\n  | [(_, String(\"windows\"))] => Windows\n  | _ => failwith(\"Unknown operating system\")\n  };\n};\n\nlet root = Sys.getenv(\"cur__root\");\nlet c_flags = [\n  \"-I\",\n  Sys.getenv(\"SDL2_INCLUDE_PATH\"),\n  \"-I\",\n  Filename.concat(root, \"include\"),\n  \"-I\",\n  Filename.concat(root, \"src\"),\n];\n\nlet c_flags = os =>\n  switch (os) {\n  | Android\n  | IOS\n  | Mac => c_flags\n  | Linux => c_flags @ [\"-fPIC\"]\n  | Windows => c_flags @ [\"-mwindows\"]\n  };\n\nlet libFolderPath = Sys.getenv(\"SDL2_LIB_PATH\");\nlet libFilePath = libFolderPath ++ \"/libSDL2.a\";\nprerr_endline(\"SDL2 Library Folder Path: \" ++ libFolderPath);\n\nlet ccopt = s => [\"-ccopt\", s];\nlet cclib = s => [\"-cclib\", s];\n\nlet flags = os =>\n  switch (os) {\n  | Android =>\n    []\n    @ ccopt(libFilePath)\n    @ cclib(\"-lEGL\")\n    @ cclib(\"-lGLESv1_CM\")\n    @ cclib(\"-lGLESv2\")\n  | IOS =>\n    []\n    @ ccopt(libFilePath)\n    @ ccopt(\"-framework Foundation\")\n    @ ccopt(\"-framework OpenGLES\")\n    @ ccopt(\"-framework UIKit\")\n    @ ccopt(\"-framework IOKit\")\n    @ ccopt(\"-framework CoreVideo\")\n    @ ccopt(\"-framework CoreAudio\")\n    @ ccopt(\"-framework AudioToolbox\")\n    @ ccopt(\"-framework Metal\")\n    @ ccopt(\"-liconv\")\n  | Linux =>\n    []\n    @ cclib(\"-lGL\")\n    @ cclib(\"-lGLU\")\n    @ ccopt(libFilePath)\n    @ cclib(\"-lX11\")\n    @ cclib(\"-lXxf86vm\")\n    @ cclib(\"-lXrandr\")\n    @ cclib(\"-lXinerama\")\n    @ cclib(\"-lXcursor\")\n    @ cclib(\"-lpthread\")\n    @ cclib(\"-lXi\")\n  | Mac =>\n    []\n    @ ccopt(libFilePath)\n    @ ccopt(\"-framework AppKit\")\n    @ ccopt(\"-framework Foundation\")\n    @ ccopt(\"-framework OpenGL\")\n    @ ccopt(\"-framework Cocoa\")\n    @ ccopt(\"-framework IOKit\")\n    @ ccopt(\"-framework CoreVideo\")\n    @ ccopt(\"-framework CoreAudio\")\n    @ ccopt(\"-framework AudioToolbox\")\n    @ ccopt(\"-framework ForceFeedback\")\n    @ ccopt(\"-framework Metal\")\n    @ ccopt(\"-framework Carbon\")\n    @ ccopt(\"-liconv\")\n  | Windows =>\n    let maybeAngleLibPath = Sys.getenv_opt(\"ANGLE_LIB_PATH\");\n    let angleLibPath =\n      switch (maybeAngleLibPath) {\n      | None =>\n        prerr_endline(\"Unable to get libraries for esy-angle-prebuilt\");\n        failwith(\"Unable to get libraries for esy-angle-prebuilt)\");\n      | Some(path) =>\n        print_endline(\"ANGLE Library Path: \" ++ path);\n        path;\n      };\n    let eglPath = angleLibPath ++ \"/libEGL.a\";\n    let glesv2Path = angleLibPath ++ \"/libGLESv2.a\";\n    []\n    // On Windows, we ship the DLL side-by-side\n    @ ccopt(\"-L\" ++ libFolderPath)\n    @ cclib(\"-lSDL2\")\n    @ ccopt(eglPath)\n    @ ccopt(glesv2Path)\n    // We use the ANGLE DLLs (to use Direct3D apis instead of OpenGL)\n    @ cclib(\"-lgdi32\")\n    @ cclib(\"-subsystem windows\");\n  };\n\nlet c_library_flags = os =>\n  switch (os) {\n  | Android\n  | IOS\n  | Mac\n  | Linux => [libFilePath]\n  | Windows => [\"-L\" ++ libFolderPath, \"-lSDL2\"]\n  };\n\nlet cxx_flags = os =>\n  switch (os) {\n  | Android\n  | Linux => c_flags(os) @ [\"-std=c++11\"]\n  | IOS\n  | Mac => c_flags(os) @ [\"-x\", \"objective-c++\"]\n  | Windows =>\n    c_flags(os) @ [\"-fno-exceptions\", \"-fno-rtti\", \"-lstdc++\", \"-mwindows\"]\n  };\n\nConfigurator.main(~name=\"reason-sdl2\", conf => {\n  let os = get_os(conf);\n  Configurator.Flags.write_sexp(\"c_library_flags.sexp\", c_library_flags(os));\n  Configurator.Flags.write_sexp(\"c_flags.sexp\", c_flags(os));\n  Configurator.Flags.write_sexp(\"cxx_flags.sexp\", cxx_flags(os));\n  Configurator.Flags.write_sexp(\"flags.sexp\", flags(os));\n});\n"
  },
  {
    "path": "packages/reason-sdl2/src/config/dune",
    "content": "(executable\n (name discover)\n (libraries dune.configurator))\n"
  },
  {
    "path": "packages/reason-sdl2/src/dune",
    "content": "(library\n (name sdl2)\n (public_name reason-sdl2)\n (library_flags\n  (:include flags.sexp))\n (c_library_flags\n  (:include c_library_flags.sexp))\n (foreign_stubs\n  (language c)\n  (flags\n   (:include c_flags.sexp)))\n (foreign_stubs\n  (language cxx)\n  (names sdl2_wrapper stb_image)\n  (flags\n   (:include cxx_flags.sexp))))\n\n(rule\n (targets c_flags.sexp cxx_flags.sexp flags.sexp c_library_flags.sexp)\n (deps\n  (:discover config/discover.exe))\n (action\n  (run %{discover})))\n"
  },
  {
    "path": "packages/reason-sdl2/src/sdl2.re",
    "content": "module Float32Array = Float32Array;\nmodule Uint16Array = Uint16Array;\n\nmodule Size = {\n  type t = {\n    width: int,\n    height: int,\n  };\n};\n\nmodule Rect = {\n  type t = {\n    x: int,\n    y: int,\n    width: int,\n    height: int,\n  };\n\n  let toString = ({x, y, width, height}) =>\n    Printf.sprintf(\"x: %d y: %d width: %d height: %d\", x, y, width, height);\n};\n\nmodule ScreenSaver = {\n  external enable: unit => unit = \"resdl_SDL_EnableScreenSaver\";\n  external disable: unit => unit = \"resdl_SDL_DisableScreenSaver\";\n  external isEnabled: unit => bool = \"resdl_SDL_IsScreenSaverEnabled\";\n};\n\nmodule Surface = {\n  type t;\n  external createFromImagePath: string => result(t, string) =\n    \"resdl_SDL_CreateRGBSurfaceFromImage\";\n};\n\nmodule Audio = {\n  module Format = {\n    type t =\n      | S8\n      | U8\n      | S16LSB\n      | S16MSB\n      | U16LSB\n      | U16MSB\n      | S32LSB\n      | S32MSB\n      | F32LSB\n      | F32MSB;\n  };\n  module Buffer = {\n    type t;\n  };\n  module Spec = {\n    type t = {\n      freq: int,\n      format: Format.t,\n      channels: int,\n      silence: int,\n      samples: int,\n      padding: int,\n      size: int,\n    };\n  };\n  module Device = {\n    module Status = {\n      type t =\n        | Stopped\n        | Playing\n        | Paused;\n    };\n    module AllowedChanges: {\n      type t;\n      let frequency: t;\n      let format: t;\n      let channels: t;\n      let samples: t;\n      let any: t;\n      let none: t;\n      let (|||): (t, t) => t;\n    } = {\n      type t = int;\n      let frequency = 0x00000001;\n      let format = 0x00000002;\n      let channels = 0x00000004;\n      let samples = 0x00000008;\n      let (|||) = (lor);\n      let any = frequency lor format lor channels lor samples;\n      let none = 0;\n    };\n    type t;\n    external open_:\n      (option(string), bool, Spec.t, AllowedChanges.t) =>\n      result((t, Spec.t), string) =\n      \"resdl_SDL_OpenAudioDevice\";\n    external close: t => unit = \"resdl_SDL_CloseAudioDevice\";\n    external getStatus: t => Status.t = \"resdl_SDL_GetAudioDeviceStatus\";\n    external pause: (t, bool) => unit = \"resdl_SDL_PauseAudioDevice\";\n  };\n\n  module Wav = {\n    external load: string => result((Spec.t, Buffer.t, int), string) =\n      \"resdl_SDL_LoadWAV\";\n  };\n\n  external queue: (Device.t, Buffer.t, int) => result(unit, string) =\n    \"resdl_SDL_QueueAudio\";\n  external clearQueued: Device.t => unit = \"resdl_SDL_ClearQueuedAudio\";\n  external getQueuedSize: Device.t => int = \"resdl_SDL_GetQueuedAudioSize\";\n};\n\nmodule Clipboard = {\n  external getText: unit => option(string) = \"resdl_SDL_GetClipboardText\";\n  external setText: string => unit = \"resdl_SDL_SetClipboardText\";\n  external hasText: unit => bool = \"resdl_SDL_HasClipboardText\";\n};\n\nmodule PixelFormat = {\n  type t;\n\n  external toString: t => string = \"resdl_SDL_GetPixelFormatName\";\n};\n\nmodule Display = {\n  type t;\n\n  module Dpi = {\n    type t = {\n      ddpi: float,\n      hdpi: float,\n      vdpi: float,\n    };\n\n    let show = (v: t) => {\n      Printf.sprintf(\"ddpi: %f hdpi: %f vdpi: %f\", v.ddpi, v.hdpi, v.vdpi);\n    };\n  };\n\n  module Mode = {\n    type t = {\n      pixelFormat: PixelFormat.t,\n      width: int,\n      height: int,\n      refreshRate: int,\n    };\n\n    let show = (v: t) => {\n      Printf.sprintf(\n        \"pixelFormat: %s width: %d height: %d refreshRate: %d\",\n        v.pixelFormat |> PixelFormat.toString,\n        v.width,\n        v.height,\n        v.refreshRate,\n      );\n    };\n  };\n\n  external getNumberOfDisplays: unit => int = \"resdl_SDL_GetNumVideoDisplays\";\n  let getDisplays = () =>\n    List.init(getNumberOfDisplays(), (i) => (Obj.magic(i: int): t));\n\n  external getDPI: t => Dpi.t = \"resdl_SDL_GetDisplayDPI\";\n  external getCurrentMode: t => Mode.t = \"resdl_SDL_GetCurrentDisplayMode\";\n  external getDesktopMode: t => Mode.t = \"resdl_SDL_GetDesktopDisplayMode\";\n  external getName: t => string = \"resdl_SDL_GetDisplayName\";\n  external getBounds: t => Rect.t = \"resdl_SDL_GetDisplayBounds\";\n  external getUsableBounds: t => Rect.t = \"resdl_SDL_GetDisplayUsableBounds\";\n};\n\nmodule Log = {\n  type category =\n    | Application\n    | Error\n    | Assert\n    | System\n    | Audio\n    | Video\n    | Render\n    | Input\n    | Test\n    | Custom\n    | Unknown;\n\n  type priority =\n    | Verbose\n    | Debug\n    | Info\n    | Warn\n    | Error\n    | Critical;\n\n  type logOutputFunction = (category, priority, string) => unit;\n\n  let _outputFunction: ref(logOutputFunction) = ref((_, _, _) => ());\n  let setOutputFunction = outputFunction => {\n    _outputFunction := outputFunction;\n  };\n\n  let _onLog = (category, priority, str) => {\n    _outputFunction^(category, priority, str);\n  };\n\n  Callback.register(\"reason_sdl2_onLog\", _onLog);\n};\n\nmodule Platform = {\n  external getName: unit => string = \"resdl_SDL_GetPlatform\";\n\n  external getVersion: unit => string = \"resdl_SDL_GetVersion\";\n\n  // Windows only\n  external win32AllocConsole: unit => int = \"resdl_SDL_WinAllocConsole\";\n  external win32AttachConsole: unit => int = \"resdl_SDL_WinAttachConsole\";\n};\n\nmodule Window = {\n  type t;\n\n  type hitTestResult =\n    | Normal\n    | Draggable\n    | ResizeTopLeft\n    | ResizeTop\n    | ResizeTopRight\n    | ResizeRight\n    | ResizeBottomRight\n    | ResizeBottom\n    | ResizeBottomLeft\n    | ResizeLeft;\n\n  type hitTestCallback = (t, int, int) => hitTestResult;\n\n  external create:\n    (\n      string,\n      [ | `Undefined | `Centered | `Absolute(int)],\n      [ | `Undefined | `Centered | `Absolute(int)],\n      int,\n      int,\n      [ | `Auto | `ForceHardware | `ForceSoftware]\n    ) =>\n    t =\n    \"resdl_SDL_CreateWindow_byte\" \"resdl_SDL_CreateWindow\";\n  external getId: t => int = \"resdl_SDL_GetWindowId\";\n  external getSize: t => Size.t = \"resdl_SDL_GetWindowSize\";\n  external getPosition: t => (int, int) = \"resdl_SDL_GetWindowPosition\";\n  external setBordered: (t, bool) => unit = \"resdl_SDL_SetWindowBordered\";\n  external getPixelFormat: t => PixelFormat.t =\n    \"resdl_SDL_GetWindowPixelFormat\";\n  external setIcon: (t, Surface.t) => unit = \"resdl_SDL_SetWindowIcon\";\n  external setTransparency: (t, float) => unit =\n    \"resdl_SDL_SetWindowTransparency\";\n  external setPosition: (t, int, int) => unit = \"resdl_SDL_SetWindowPosition\";\n  external center: t => unit = \"resdl_SDL_WindowCenter\";\n  external setResizable: (t, bool) => unit = \"resdl_SDL_SetWindowResizable\";\n  external setSize: (t, int, int) => unit = \"resdl_SDL_SetWindowSize\";\n  external setTitle: (t, string) => unit = \"resdl_SDL_SetWindowTitle\";\n  external setMinimumSize: (t, int, int) => unit =\n    \"resdl_SDL_SetWindowMinimumSize\";\n\n  external _enableHitTest: t => unit = \"resdl_SDL_EnableHitTest\";\n  external _disableHitTest: t => unit = \"resdl_SDL_EnableHitTest\";\n\n  let _idToHitTest: Hashtbl.t(int, hitTestCallback) = Hashtbl.create(16);\n\n  let setHitTest = (win: t, cb: option(hitTestCallback)) => {\n    switch (cb) {\n    | None => _disableHitTest(win)\n    | Some(v) =>\n      _enableHitTest(win);\n      Hashtbl.add(_idToHitTest, getId(win), v);\n    };\n  };\n\n  let _hitTest = (win: t, x: int, y: int) => {\n    let id = getId(win);\n    switch (Hashtbl.find_opt(_idToHitTest, id)) {\n    | Some(v) => v(win, x, y)\n    | None => Normal\n    };\n  };\n\n  Callback.register(\"__sdl2_caml_hittest__\", _hitTest);\n\n  external hide: t => unit = \"resdl_SDL_HideWindow\";\n  external raise: t => unit = \"resdl_SDL_RaiseWindow\";\n  external show: t => unit = \"resdl_SDL_ShowWindow\";\n\n  external minimize: t => unit = \"resdl_SDL_MinimizeWindow\";\n  external restore: t => unit = \"resdl_SDL_RestoreWindow\";\n  external maximize: t => unit = \"resdl_SDL_MaximizeWindow\";\n\n  external isMaximized: t => bool = \"resdl_SDL_IsWindowMaximized\";\n  external isFullscreen: t => bool = \"resdl_SDL_IsWindowFullscreen\";\n\n  external getDisplay: t => Display.t = \"resdl_SDL_GetWindowDisplayIndex\";\n\n  // Windows-Only: Set DPI Aware process flag\n  // Other platforms: no-op\n  external setWin32ProcessDPIAware: t => unit =\n    \"resdl_SDL_SetWin32ProcessDPIAware\";\n\n  // WINDOWS-ONLY: Get the monitor scale factor for the window\n  // Other platforms: Always returns 1.0\n  external getWin32ScaleFactor: t => float = \"resdl_SDL_GetWin32ScaleFactor\";\n\n  type nativeWindow;\n  external getNativeWindow: t => nativeWindow = \"resdl_SDL_GetNativeWindow\";\n\n  // MacOS-Only\n  external setMacTitlebarTransparent: t => unit =\n    \"resdl_SDL_SetMacTitlebarTransparent\";\n  external setMacTitlebarHidden: t => unit = \"resdl_SDL_SetMacTitlebarHidden\";\n  external setMacBackgroundColor: (t, float, float, float, float) => unit =\n    \"resdl_SDL_SetMacBackgroundColor\";\n  external getMacTitlebarHeight: t => float = \"resdl_SDL_GetMacTitlebarHeight\";\n};\n\nmodule Gl = {\n  type context;\n\n  type glString =\n    | Vendor\n    | Renderer\n    | Version\n    | ShadingLanguageVersion;\n  type glInt =\n    | FramebufferBinding;\n\n  external setup: Window.t => context = \"resdl_SDL_GL_Setup\";\n  external makeCurrent: (Window.t, context) => unit =\n    \"resdl_SDL_GL_MakeCurrent\";\n  external swapWindow: Window.t => unit = \"resdl_SDL_GL_SwapWindow\";\n  external getDrawableSize: Window.t => Size.t =\n    \"resdl_SDL_GL_GetDrawableSize\";\n  external setSwapInterval: int => unit = \"resdl_SDL_GL_SetSwapInterval\";\n\n  external getString: glString => string = \"resdl_SDL_GL_GetString\";\n  external getFramebufferBinding: unit => int =\n    \"resdl_SDL_GL_GetFramebufferBinding\";\n};\n\nexternal delay: int => unit = \"resdl_SDL_Delay\";\nexternal init: unit => int = \"resdl_SDL_Init\";\nexternal main: (int, array(string), unit => unit) => unit = \"resdl_SDL_main\";\nlet main = cb => main(Array.length(Sys.argv), Sys.argv, cb);\n\nmodule TextInput = {\n  [@noalloc] external start: unit => unit = \"resdl_SDL_StartTextInput\";\n  [@noalloc] external stop: unit => unit = \"resdl_SDL_StopTextInput\";\n  [@noalloc]\n  external setInputRect: (int, int, int, int) => unit =\n    \"resdl_SDL_SetTextInputRect\";\n  [@noalloc] external isActive: unit => bool = \"resdl_SDL_IsTextInputActive\";\n};\n\nmodule MouseButton = {\n  type t =\n    | Left\n    | Middle\n    | Right\n    | X1\n    | X2;\n\n  let show = v =>\n    switch (v) {\n    | Left => \"Left\"\n    | Middle => \"Middle\"\n    | Right => \"Right\"\n    | X1 => \"X1\"\n    | X2 => \"X2\"\n    };\n};\n\nmodule Mouse = {\n  external capture: bool => int = \"resdl_SDL_CaptureMouse\";\n  external getGlobalPosition: unit => (int, int) =\n    \"resdl_SDL_GetGlobalMouseState\";\n};\n\nmodule Scancode = {\n  type t = int;\n\n  external getName: t => string = \"resdl_SDL_GetScancodeName\";\n\n  external ofName: string => t = \"resdl_SDL_GetScancodeFromName\";\n\n  [@noalloc] external ofInt: int => t = \"resdl_PassThrough\";\n  [@noalloc] external toInt: t => int = \"resdl_PassThrough\";\n\n  // Incrementally add these as needed from:\n  // https://wiki.libsdl.org/SDLScancodeLookup\n  let unknown = 0;\n};\n\nmodule Keycode = {\n  type t = int;\n  // as some keycodes are bigger than 2^30 but still less than 2^31\n  // so by using the negative numbers we can fit them in a normal int\n  let int = Int32.to_int;\n\n  external getName: t => string = \"resdl_SDL_GetKeyName\";\n\n  external ofName: string => t = \"resdl_SDL_GetKeyFromName\";\n\n  [@noalloc]\n  external ofScancode: Scancode.t => t = \"resdl_SDL_GetKeyFromScancode\";\n  [@noalloc]\n  external toScancode: t => Scancode.t = \"resdl_SDL_GetScancodeFromKey\";\n\n  // Incrementally add these as needed from:\n  // https://wiki.libsdl.org/SDLKeycodeLookup\n  let unknown = int(0l);\n  let backspace = int(8l);\n  let return = int(13l);\n\n  let escape = int(27l);\n\n  let space = int(32l);\n\n  let left_paren = int(40l);\n  let right_paren = int(41l);\n\n  let asterisk = int(42l);\n  let plus = int(43l);\n  let minus = int(45l);\n  let period = int(46l);\n  let slash = int(47l);\n  let caret = int(94l);\n\n  let equals = int(61l);\n\n  let digit0 = int(48l);\n  let digit1 = int(49l);\n  let digit2 = int(50l);\n  let digit3 = int(51l);\n  let digit4 = int(52l);\n  let digit5 = int(53l);\n  let digit6 = int(54l);\n  let digit7 = int(55l);\n  let digit8 = int(56l);\n  let digit9 = int(57l);\n\n  let pad_divide = int(1073741908l);\n  let pad_multiply = int(1073741909l);\n  let pad_minus = int(1073741910l);\n  let pad_plus = int(1073741911l);\n  let pad_period = int(1073741923l);\n\n  let pad_equals = int(1073741927l);\n\n  let p_digit1 = int(1073741913l);\n  let p_digit2 = int(1073741914l);\n  let p_digit3 = int(1073741915l);\n  let p_digit4 = int(1073741916l);\n  let p_digit5 = int(1073741917l);\n  let p_digit6 = int(1073741918l);\n  let p_digit7 = int(1073741919l);\n  let p_digit8 = int(1073741920l);\n  let p_digit9 = int(1073741921l);\n  let p_digit0 = int(1073741922l);\n\n  let a = int(97l);\n  let b = int(98l);\n  let c = int(99l);\n  let d = int(100l);\n  let e = int(101l);\n  let f = int(102l);\n  let g = int(103l);\n  let h = int(104l);\n  let i = int(105l);\n  let j = int(106l);\n  let k = int(107l);\n  let l = int(108l);\n  let m = int(109l);\n  let n = int(110l);\n  let o = int(111l);\n  let p = int(112l);\n  let q = int(113l);\n  let r = int(114l);\n  let s = int(115l);\n  let t = int(116l);\n  let u = int(117l);\n  let v = int(118l);\n  let w = int(119l);\n  let x = int(120l);\n  let y = int(121l);\n  let z = int(122l);\n\n  let delete = int(127l);\n\n  let right = int(1073741903l);\n  let left = int(1073741904l);\n};\n\nmodule WheelType = {\n  type t =\n    | Last\n    | Undefined\n    | Touchscreen\n    | Touchpad\n    | Wheel\n    | WheelPrecise\n    | OtherNonKinetic\n    | OtherKinetic;\n};\n\nmodule Keymod = {\n  type t = int;\n\n  let none = 0;\n\n  [@noalloc] external isLeftShiftDown: t => bool = \"resdl_SDL_ModLeftShift\";\n  [@noalloc] external isRightShiftDown: t => bool = \"resdl_SDL_ModRightShift\";\n\n  let isShiftDown = v => isLeftShiftDown(v) || isRightShiftDown(v);\n\n  [@noalloc]\n  external isLeftControlDown: t => bool = \"resdl_SDL_ModLeftControl\";\n  [@noalloc]\n  external isRightControlDown: t => bool = \"resdl_SDL_ModRightControl\";\n\n  let isControlDown = v => isLeftControlDown(v) || isRightControlDown(v);\n\n  [@noalloc] external isLeftAltDown: t => bool = \"resdl_SDL_ModLeftAlt\";\n  [@noalloc] external isRightAltDown: t => bool = \"resdl_SDL_ModRightAlt\";\n\n  let isAltDown = v => isLeftAltDown(v) || isRightAltDown(v);\n\n  [@noalloc] external isLeftGuiDown: t => bool = \"resdl_SDL_ModLeftGui\";\n  [@noalloc] external isRightGuiDown: t => bool = \"resdl_SDL_ModRightGui\";\n\n  let isGuiDown = v => isLeftGuiDown(v) || isRightGuiDown(v);\n\n  [@noalloc] external isNumLockDown: t => bool = \"resdl_SDL_ModNumLockDown\";\n  [@noalloc] external isCapsLockDown: t => bool = \"resdl_SDL_ModCapsLockDown\";\n\n  [@noalloc] external isAltGrKeyDown: t => bool = \"resdl_SDL_ModAltGrDown\";\n\n  let show = (v: t) => {\n    let int_of_bool = b => b ? 1 : 0;\n\n    Printf.sprintf(\n      \"Keymods - LSHIFT: %d RSHIFT: %d LCTRL: %d RCTRL: %d LALT: %d RALT: %d LGUI: %d RGUI: %d NUM: %d CAPS: %d ALTGR: %d\",\n      int_of_bool(isLeftShiftDown(v)),\n      int_of_bool(isRightShiftDown(v)),\n      int_of_bool(isLeftControlDown(v)),\n      int_of_bool(isRightControlDown(v)),\n      int_of_bool(isLeftAltDown(v)),\n      int_of_bool(isRightAltDown(v)),\n      int_of_bool(isLeftGuiDown(v)),\n      int_of_bool(isRightGuiDown(v)),\n      int_of_bool(isNumLockDown(v)),\n      int_of_bool(isCapsLockDown(v)),\n      int_of_bool(isAltGrKeyDown(v)),\n    );\n  };\n\n  [@noalloc] external getState: unit => t = \"resdl_SDL_GetModState\";\n  [@noalloc] external setState: t => unit = \"resdl_SDL_SetModState\";\n};\n\nmodule Event = {\n  type mouseMotion = {\n    windowID: int,\n    x: int,\n    y: int,\n  };\n\n  type mouseWheel = {\n    windowID: int,\n    deltaX: int,\n    deltaY: int,\n    isFlipped: bool,\n  };\n\n  type mousePan = {\n    windowID: int,\n    deltaX: int,\n    deltaY: int,\n    containsX: bool,\n    containsY: bool,\n    isFling: bool,\n    isInterrupt: bool,\n    source: WheelType.t,\n    timestamp: int,\n  };\n\n  type mouseButtonEvent = {\n    windowID: int,\n    button: MouseButton.t,\n    clicks: int,\n    x: int,\n    y: int,\n  };\n\n  type keyboardEvent = {\n    windowID: int,\n    repeat: bool,\n    keymod: Keymod.t,\n    scancode: Scancode.t,\n    keycode: Keycode.t,\n  };\n\n  type textInputEvent = {\n    windowID: int,\n    text: string,\n  };\n\n  type textEditingEvent = {\n    windowID: int,\n    text: string,\n    start: int,\n    length: int,\n  };\n\n  type windowEvent = {windowID: int};\n\n  type windowMoveEvent = {\n    windowID: int,\n    x: int,\n    y: int,\n  };\n\n  type windowSizeEvent = {\n    windowID: int,\n    width: int,\n    height: int,\n  };\n\n  /* Drop events\n      dropNotificationEvent occurs when SDL is notifying when a drop\n      is about to/has taken place through DROPBEGIN & DROPCOMPLETE\n\n      dropEvent occurs when a file is actually being dropped\n\n      The main difference is that dropEvent includes a file/text path, whereas\n      dropNotificationEvent does not.\n     */\n  type dropNotificationEvent = {\n    windowID: int,\n    timestamp: int,\n    x: int,\n    y: int,\n  };\n  type dropEvent = {\n    windowID: int,\n    file: string,\n    timestamp: int,\n    x: int,\n    y: int,\n  };\n\n  type t =\n    | Quit\n    | MouseMotion(mouseMotion) // 0\n    | MouseWheel(mouseWheel) // 1\n    | MouseButtonDown(mouseButtonEvent) // 2\n    | MouseButtonUp(mouseButtonEvent) // 3\n    | KeyDown(keyboardEvent) // 4\n    | KeyUp(keyboardEvent) // 5\n    | TextInput(textInputEvent) // 6\n    | TextEditing(textEditingEvent) // 7\n    | WindowShown(windowEvent) // 8\n    | WindowHidden(windowEvent) // 9\n    | WindowExposed(windowEvent) // 10\n    | WindowMoved(windowMoveEvent) // 11\n    | WindowResized(windowSizeEvent) // 12\n    | WindowSizeChanged(windowSizeEvent) // 13\n    | WindowMinimized(windowEvent) // 14\n    | WindowMaximized(windowEvent) // 15\n    | WindowRestored(windowEvent) // 16\n    | WindowEnter(windowEvent) // 17\n    | WindowLeave(windowEvent) // 18\n    | WindowFocusGained(windowEvent) // 19\n    | WindowFocusLost(windowEvent) // 20\n    | WindowClosed(windowEvent) // 21\n    | WindowTakeFocus(windowEvent) // 22\n    | WindowHitTest(windowEvent) // 23\n    | MousePan(mousePan) // 24\n    | DropText(dropEvent) // 25\n    | DropFile(dropEvent) // 26\n    | DropBegin(dropNotificationEvent) // 27\n    | DropComplete(dropNotificationEvent) // 28\n    | WindowFullscreen(windowEvent) // 29\n    // An event that hasn't been implemented yet\n    | Unknown\n    | KeymapChanged;\n\n  let show = (v: t) => {\n    switch (v) {\n    | Quit => \"Quit\"\n    | MouseMotion({windowID, x, y}) =>\n      Printf.sprintf(\"MouseMotion windowId: %d x: %d y: %d\", windowID, x, y)\n    | MouseWheel({windowID, deltaX, deltaY, isFlipped}) =>\n      Printf.sprintf(\n        \"MouseWheel windowId: %d x: %d y: %d isFlipped: %d\",\n        windowID,\n        deltaX,\n        deltaY,\n        isFlipped ? 1 : 0,\n      )\n    | MousePan({\n        windowID,\n        deltaX,\n        deltaY,\n        containsX,\n        containsY,\n        isFling,\n        isInterrupt,\n        _,\n      }) =>\n      Printf.sprintf(\n        \"Pan event: %d %d %d %d %d %d %d\",\n        windowID,\n        deltaX,\n        deltaY,\n        if (containsX) {1} else {0},\n        if (containsY) {1} else {0},\n        if (isFling) {1} else {0},\n        if (isInterrupt) {1} else {0},\n      )\n    | MouseButtonUp({windowID, button, _}) =>\n      Printf.sprintf(\n        \"MouseButtonUp windowId: %d button: %s\",\n        windowID,\n        MouseButton.show(button),\n      )\n    | MouseButtonDown({windowID, button, _}) =>\n      Printf.sprintf(\n        \"MouseButtonDown windowId: %d button: %s\",\n        windowID,\n        MouseButton.show(button),\n      )\n    | KeyDown({repeat, keymod, scancode, keycode, _}) =>\n      Printf.sprintf(\n        \"KeyDown repeat %d:\\n -- %s\\n -- Scancode: %s\\n -- Keycode: %s\\n\",\n        repeat ? 1 : 0,\n        Keymod.show(keymod),\n        Scancode.getName(scancode),\n        Keycode.getName(keycode),\n      )\n    | KeyUp({repeat, keymod, scancode, keycode, _}) =>\n      Printf.sprintf(\n        \"KeyUp repeat %d:\\n -- %s\\n -- Scancode: %s\\n -- Keycode: %s\\n\",\n        repeat ? 1 : 0,\n        Keymod.show(keymod),\n        Scancode.getName(scancode),\n        Keycode.getName(keycode),\n      )\n    | TextEditing({text, start, length, _}) =>\n      Printf.sprintf(\n        \"TextEditing:\\n -- start: %d length: %d text: %s\\n\",\n        start,\n        length,\n        text,\n      )\n    | TextInput({text, _}) =>\n      Printf.sprintf(\"TextInput:\\n -- text: %s\\n\", text)\n    | WindowShown({windowID}) =>\n      Printf.sprintf(\"WindowShown: %d\\n\", windowID)\n    | WindowHidden({windowID}) =>\n      Printf.sprintf(\"WindowHidden: %d\\n\", windowID)\n    | WindowExposed({windowID}) =>\n      Printf.sprintf(\"WindowExposed: %d\\n\", windowID)\n    | WindowMoved({windowID, x, y}) =>\n      Printf.sprintf(\n        \"WindowMoved - windowID: %d x: %d y: %d\\n\",\n        windowID,\n        x,\n        y,\n      )\n    | WindowResized({windowID, width, height}) =>\n      Printf.sprintf(\n        \"WindowResized - windowID: %d x: %d y: %d\\n\",\n        windowID,\n        width,\n        height,\n      )\n    | WindowSizeChanged({windowID, width, height}) =>\n      Printf.sprintf(\n        \"WindowSizeChanged - windowID: %d x: %d y: %d\\n\",\n        windowID,\n        width,\n        height,\n      )\n    | WindowMinimized({windowID}) =>\n      Printf.sprintf(\"WindowMinimized: %d\\n\", windowID)\n    | WindowMaximized({windowID}) =>\n      Printf.sprintf(\"WindowMaximized: %d\\n\", windowID)\n    | WindowFullscreen({windowID}) =>\n      Printf.sprintf(\"WindowFullscreen: %d\\n\", windowID)\n    | WindowRestored({windowID}) =>\n      Printf.sprintf(\"WindowRestored: %d\\n\", windowID)\n    | WindowEnter({windowID}) =>\n      Printf.sprintf(\"WindowEnter: %d\\n\", windowID)\n    | WindowLeave({windowID}) =>\n      Printf.sprintf(\"WindowLeave: %d\\n\", windowID)\n    | WindowFocusGained({windowID}) =>\n      Printf.sprintf(\"WindowFocusGained: %d\\n\", windowID)\n    | WindowFocusLost({windowID}) =>\n      Printf.sprintf(\"WindowFocusLost: %d\\n\", windowID)\n    | WindowClosed({windowID}) =>\n      Printf.sprintf(\"WindowClosed: %d\\n\", windowID)\n    | WindowTakeFocus({windowID}) =>\n      Printf.sprintf(\"WindowTakeFocus: %d\\n\", windowID)\n    | WindowHitTest({windowID}) =>\n      Printf.sprintf(\"WindowHitTest: %d\\n\", windowID)\n    | DropText({windowID, file, x, y, _}) =>\n      Printf.sprintf(\n        \"DropText - windowID: %d file: %s x: %d y: %d\\n\",\n        windowID,\n        file,\n        x,\n        y,\n      )\n    | DropFile({windowID, file, x, y, _}) =>\n      Printf.sprintf(\n        \"DropFile - windowID: %d file: %s x: %d y: %d\\n\",\n        windowID,\n        file,\n        x,\n        y,\n      )\n    | DropBegin({windowID, x, y, _}) =>\n      Printf.sprintf(\"DropBegin - windowID: %d x: %d y: %d\\n\", windowID, x, y)\n    | DropComplete({windowID, x, y, _}) =>\n      Printf.sprintf(\n        \"DropComplete - windowID: %d x: %d y: %d\\n\",\n        windowID,\n        x,\n        y,\n      )\n    | KeymapChanged => \"KeymapChanged\"\n    | Unknown => \"Unknown\"\n    };\n  };\n\n  external poll: unit => option(t) = \"resdl_SDL_PollEvent\";\n  external push: unit => unit = \"resdl_SDL_PushEvent\";\n  external wait: unit => result(t, string) = \"resdl_SDL_WaitEvent\";\n  external waitTimeout: int => option(t) = \"resdl_SDL_WaitTimeoutEvent\";\n};\n\nmodule Cursor = {\n  type systemCursor =\n    | Arrow\n    | IBeam\n    | Wait\n    | Crosshair\n    | WaitArrow\n    | SizeNWSE\n    | SizeNESW\n    | SizeWE\n    | SizeNS\n    | SizeAll\n    | No\n    | Hand;\n\n  type t;\n\n  external createSystem: systemCursor => t = \"resdl_SDL_CreateSystemCursor\";\n  external set: t => unit = \"resdl_SDL_SetCursor\";\n};\n\nmodule MessageBox = {\n  type flags =\n    | Error\n    | Warning\n    | Information;\n\n  external showSimple: (flags, string, string, option(Window.t)) => unit =\n    \"resdl_SDL_ShowSimpleMessageBox\";\n};\n\nmodule Timekeeping = {\n  external getTicks: unit => int = \"resdl_SDL_GetTicks\";\n};\n\nmodule Version = {\n  type t = {\n    major: int,\n    minor: int,\n    patch: int,\n  };\n\n  external getCompiled: unit => t = \"resdl_SDL_GetCompiledVersion\";\n  external getLinked: unit => t = \"resdl_SDL_GetLinkedVersion\";\n\n  let toString: t => string =\n    ({major, minor, patch}) =>\n      Printf.sprintf(\"Major: %d Minor: %d Patch: %d\", major, minor, patch);\n};\n\ntype renderFunction = unit => bool;\nexternal _javaScriptRenderLoop: renderFunction => unit =\n  \"resdl__javascript__renderloop\";\nexternal _javaScriptRenderLoop: renderFunction => unit =\n  \"resdl__javascript__renderloop\";\n\nlet _nativeLoop = renderFn => {\n  while (!renderFn()) {\n    ();\n      /*Thread.yield();*/\n  };\n  ();\n};\n\nlet renderLoop = (renderFunction: renderFunction) => {\n  Callback.register(\"__sdl2_caml_resize__\", renderFunction);\n  switch (Sys.backend_type) {\n  | Native => _nativeLoop(renderFunction)\n  | Bytecode => _nativeLoop(renderFunction)\n  | _ => _javaScriptRenderLoop(renderFunction)\n  };\n};\n"
  },
  {
    "path": "packages/reason-sdl2/src/sdl2_stubs.js",
    "content": "//Provides: resdl_SDL_Init\nfunction resdl_SDL_Init() {\n    joo_global_object._time = {\n        start: Date.now(),\n        offset: 0,\n    };\n\n    joo_global_object._events = [];\n    joo_global_object._popEvent = function () {\n        if (joo_global_object._events.length > 0) {\n            return joo_global_object._events.shift();\n        } else {\n            return null;\n        }\n    };\n\n    joo_global_object._pushEvent = function (evt) {\n        joo_global_object._events.push(evt);\n    };\n\n    joo_global_object.window.addEventListener(\"resize\", function () {\n        var wins = joo_global_object._activeWindows;\n        for (var i = 0; i < wins.length; i++) {\n            wins[i]._notifyResize();\n        }\n    });\n\n    joo_global_object._mouseState = {};\n    joo_global_object.window.addEventListener(\"mousemove\", function (e) {\n        var activeWindow = joo_global_object._activeWindow;\n        joo_global_object._mouseState.x = e.pageX - activeWindow.x;\n        joo_global_object._mouseState.y = e.pageY - activeWindow.y;\n\n        var wins = joo_global_object._activeWindows;\n        for (var i = 0; i < wins.length; i++) {\n            var win = wins[i];\n            win._notifyMouseMove(e.pageX - win.x, e.pageY - win.y);\n        }\n    });\n\n    joo_global_object.window.addEventListener(\"wheel\", function (e) {\n        // TODO: account for deltaMode\n        var deltaX = e.deltaX;\n        var deltaY = e.deltaY;\n\n        var wins = joo_global_object._activeWindows;\n        for (var i = 0; i < wins.length; i++) {\n            wins[i]._notifyScroll(deltaX, deltaY);\n        }\n    });\n\n    joo_global_object.window.addEventListener(\"keypress\", function (keyEvent) {\n        if (keyEvent.key && keyEvent.key.length === 1) {\n            var codepoint = keyEvent.key.codePointAt(0);\n            var wins = joo_global_object._activeWindows;\n            for (var i = 0; i < wins.length; i++) {\n                wins[i]._notifyChar(codepoint);\n            }\n        }\n    });\n\n    joo_global_object.window.addEventListener(\"mousedown\", function (mouseEvent) {\n        var wins = joo_global_object._activeWindows;\n        for (var i = 0; i < wins.length; i++) {\n            wins[i]._notifyMouseButton(mouseEvent, 2);\n        }\n    });\n\n    joo_global_object.window.addEventListener(\"mouseup\", function (mouseEvent) {\n        var wins = joo_global_object._activeWindows;\n        for (var i = 0; i < wins.length; i++) {\n            wins[i]._notifyMouseButton(mouseEvent, 3);\n        }\n    });\n\n    joo_global_object.window.addEventListener(\"keydown\", function (keyEvent) {\n        if (keyEvent.key) {\n            var wins = joo_global_object._activeWindows;\n            for (var i = 0; i < wins.length; i++) {\n                var pressMode = keyEvent.repeat ? 2 : 0;\n                wins[i]._notifyKey(keyEvent, pressMode);\n            }\n        }\n    });\n\n    joo_global_object.window.addEventListener(\"keyup\", function (keyEvent) {\n        if (keyEvent.key) {\n            var wins = joo_global_object._activeWindows;\n            for (var i = 0; i < wins.length; i++) {\n                wins[i]._notifyKey(keyEvent, 1);\n            }\n        }\n    });\n};\n\n// Provides: caml_glfwGetCursorPos\n// Requires: caml_js_to_array\nfunction caml_glfwGetCursorPos(w) {\n    // TODO: Window parameter is currently ignored, but\n    // we should calculate the mouse position relative to it.\n\n    return caml_js_to_array([joo_global_object._mouseState.x, joo_global_object._mouseState.y]);\n}\n\n// Provides: resdl_SDL_CreateSystemCursor\nfunction resdl_SDL_CreateSystemCursor(shape) {\n  switch (shape) {\n  case 0: return \"default\";\n  case 1: return \"text\";\n  case 3: return \"crosshair\";\n  case 4: return \"wait\";\n  case 5: return \"nwse-resize\";\n  case 6: return \"nesw-resize\";\n  case 7: return \"we-resize\";\n  case 8: return \"ns-resize\";\n  case 9: return \"resize\";\n  case 10: return \"not-allowed\";\n  case 11: return \"pointer\";\n  default:\n    joo_global_object.console.warn(\"Unsupported cursor shape.\");\n    return \"default\";\n  }\n}\n\n// Provides: resdl_SDL_EnableScreenSaver\nfunction resdl_SDL_EnableScreenSaver() {\n  // no op\n}\n\n// Provides: resdl_SDL_DisableScreenSaver\nfunction resdl_SDL_DisableScreenSaver() {\n  // no op\n}\n\n// Provides: resdl_SDL_IsScreenSaverEnabled\nfunction resdl_SDL_IsScreenSaverEnabled() {\n  return false;\n}\n\n// Provides: resdl_SDL_GL_SetSwapInterval\nfunction resdl_SDL_GL_SetSwapInterval() {\n  // no op\n}\n\n// Provides: resdl_SDL_PushEvent\nfunction resdl_SDL_PushEvent() {\n  // no op\n}\n\n// Provides: resdl_SDL_GL_MakeCurrent\nfunction resdl_SDL_GL_MakeCurrent() {\n  // no op\n}\n\n// Provides: resdl_SDL_SetWin32ProcessDPIAware\nfunction resdl_SDL_SetWin32ProcessDPIAware() {\n  // no op\n}\n\n// Provides: resdl_SDL_GetWin32ScaleFactor\nfunction resdl_SDL_GetWin32ScaleFactor() {\n  return 1;\n}\n\n// Provides: resdl_SDL_GetWindowDisplayIndex\nfunction resdl_SDL_GetWindowDisplayIndex() {\n  return 0;\n}\n\n// Provides: resdl_SDL_GetDisplayDPI\nfunction resdl_SDL_GetDisplayDPI() {\n  return 96;\n}\n\n// Provides: resdl_SDL_GetDesktopDisplayMode\nfunction resdl_SDL_GetDesktopDisplayMode() {\n  return [0, 100, 200, 0];\n}\n\n// Provides: resdl_SDL_WindowCenter\nfunction resdl_SDL_WindowCenter() {\n  //no-op\n}\n\n// Provides: caml_glfwDestroyCursor\nfunction caml_glfwDestroyCursor(cursor) {\n  // no op\n}\n\n// Provides: resdl_SDL_SetCursor\nfunction resdl_SDL_SetCursor(cursor) {\n  joo_global_object._activeWindow.canvas.style.cursor = cursor;\n}\n\n// Provides: resdl_SDL_SetWindowMinimumSize\nfunction resdl_SDL_SetWindowMinimumSize(win, minWidth, minHeight) {\n    // no-op\n}\n\n// Provides: caml_glfwGetTime_byte\nfunction caml_glfwGetTime_byte() {\n    return (joo_global_object._time.offset + (Date.now() - joo_global_object._time.start)) / 1000;\n}\n\n// Provides: caml_glfwSetTime_byte\nfunction caml_glfwSetTime_byte(t) {\n    joo_global_object._time.offset = t * 1000;\n    joo_global_object._time.start = Date.now();\n}\n\n// Provides: caml_glfwGetPrimaryMonitor\nfunction caml_glfwGetPrimaryMonitor() {\n    // No-op\n}\n\n// Provides: caml_glfwGetVideoMode\nfunction caml_glfwGetVideoMode() {\n    var win = joo_global_object.window;\n    return [0, win.innerWidth, win.innerHeight];\n};\n\n// Provides: caml_glfwGetMonitorPos\nfunction caml_glfwGetMonitorPos() {\n    return [0, 0, 0];\n};\n\n// Provides: caml_glfwGetMonitorPhysicalSize\nfunction caml_glfwGetMonitorPhysicalSize() {\n  var win = joo_global_object.window;\n  var dpi = 96;\n  var widthMM = (win.innerWidth * 25.4) / dpi;\n  var heightMM = (win.innerHeight * 25.4) / dpi;\n  return [0, widthMM, heightMM];\n};\n\n// Provides: resdl_SDL_GetWindowSize\nfunction resdl_SDL_GetWindowSize(w) {\n    var pixelRatio = joo_global_object.window.devicePixelRatio;\n    var width = w.canvas.width / pixelRatio;\n    var height = w.canvas.height / pixelRatio;\n    return [0, width, height];\n}\n\n// Provides: resdl_SDL_GetWindowId\nfunction resdl_SDL_GetWindowId(w) {\n    return 0;\n}\n\n// Provides: resdl_SDL_GL_GetDrawableSize\nfunction resdl_SDL_GL_GetDrawableSize(w) {\n    var pixelRatio = joo_global_object.window.devicePixelRatio;\n    var width = w.canvas.width;\n    var height = w.canvas.height;\n    return [0, width, height];\n}\n\n// Provides: caml_glfwDefaultWindowHints\nfunction caml_glfwDefaultWindowHints(w) {\n    joo_global_object.console.warn(\"glfwDefaultWindowHints not implemented in WebGL\");\n}\n\n// Provides: caml_glfwShowWindow\nfunction caml_glfwShowWindow(w) {\n    joo_global_object.console.warn(\"glfwShowWindow not implemented in WebGL\");\n}\n\n// Provides: caml_glfwHideWindow\nfunction caml_glfwHideWindow(w) {\n    joo_global_object.console.warn(\"glfwHideWindow not implemented in WebGL\");\n}\n\n// Provides: caml_test_callback_success\nfunction caml_test_callback_success(s, f) {\n    s(999);\n}\n\n// Provides: resdl__javascript__renderloop\nfunction resdl__javascript__renderloop(loopFunc) {\n    function renderLoop(t) {\n        var ret = loopFunc(t);\n\n        if (!ret) {\n            joo_global_object.window.requestAnimationFrame(renderLoop);\n        }\n    }\n\n    joo_global_object.window.requestAnimationFrame(renderLoop);\n}\n\n//Provides: resdl_SDL_ShowWindow\nfunction resdl_SDL_ShowWindow(w) {\n    // no-op\n}\n\n//Provides: resdl_SDL_CreateWindow\nfunction resdl_SDL_CreateWindow(width, height, title) {\n    var canvas = document.createElement(\"canvas\");\n    canvas.style.position = \"absolute\";\n    canvas.style.top = \"0px\";\n    canvas.style.bottom = \"0px\";\n    canvas.style.width = width.toString() + \"px\";\n    canvas.style.height = height.toString() + \"px\";\n    var pixelRatio = joo_global_object.window.devicePixelRatio;\n    canvas.width = width * pixelRatio;\n    canvas.height = height * pixelRatio;\n\n    joo_global_object._activeWindows = joo_global_object._activeWindows || [];\n\n    document.body.appendChild(canvas);\n    var w = {\n        id: 0,\n        canvas: canvas,\n        title: title,\n        isMaximized: false,\n        onSetFramebufferSize: null,\n        onSetWindowSize: null,\n        onKey: null,\n        onChar: null,\n        x: 0,\n        y: 0,\n    };\n\n    var notifyChar = function (codepoint) {\n        if (w.onChar) {\n            w.onChar(w, codepoint);\n        }\n    };\n\n    var notifyKey = function (keyEvent, pressMode) {\n        if (w.onKey) {\n\n            if (keyEvent.key && keyEvent.key.length == 1) {\n                var code = keyEvent.key.toUpperCase().charCodeAt(0);\n                var modifier = 0;\n                modifier = keyEvent.shiftKey ? modifier + 1 : modifier;\n                modifier = keyEvent.ctrlKey ? modifier + 2 : modifier;\n                modifier = keyEvent.altKey ? modifier + 4 : modifier;\n                modifier = keyEvent.metaKey ? modifier + 8 : modifier\n\n                w.onKey(w, code, keyEvent.location, pressMode, modifier);\n            };\n        }\n    };\n\n    var notifyMouseButton = function(mouseEvent, pressMode) {\n        joo_global_object._pushEvent(\n        [pressMode, \n            [0, \n                w.id, \n                0, \n                0, \n                joo_global_object._mouseState.x, \n                joo_global_object._mouseState.y]]);\n    };\n\n    var notifyMouseMove = function(x, y) {\n        joo_global_object._pushEvent(\n        [0, [0, w.id, x, y]]\n        );\n    };\n\n    var notifyResize = function () {\n        if (w.isMaximized) {\n            var pixelRatio = joo_global_object.window.devicePixelRatio;\n\n            canvas.width = canvas.offsetWidth * pixelRatio;\n            canvas.height = canvas.offsetHeight * pixelRatio;\n\n            joo_global_object._pushEvent([12, [0, w.id, canvas.offsetWidth, canvas.offsetHeight]]);\n        }\n    };\n\n    var notifyScroll = function (x, y) {\n        joo_global_object._pushEvent([1, [0, w.id, x, y, false]]);\n    };\n\n    w._notifyResize = notifyResize;\n    w._notifyChar = notifyChar;\n    w._notifyScroll = notifyScroll;\n    w._notifyKey = notifyKey;\n    w._notifyMouseButton = notifyMouseButton;\n    w._notifyMouseMove = notifyMouseMove;\n\n    joo_global_object._activeWindows.push(w);\n    return w;\n};\n\n// Provides: resdl_SDL_SetWindowPosition\nfunction resdl_SDL_SetWindowPosition(w, x, y) {\n    var canvas = w.canvas;\n    canvas.style.transform = \"translate(\" + x.toString() + \"px, \" + y.toString() + \"px)\";\n    w.x = x;\n    w.y = y;\n}\n\n// Provides: resdl_SDL_SetWindowSize\nfunction resdl_SDL_SetWindowSize(w, width, height) {\n    var canvas = w.canvas;\n    canvas.style.width = width.toString() + \"px\";\n    canvas.style.height = height.toString() + \"px\";\n    var pixelRatio = joo_global_object.window.devicePixelRatio;\n    canvas.width = width * pixelRatio;\n    canvas.height = height * pixelRatio;\n}\n\n// Provides: resdl_SDL_SetWindowTitle\nfunction resdl_SDL_SetWindowTitle(w, title) {\n    var t = title.toString();\n    w.title = t;\n    document.title = t;\n}\n\n// Provides: resdl_SDL_CreateRGBSurfaceFromImage\nfunction resdl_SDL_CreateRGBSurfaceFromImage(path) {\n    return [0, path];\n};\n\n// Provides: resdl_SDL_SetWindowIcon\nfunction resdl_SDL_SetWindowIcon(w, surface) {\n    var p = surface.toString();\n    var link = document.querySelector(\"link[rel*='icon']\") || document.createElement('link');\n    link.type = 'image/x-icon';\n    link.rel = 'shortcut icon';\n    link.href = p;\n    document.getElementsByTagName('head')[0].appendChild(link);\n}\n\n// Provides: resdl_SDL_SetTextInputRect\nfunction resdl_SDL_SetTextInputRect(x, y, w, h) {\n    // TODDO: No-op\n}\n\n// Provides: resdl_SDL_StartTextInput\nfunction resdl_SDL_StartTextInput() {\n    // TODDO: No-op\n}\n\n// Provides: resdl_SDL_MaximizeWindow\nfunction resdl_SDL_MaximizeWindow(w) {\n    if (w && !w.isMaximized) {\n        var pixelRatio = joo_global_object.window.devicePixelRatio;\n        var canvas = w.canvas;\n        canvas.style.width = \"100%\";\n        canvas.style.height = \"100%\";\n        canvas.width = canvas.offsetWidth * pixelRatio;\n        canvas.height = canvas.offsetHeight * pixelRatio;\n        w.isMaximized = true;\n        w._notifyResize();\n    }\n}\n\n// Provides: resdl_SDL_GL_Setup\nfunction resdl_SDL_GL_Setup(win) {\n    var gl = win.canvas.getContext('webgl');\n    joo_global_object.window.__glfw__gl__ = gl;\n    joo_global_object.window._activeWindow = win;\n\n    joo_global_object.variantToTextureType = {\n        '0': gl.TEXTURE_2D,\n    };\n\n    joo_global_object.variantToEnableOption = {\n        '0': gl.DEPTH_TEST,\n        '1': gl.BLEND,\n        '2': gl.SCISSOR_TEST,\n    };\n\n    joo_global_object.variantToBlendFunc = {\n        '0': gl.ZERO,\n        '1': gl.ONE,\n        '2': gl.SRC_ALPHA,\n        '3': gl.ONE_MINUS_SRC_ALPHA,\n    };\n\n    joo_global_object.variantToTextureParameter = {\n        '0': gl.TEXTURE_WRAP_S,\n        '1': gl.TEXTURE_WRAP_T,\n        '2': gl.TEXTURE_MIN_FILTER,\n        '3': gl.TEXTURE_MAG_FILTER,\n    };\n\n    joo_global_object.variantToPixelAlignmentParameter = {\n        '0': gl.PACK_ALIGNMENT,\n        '1': gl.UNPACK_ALIGNMENT,\n    };\n\n    joo_global_object.variantToTextureParameterValue = {\n        '0': gl.REPEAT,\n        '1': gl.LINEAR,\n        '2': gl.CLAMP_TO_EDGE,\n    }\n\n    joo_global_object.variantToFormat = {\n        '0': gl.ALPHA,\n        '1': gl.RGB,\n        '2': gl.RGBA,\n    }\n\n    joo_global_object.formatToNumChannels = {\n        '6406': 1, // ALPHA\n        '6407': 3, // RGB\n        '6408': 4, // RGBA\n    }\n\n    joo_global_object.variantToType = {\n        '0': gl.FLOAT,\n        '1': gl.UNSIGNED_BYTE,\n        '2': gl.UNSIGNED_SHORT,\n    }\n\n    joo_global_object.variantToBufferType = {\n        '0': gl.ARRAY_BUFFER,\n        '1': gl.ELEMENT_ARRAY_BUFFER,\n    }\n\n    joo_global_object.variantToDrawMode = {\n        '0': gl.TRIANGLES,\n        '1': gl.TRIANGLE_STRIP\n    }\n\n    joo_global_object.gl = gl;\n}\n\n// Provides: resdl_SDL_PollEvent\nfunction resdl_SDL_PollEvent() {\n    var evt = joo_global_object._popEvent();\n    if (evt != null) {\n        return [0, evt];\n    } else {\n        return 0;\n    }\n    return 0;\n}\n// Provides: resdl_SDL_ShowSimpleMessageBox\nfunction resdl_SDL_ShowSimpleMessageBox(flags, title, msg, win) {\n    joo_global_object.alert(msg);\n}\n\n// Provides: resdl_SDL_GetNativeWindow\nfunction resdl_SDL_GetNativeWindow(win) {\n    // TODO: Return canvas?\n    return null;\n}\n\n// Provides: resdl_SDL_WinAttachConsole\nfunction resdl_SDL_WinAttachConsole() {\n    // no-op\n    return 1;\n}\n\n// Provides: resdl_SDL_WinAllocConsole\nfunction resdl_SDL_WinAllocConsole() {\n    // no-op\n    return 1;\n}\n\n// Provides: resdl_SDL_GetPlatform\n// Requires: caml_js_to_string\nfunction resdl_SDL_GetPlatform() {\n    return caml_js_to_string(\"Browser\");\n}\n\n// Provides: resdl_SDL_GetVersion\n// Requires: caml_js_to_string\nfunction resdl_SDL_GetVersion() {\n    return caml_js_to_string(\"Not implemented\");\n}\n\n// Provides: resdl_SDL_SetMacBackgroundColor\nfunction resdl_SDL_SetMacBackgroundColor() {\n    // No-op\n}\n\n// Provides: resdl_SDL_SetMacTitlebarTransparent\nfunction resdl_SDL_SetMacTitlebarTransparent() {\n    // No-op\n}\n\n// Provides: resdl_SDL_EnableHitTest\nfunction resdl_SDL_EnableHitTest() {\n    // No-op\n}\n\n// Provides: resdl_SDL_DisableHitTest\nfunction resdl_SDL_DisableHitTest() {\n    // No-op\n}\n\n// Provides: resdl_SDL_SetWindowBordered\nfunction resdl_SDL_SetWindowBordered() {\n    // No-op\n}\n\n// Provides: resdl_SDL_SetWindowResizable\nfunction resdl_SDL_SetWindowResizable() {\n    // No-op\n}\n\n// Provides: resdl_SDL_GetModState\nfunction resdl_SDL_GetModState() {\n    return 0;\n}\n\n// Provides: resdl_SDL_WaitTimeoutEvent\n// Requires: resdl_SDL_PollEvent\nfunction resdl_SDL_WaitTimeoutEvent() {\n    joo_global_object.console.warn(\"waitTimeout not supported in JSOO\");\n    return resdl_SDL_PollEvent();\n}\n\n// Provides: resdl_SDL_GL_SwapWindow\nfunction resdl_SDL_GL_SwapWindow() {\n    // no op\n}\n"
  },
  {
    "path": "packages/reason-sdl2/src/sdl2_wrapper.cpp",
    "content": "#include <cstdint>\n#include <cstdio>\n\n#include <algorithm>\n#include <array>\n\n#include <caml/alloc.h>\n#include <caml/bigarray.h>\n#include <caml/callback.h>\n#include <caml/fail.h>\n#include <caml/memory.h>\n#include <caml/mlvalues.h>\n#include <caml/threads.h>\n\n#include \"stb_image.h\"\n\n#include <SDL2/SDL.h>\n#include <SDL2/SDL_audio.h>\n#include <SDL2/SDL_main.h>\n#include <SDL2/SDL_config.h>\n#include <SDL2/SDL_syswm.h>\n\n#ifdef SDL_VIDEO_DRIVER_COCOA\n#import <Cocoa/Cocoa.h>\n#endif\n\n#ifdef WIN32\n#include <Windows.h>\n#include <fcntl.h>\n#include <io.h>\n#endif\n\n#include <glad/glad.h>\n\n#define Val_none Val_int(0)\nstatic value Val_some(value v) {\n    CAMLparam1(v);\n    CAMLlocal1(some);\n    some = caml_alloc(1, 0);\n    Store_field(some, 0, v);\n    CAMLreturn(some);\n}\n\nstatic value Val_ok(value v) {\n    CAMLparam1(v);\n    CAMLlocal1(some);\n    some = caml_alloc(1, 0);\n    Store_field(some, 0, v);\n    CAMLreturn(some);\n}\n\nstatic value Val_error(value v) {\n    CAMLparam1(v);\n    CAMLlocal1(some);\n    some = caml_alloc(1, 1);\n    Store_field(some, 0, v);\n    CAMLreturn(some);\n}\n\nextern \"C\" {\n    /* Create an OCaml value encapsulating the pointer p */\n    value resdl_wrapPointer(void *p) {\n        value v = caml_alloc(1, Abstract_tag);\n        *((void **) Data_abstract_val(v)) = p;\n        return v;\n    }\n\n    /* Extract the pointer encapsulated in the given OCaml value */\n    void *resdl_unwrapPointer(value v) {\n        return *((void **) Data_abstract_val(v));\n    }\n\n    CAMLprim value resdl_SDL_EnableScreenSaver() {\n        CAMLparam0();\n        SDL_EnableScreenSaver();\n        CAMLreturn(Val_unit);\n    }\n\n    CAMLprim value resdl_SDL_DisableScreenSaver() {\n        CAMLparam0();\n        SDL_DisableScreenSaver();\n        CAMLreturn(Val_unit);\n    }\n\n    CAMLprim value resdl_SDL_IsScreenSaverEnabled() {\n        CAMLparam0();\n        int result = SDL_IsScreenSaverEnabled() == SDL_TRUE;\n        CAMLreturn(Val_int(result));\n    }\n\n    CAMLprim value resdl_SDL_SetMainReady() {\n        SDL_SetMainReady();\n\n        return Val_unit;\n    }\n    CAMLprim value resdl_SDL_DestroyWindow(value vWin) {\n        CAMLparam1(vWin);\n        SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);\n        SDL_DestroyWindow(win);\n        CAMLreturn(Val_unit);\n    }\n\n    SDL_HitTestResult resdl_hit_test(SDL_Window *win, const SDL_Point *area,\n                                     void *data) {\n\n        CAMLparam0();\n        CAMLlocal2(vWin, vRet);\n        static const value *hitTestCallback = NULL;\n        if (hitTestCallback == NULL) {\n            hitTestCallback = caml_named_value(\"__sdl2_caml_hittest__\");\n        }\n        vWin = resdl_wrapPointer(win);\n        vRet = caml_callback3(*hitTestCallback, vWin, Val_int(area->x),\n                              Val_int(area->y));\n        SDL_HitTestResult result;\n        switch (Int_val(vRet)) {\n        case 0:\n            result = SDL_HITTEST_NORMAL;\n            break;\n        case 1:\n            result = SDL_HITTEST_DRAGGABLE;\n            break;\n        case 2:\n            result = SDL_HITTEST_RESIZE_TOPLEFT;\n            break;\n        case 3:\n            result = SDL_HITTEST_RESIZE_TOP;\n            break;\n        case 4:\n            result = SDL_HITTEST_RESIZE_TOPRIGHT;\n            break;\n        case 5:\n            result = SDL_HITTEST_RESIZE_RIGHT;\n            break;\n        case 6:\n            result = SDL_HITTEST_RESIZE_BOTTOMRIGHT;\n            break;\n        case 7:\n            result = SDL_HITTEST_RESIZE_BOTTOM;\n            break;\n        case 8:\n            result = SDL_HITTEST_RESIZE_BOTTOMLEFT;\n            break;\n        case 9:\n            result = SDL_HITTEST_RESIZE_LEFT;\n            break;\n        default:\n            result = SDL_HITTEST_NORMAL;\n            break;\n        }\n\n        CAMLreturnT(SDL_HitTestResult, result);\n    };\n\n    CAMLprim value resdl_SDL_EnableHitTest(value vWin) {\n        CAMLparam1(vWin);\n        SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);\n        SDL_SetWindowHitTest(win, resdl_hit_test, NULL);\n        CAMLreturn(Val_unit);\n    }\n\n    CAMLprim value resdl_SDL_DisableHitTest(value vWin) {\n        CAMLparam1(vWin);\n        SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);\n        SDL_SetWindowHitTest(win, NULL, NULL);\n        CAMLreturn(Val_unit);\n    }\n\n    CAMLprim value resdl_SDL_Delay(value delay) {\n        CAMLparam1(delay);\n\n        int d = Int_val(delay);\n        SDL_Delay(d);\n        CAMLreturn(Val_unit);\n    }\n\n#ifdef WIN32\n\n    typedef enum PROCESS_DPI_AWARENESS {\n        PROCESS_DPI_UNAWARE = 0,\n        PROCESS_SYSTEM_DPI_AWARE = 1,\n        PROCESS_PER_MONITOR_DPI_AWARE = 2\n    } PROCESS_DPI_AWARENESS;\n\n    HWND getHWNDFromSDLWindow(SDL_Window *win) {\n        SDL_SysWMinfo wmInfo;\n        SDL_VERSION(&wmInfo.version);\n        SDL_GetWindowWMInfo(win, &wmInfo);\n        return wmInfo.info.win.window;\n    };\n\n#endif\n\n    CAMLprim value resdl_SDL_GetPlatform() {\n        CAMLparam0();\n        CAMLlocal1(ret);\n\n        const char *str = SDL_GetPlatform();\n        ret = caml_copy_string(str);\n\n        CAMLreturn(ret);\n    }\n\n    CAMLprim value resdl_SDL_GetVersion() {\n        CAMLparam0();\n        CAMLlocal1(ret);\n\n        const char *str;\n#ifdef __APPLE__\n        NSProcessInfo *pInfo = [NSProcessInfo processInfo];\n        NSString *version = [pInfo operatingSystemVersionString];\n        str = [version UTF8String];\n#else\n        str = \"??.??.??\";\n#endif\n        ret = caml_copy_string(str);\n\n        CAMLreturn(ret);\n    }\n\n    CAMLprim value resdl_SDL_GetNativeWindow(value vWin) {\n        CAMLparam1(vWin);\n        CAMLlocal1(vNativeWindow);\n\n        SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);\n        SDL_SysWMinfo wmInfo;\n        SDL_VERSION(&wmInfo.version);\n        SDL_GetWindowWMInfo(win, &wmInfo);\n\n        void *pNativeWindow = NULL;\n        switch (wmInfo.subsystem) {\n#ifdef SDL_VIDEO_DRIVER_WINDOWS\n        case SDL_SYSWM_WINDOWS:\n            pNativeWindow = (void *)wmInfo.info.win.window;\n            break;\n#endif\n#ifdef SDL_VIDEO_DRIVER_UIKIT\n        case SDL_SYSWM_UIKIT:\n            pNativeWindow = (void *)wmInfo.info.uikit.window;\n            break;\n#endif\n#ifdef SDL_VIDEO_DRIVER_COCOA\n        case SDL_SYSWM_COCOA:\n            pNativeWindow = (void *)wmInfo.info.cocoa.window;\n            break;\n#endif\n#ifdef SDL_VIDEO_DRIVER_ANDROID\n        case SDL_SYSWM_ANDROID:\n            pNativeWindow = (void *)wmInfo.info.android.window;\n            break;\n#endif\n#ifdef SDL_VIDEO_DRIVER_X11\n        case SDL_SYSWM_X11:\n            pNativeWindow = (void *)wmInfo.info.x11.window;\n            break;\n#endif\n#ifdef SDL_VIDEO_DRIVER_WAYLAND\n        case SDL_SYSWM_WAYLAND:\n            pNativeWindow = (void *)wmInfo.info.wl.surface;\n            break;\n#endif\n        default:\n            break;\n        }\n\n        vNativeWindow = resdl_wrapPointer(pNativeWindow);\n\n        CAMLreturn(vNativeWindow);\n    };\n\n#ifdef WIN32\n\n// This method is calling after attach / alloc console\n// to wire up the new stdin/stdout/stderr.\n// See further details (thanks @dra27 for the help!)\n// - https://github.com/ocaml/ocaml/issues/9252\n    void resdl_Win32AttachStdIO() {\n        int fd_in = _open_osfhandle((intptr_t)GetStdHandle(STD_INPUT_HANDLE),\n                                    _O_RDONLY | _O_BINARY);\n        int fd_out = _open_osfhandle((intptr_t)GetStdHandle(STD_OUTPUT_HANDLE),\n                                     _O_WRONLY | _O_BINARY);\n        int fd_err = _open_osfhandle((intptr_t)GetStdHandle(STD_ERROR_HANDLE),\n                                     _O_WRONLY | _O_BINARY);\n\n        if (fd_in) {\n            dup2(fd_in, 0);\n            close(fd_in);\n            SetStdHandle(STD_INPUT_HANDLE, (HANDLE)_get_osfhandle(0));\n        }\n\n        if (fd_out) {\n            dup2(fd_out, 1);\n            close(fd_out);\n            SetStdHandle(STD_OUTPUT_HANDLE, (HANDLE)_get_osfhandle(1));\n        }\n\n        if (fd_err) {\n            dup2(fd_err, 2);\n            close(fd_err);\n            SetStdHandle(STD_ERROR_HANDLE, (HANDLE)_get_osfhandle(2));\n        }\n        *stdin = *(fdopen(0, \"rb\"));\n        *stdout = *(fdopen(1, \"wb\"));\n        *stderr = *(fdopen(2, \"wb\"));\n\n        setvbuf(stdin, NULL, _IONBF, 0);\n        setvbuf(stdout, NULL, _IONBF, 0);\n        setvbuf(stderr, NULL, _IONBF, 0);\n    }\n#endif\n\n    CAMLprim value resdl_SDL_WinAttachConsole() {\n        CAMLparam0();\n        int ret = 0;\n#ifdef WIN32\n        // Only attach if we don't already have a stdout handle\n        if (GetStdHandle(STD_OUTPUT_HANDLE) == NULL) {\n            ret = AttachConsole(ATTACH_PARENT_PROCESS);\n            if (ret) {\n                resdl_Win32AttachStdIO();\n            }\n        } else {\n            // There's already a stdout handle available,\n            // so return success\n            ret = 1;\n        }\n#endif\n        CAMLreturn(Val_int(ret));\n    }\n\n    CAMLprim value resdl_SDL_WinAllocConsole() {\n        CAMLparam0();\n        int ret = 0;\n#ifdef WIN32\n        ret = AllocConsole();\n        if (ret) {\n            resdl_Win32AttachStdIO();\n        }\n#endif\n        CAMLreturn(Val_int(ret));\n    }\n\n    CAMLprim value resdl_SDL_SetMacTitlebarHidden(value vWin) {\n        CAMLparam1(vWin);\n\n#ifdef SDL_VIDEO_DRIVER_COCOA\n        SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);\n        SDL_SysWMinfo wmInfo;\n        SDL_VERSION(&wmInfo.version);\n        SDL_GetWindowWMInfo(win, &wmInfo);\n        NSWindow *nWindow = wmInfo.info.cocoa.window;\n        [nWindow\n         setStyleMask:[nWindow styleMask] | NSWindowStyleMaskDocModalWindow];\n#endif\n\n        CAMLreturn(Val_unit);\n    }\n\n    CAMLprim value resdl_SDL_SetMacTitlebarTransparent(value vWin) {\n        CAMLparam1(vWin);\n\n#ifdef SDL_VIDEO_DRIVER_COCOA\n        SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);\n        SDL_SysWMinfo wmInfo;\n        SDL_VERSION(&wmInfo.version);\n        SDL_GetWindowWMInfo(win, &wmInfo);\n        NSWindow *nWindow = wmInfo.info.cocoa.window;\n        [nWindow\n         setStyleMask:[nWindow styleMask] | NSWindowStyleMaskFullSizeContentView];\n        [nWindow setTitlebarAppearsTransparent:YES];\n        nWindow.titleVisibility = NSWindowTitleHidden;\n#endif\n\n        CAMLreturn(Val_unit);\n    }\n\n    CAMLprim value resdl_SDL_GetMacTitlebarHeight(value vWin) {\n        CAMLparam1(vWin);\n        double titlebarHeight = 0.0;\n\n#ifdef SDL_VIDEO_DRIVER_COCOA\n        SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);\n        SDL_SysWMinfo wmInfo;\n        SDL_VERSION(&wmInfo.version);\n        SDL_GetWindowWMInfo(win, &wmInfo);\n        NSWindow *nWindow = wmInfo.info.cocoa.window;\n\n        // Sourced from: https://stackoverflow.com/a/59323932/12701512\n        CGFloat windowFrameHeight = CGRectGetHeight([nWindow contentView].frame);\n        CGFloat contentLayoutRectHeight = CGRectGetHeight([nWindow contentLayoutRect]);\n        titlebarHeight = (double)(windowFrameHeight - contentLayoutRectHeight);\n#endif\n        CAMLreturn(caml_copy_double(titlebarHeight));\n    }\n\n    CAMLprim value resdl_SDL_SetMacBackgroundColor(value vWin, value r, value g,\n            value b, value a) {\n        CAMLparam5(vWin, r, g, b, a);\n\n#ifdef SDL_VIDEO_DRIVER_COCOA\n        SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);\n        SDL_SysWMinfo wmInfo;\n        SDL_VERSION(&wmInfo.version);\n        SDL_GetWindowWMInfo(win, &wmInfo);\n        double red = Double_val(r);\n        double green = Double_val(g);\n        double blue = Double_val(b);\n        double alpha = Double_val(a);\n        NSWindow *nWindow = wmInfo.info.cocoa.window;\n        NSColor *rgb =\n            [NSColor colorWithDeviceRed:red green:green blue:blue alpha:alpha];\n        [nWindow setBackgroundColor:rgb];\n#endif\n\n        CAMLreturn(Val_unit);\n    }\n\n    CAMLprim value resdl_SDL_SetWin32ProcessDPIAware(value vWin) {\n        CAMLparam1(vWin);\n\n#ifdef WIN32\n        void *userDLL;\n        BOOL(WINAPI * SetProcessDPIAware)(void); // Vista and later\n        void *shcoreDLL;\n        HRESULT(WINAPI * SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS dpiAwareness);\n\n        userDLL = SDL_LoadObject(\"USER32.DLL\");\n        if (userDLL) {\n            SetProcessDPIAware =\n                (BOOL(WINAPI *)(void))SDL_LoadFunction(userDLL, \"SetProcessDPIAware\");\n        }\n\n        shcoreDLL = SDL_LoadObject(\"SHCORE.DLL\");\n        if (shcoreDLL) {\n            SetProcessDpiAwareness =\n                (HRESULT(WINAPI *)(PROCESS_DPI_AWARENESS))SDL_LoadFunction(\n                    shcoreDLL, \"SetProcessDpiAwareness\");\n        }\n\n        if (SetProcessDpiAwareness) {\n            // Try Windows 8.1+ version\n            HRESULT result = SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);\n        } else if (SetProcessDPIAware) {\n            // Try Vista - Windows 8 version.\n            // This has a constant scale factor for all monitors.\n            BOOL success = SetProcessDPIAware();\n        }\n#endif\n\n        CAMLreturn(Val_unit);\n    };\n\n    CAMLprim value resdl_SDL_GetWin32ScaleFactor(value vWin) {\n        CAMLparam1(vWin);\n\n#ifdef WIN32\n        SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);\n        HWND hwnd = getHWNDFromSDLWindow(win);\n        HMONITOR hmon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY);\n\n        void *shcoreDLL;\n        HRESULT(WINAPI * GetScaleFactorForMonitor)(HMONITOR hmon, int *pScale);\n\n        shcoreDLL = SDL_LoadObject(\"SHCORE.DLL\");\n        if (shcoreDLL) {\n            GetScaleFactorForMonitor =\n                (HRESULT(WINAPI *)(HMONITOR, int *))SDL_LoadFunction(\n                    shcoreDLL, \"GetScaleFactorForMonitor\");\n        }\n\n        if (GetScaleFactorForMonitor) {\n            int pScale;\n            GetScaleFactorForMonitor(hmon, &pScale);\n            CAMLreturn(caml_copy_double(pScale / 100.0));\n        } else {\n            CAMLreturn(caml_copy_double(1.0));\n        }\n\n#else\n        CAMLreturn(caml_copy_double(1.0));\n#endif\n    };\n\n    CAMLprim value resdl_SDL_GetDisplayDPI(value vDisplay) {\n        CAMLparam1(vDisplay);\n        CAMLlocal1(ret);\n        int displayIndex = Int_val(vDisplay);\n\n        float ddpi, hdpi, vdpi;\n        SDL_GetDisplayDPI(displayIndex, &ddpi, &hdpi, &vdpi);\n\n        ret = caml_alloc(3, 0);\n        Store_double_field(ret, 0, ddpi);\n        Store_double_field(ret, 1, hdpi);\n        Store_double_field(ret, 2, vdpi);\n        CAMLreturn(ret);\n    };\n\n    CAMLprim value resdl_SDL_GetCurrentDisplayMode(value vDisplay) {\n        CAMLparam1(vDisplay);\n        CAMLlocal1(ret);\n\n        int displayIndex = Int_val(vDisplay);\n        SDL_DisplayMode current;\n\n        SDL_GetCurrentDisplayMode(displayIndex, &current);\n\n        ret = caml_alloc(4, 0);\n        Store_field(ret, 0, Val_int(current.format));\n        Store_field(ret, 1, Val_int(current.w));\n        Store_field(ret, 2, Val_int(current.h));\n        Store_field(ret, 3, Val_int(current.refresh_rate));\n        CAMLreturn(ret);\n    };\n\n    CAMLprim value resdl_SDL_GetDesktopDisplayMode(value vDisplay) {\n        CAMLparam1(vDisplay);\n        CAMLlocal1(ret);\n\n        int displayIndex = Int_val(vDisplay);\n        SDL_DisplayMode current;\n\n        SDL_GetDesktopDisplayMode(displayIndex, &current);\n\n        ret = caml_alloc(4, 0);\n        Store_field(ret, 0, Val_int(current.format));\n        Store_field(ret, 1, Val_int(current.w));\n        Store_field(ret, 2, Val_int(current.h));\n        Store_field(ret, 3, Val_int(current.refresh_rate));\n        CAMLreturn(ret);\n    };\n\n    CAMLprim value resdl_SDL_GetDisplayBounds(value vDisplay) {\n        CAMLparam1(vDisplay);\n        CAMLlocal1(rect);\n\n        int displayIndex = Int_val(vDisplay);\n        SDL_Rect sdlRect;\n\n        SDL_GetDisplayBounds(displayIndex, &sdlRect);\n\n        rect = caml_alloc(4, 0);\n        Store_field(rect, 0, Val_int(sdlRect.x));\n        Store_field(rect, 1, Val_int(sdlRect.y));\n        Store_field(rect, 2, Val_int(sdlRect.w));\n        Store_field(rect, 3, Val_int(sdlRect.h));\n        CAMLreturn(rect);\n    };\n\n    CAMLprim value resdl_SDL_GetDisplayName(value vDisplay) {\n        CAMLparam1(vDisplay);\n        CAMLlocal1(retStr);\n\n        int displayIndex = Int_val(vDisplay);\n        const char *szDisplayName = SDL_GetDisplayName(displayIndex);\n        if (!szDisplayName) {\n            szDisplayName = \"(Null)\";\n        };\n\n        retStr = caml_copy_string(szDisplayName);\n        CAMLreturn(retStr);\n    };\n\n    CAMLprim value resdl_SDL_GetDisplayUsableBounds(value vDisplay) {\n        CAMLparam1(vDisplay);\n        CAMLlocal1(rect);\n\n        int displayIndex = Int_val(vDisplay);\n        SDL_Rect sdlRect;\n\n        SDL_GetDisplayUsableBounds(displayIndex, &sdlRect);\n\n        rect = caml_alloc(4, 0);\n        Store_field(rect, 0, Val_int(sdlRect.x));\n        Store_field(rect, 1, Val_int(sdlRect.y));\n        Store_field(rect, 2, Val_int(sdlRect.w));\n        Store_field(rect, 3, Val_int(sdlRect.h));\n        CAMLreturn(rect);\n    };\n\n    CAMLprim value resdl_SDL_GetNumVideoDisplays(value vUnit) {\n        CAMLparam0();\n        int num = SDL_GetNumVideoDisplays();\n        CAMLreturn(Val_int(num));\n    };\n\n    CAMLprim value resdl_SDL_GetPixelFormatName(value vPixelFormat) {\n        CAMLparam1(vPixelFormat);\n        CAMLlocal1(ret);\n\n        Uint32 format = Int_val(vPixelFormat);\n        const char *szPixelFormatName = SDL_GetPixelFormatName(format);\n\n        if (!szPixelFormatName) {\n            ret = caml_copy_string(\"(null)\");\n        } else {\n            ret = caml_copy_string(szPixelFormatName);\n        }\n\n        CAMLreturn(ret);\n    }\n\n    CAMLprim value resdl_SDL_GetWindowDisplayIndex(value vWin) {\n        CAMLparam1(vWin);\n        SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);\n        int idx = SDL_GetWindowDisplayIndex(win);\n        CAMLreturn(Val_int(idx));\n    };\n\n    CAMLprim value resdl_SDL_GetWindowPixelFormat(value vWin) {\n        CAMLparam1(vWin);\n        SDL_Window *pWin = (SDL_Window *)resdl_unwrapPointer(vWin);\n        Uint32 format = SDL_GetWindowPixelFormat(pWin);\n        CAMLreturn(Val_int(format));\n    };\n\n    CAMLprim value resdl_SDL_GL_SetSwapInterval(value vInterval) {\n        int interval = Int_val(vInterval);\n        SDL_GL_SetSwapInterval(interval);\n        return Val_unit;\n    };\n\n    CAMLprim value resdl_SDL_GL_Setup(value vWin) {\n        CAMLparam1(vWin);\n        CAMLlocal1(vCtx);\n        SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);\n        SDL_GLContext ctx = SDL_GL_CreateContext(win);\n\n        if (!ctx) {\n            SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, \"SDL_GL_Setup failed: %s\\n\",\n                            SDL_GetError());\n        }\n\n        vCtx = resdl_wrapPointer(ctx);\n\n        CAMLreturn(vCtx);\n    }\n\n    typedef const GLubyte *(*glGetStringFunc)(GLenum);\n\n    CAMLprim value resdl_SDL_GL_GetString(value vStr) {\n        CAMLparam1(vStr);\n        CAMLlocal1(ret);\n\n        GLenum name = GL_VENDOR;\n        switch (Int_val(vStr)) {\n        case 0:\n            name = GL_VENDOR;\n            break;\n        case 1:\n            name = GL_RENDERER;\n            break;\n        case 2:\n            name = GL_VERSION;\n            break;\n        case 3:\n            name = GL_SHADING_LANGUAGE_VERSION;\n            break;\n        default:\n            break;\n        }\n\n        glGetStringFunc glGetString =\n            (glGetStringFunc)(SDL_GL_GetProcAddress(\"glGetString\"));\n\n        if (!glGetString) {\n            ret = caml_copy_string(\"Unable to get OpenGL proc address for glGetString\");\n        } else {\n            const char *sz = (const char *)((void *)glGetString(name));\n            if (!sz) {\n                ret = caml_copy_string(\"(null)\");\n            } else {\n                ret = caml_copy_string(sz);\n            }\n        }\n\n        CAMLreturn(ret);\n    }\n\n    typedef void (*glGetIntegervFunc)(GLenum, GLint*);\n    CAMLprim value resdl_SDL_GL_GetFramebufferBinding(value vInt) {\n        CAMLparam1(vInt);\n\n        GLenum name = GL_FRAMEBUFFER_BINDING;\n        switch (Int_val(vInt)) {\n        case 0:\n            name = GL_FRAMEBUFFER_BINDING;\n            break;\n        default:\n            break;\n        }\n\n        GLint ret = -1;\n        glGetIntegervFunc glGetIntegerv =\n            (glGetIntegervFunc)(SDL_GL_GetProcAddress(\"glGetIntegerv\"));\n        if (glGetIntegerv) {\n            glGetIntegerv(name, &ret);\n        }\n        CAMLreturn(Val_int(ret));\n    }\n\n    CAMLprim value resdl_SDL_GL_MakeCurrent(value vWin, value vContext) {\n        CAMLparam2(vWin, vContext);\n        SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);\n\n        SDL_GLContext ctx = (SDL_GLContext)resdl_unwrapPointer(vContext);\n\n        SDL_GL_MakeCurrent(win, ctx);\n        CAMLreturn(Val_unit);\n    };\n\n    CAMLprim value resdl_SDL_GetClipboardText(value vUnit) {\n        CAMLparam0();\n        CAMLlocal1(ret);\n\n        char *clip = SDL_GetClipboardText();\n        if (clip == NULL) {\n            ret = Val_none;\n        } else {\n            ret = Val_some(caml_copy_string(clip));\n        }\n\n        CAMLreturn(ret);\n    }\n\n    CAMLprim value resdl_SDL_HasClipboardText(value vUnit) {\n        CAMLparam0();\n        SDL_bool res = SDL_HasClipboardText();\n        CAMLreturn(res == SDL_TRUE ? Val_bool(1) : Val_bool(0));\n    }\n\n    CAMLprim value resdl_SDL_SetClipboardText(value vText) {\n        CAMLparam1(vText);\n        SDL_SetClipboardText(String_val(vText));\n        CAMLreturn(Val_unit);\n    }\n\n    CAMLprim value Val_SDL_WindowEvent(int type, int windowID) {\n        CAMLparam0();\n        CAMLlocal2(ret, v);\n\n        v = caml_alloc(1, 0);\n        Store_field(v, 0, Val_int(windowID));\n\n        ret = caml_alloc(1, type);\n        Store_field(ret, 0, v);\n\n        CAMLreturn(ret);\n    }\n\n    CAMLprim value Val_SDL_WindowEventWithData(int type, int windowID, int data1,\n            int data2) {\n        CAMLparam0();\n        CAMLlocal2(ret, v);\n\n        v = caml_alloc(3, 0);\n        Store_field(v, 0, Val_int(windowID));\n        Store_field(v, 1, Val_int(data1));\n        Store_field(v, 2, Val_int(data2));\n\n        ret = caml_alloc(1, type);\n        Store_field(ret, 0, v);\n\n        CAMLreturn(ret);\n    }\n\n    void getNonFocusedMousePosition(SDL_Window *window, int *localMouseX, int *localMouseY) {\n        int globalMouseX, globalMouseY;\n        SDL_GetGlobalMouseState(&globalMouseX, &globalMouseY);\n        int windowX, windowY;\n        SDL_GetWindowPosition(window, &windowX, &windowY);\n        *localMouseX = globalMouseX - windowX;\n        *localMouseY = globalMouseY - windowY;\n    }\n\n    CAMLprim value Val_SDL_Event(SDL_Event *event) {\n        CAMLparam0();\n        CAMLlocal2(v, vInner);\n\n        int tag, mouseButton, mousePosX, mousePosY;\n\n        switch (event->type) {\n        case SDL_QUIT:\n            v = Val_int(0);\n            break;\n        case SDL_MOUSEMOTION:\n            vInner = caml_alloc(3, 0);\n            Store_field(vInner, 0, Val_int(event->motion.windowID));\n            Store_field(vInner, 1, Val_int(event->motion.x));\n            Store_field(vInner, 2, Val_int(event->motion.y));\n\n            v = caml_alloc(1, 0);\n            Store_field(v, 0, vInner);\n            break;\n        case SDL_MOUSEWHEEL:\n            v = caml_alloc(1, 1);\n\n            vInner = caml_alloc(4, 0);\n            Store_field(vInner, 0, Val_int(event->wheel.windowID));\n            Store_field(vInner, 1, Val_int(event->wheel.x));\n            Store_field(vInner, 2, Val_int(event->wheel.y));\n            Store_field(vInner, 3,\n                        Val_bool(event->wheel.direction == SDL_MOUSEWHEEL_FLIPPED));\n\n            Store_field(v, 0, vInner);\n            break;\n        case SDL_MOUSEBUTTONUP:\n        case SDL_MOUSEBUTTONDOWN:\n            if (event->type == SDL_MOUSEBUTTONDOWN)\n                v = caml_alloc(1, 2);\n            else\n                v = caml_alloc(1, 3);\n\n            mouseButton = 0;\n            switch (event->button.button) {\n            case SDL_BUTTON_LEFT:\n                mouseButton = 0;\n                break;\n            case SDL_BUTTON_MIDDLE:\n                mouseButton = 1;\n                break;\n            case SDL_BUTTON_RIGHT:\n                mouseButton = 2;\n                break;\n            case SDL_BUTTON_X1:\n                mouseButton = 3;\n                break;\n            case SDL_BUTTON_X2:\n                mouseButton = 4;\n                break;\n            default:\n                mouseButton = 0;\n                break;\n            }\n\n            vInner = caml_alloc(5, 0);\n            Store_field(vInner, 0, Val_int(event->button.windowID));\n            Store_field(vInner, 1, Val_int(mouseButton));\n            Store_field(vInner, 2, Val_int(event->button.clicks));\n            Store_field(vInner, 3, Val_int(event->button.x));\n            Store_field(vInner, 4, Val_int(event->button.y));\n\n            Store_field(v, 0, vInner);\n            break;\n        case SDL_KEYDOWN:\n        case SDL_KEYUP:\n            v = caml_alloc(1, event->type == SDL_KEYDOWN ? 4 : 5);\n\n            vInner = caml_alloc(5, 0);\n            Store_field(vInner, 0, Val_int(event->key.windowID));\n            Store_field(vInner, 1, Val_bool(event->key.repeat));\n            Store_field(vInner, 2, Val_int(event->key.keysym.mod));\n            Store_field(vInner, 3, Val_int(event->key.keysym.scancode));\n            Store_field(vInner, 4, Val_int(event->key.keysym.sym));\n\n            Store_field(v, 0, vInner);\n            break;\n        case SDL_TEXTINPUT:\n            v = caml_alloc(1, 6);\n\n            vInner = caml_alloc(2, 0);\n            Store_field(vInner, 0, Val_int(event->text.windowID));\n            Store_field(vInner, 1, caml_copy_string(event->text.text));\n\n            Store_field(v, 0, vInner);\n            break;\n        case SDL_TEXTEDITING:\n            v = caml_alloc(1, 7);\n\n            vInner = caml_alloc(4, 0);\n            Store_field(vInner, 0, Val_int(event->edit.windowID));\n            Store_field(vInner, 1, caml_copy_string(event->edit.text));\n            Store_field(vInner, 2, Val_int(event->edit.start));\n            Store_field(vInner, 3, Val_int(event->edit.length));\n\n            Store_field(v, 0, vInner);\n            break;\n        case SDL_PANEVENT:\n            v = caml_alloc(1, 24);\n\n            vInner = caml_alloc(9, 0);\n            Store_field(vInner, 0, Val_int(event->window.windowID));\n            Store_field(vInner, 1, Val_int(event->pan.x));\n            Store_field(vInner, 2, Val_int(event->pan.y));\n            Store_field(vInner, 3, Val_bool(event->pan.contains_x));\n            Store_field(vInner, 4, Val_bool(event->pan.contains_y));\n            Store_field(vInner, 5, Val_bool(event->pan.fling));\n            Store_field(vInner, 6, Val_bool(event->pan.interrupt));\n            // verify this is the correct way of representing a ref to some WheelType.t\n            Store_field(vInner, 7, Val_int(event->pan.source_type));\n            Store_field(vInner, 8, Val_int(event->pan.timestamp));\n\n            Store_field(v, 0, vInner);\n            break;\n        case SDL_DROPTEXT:\n            v = caml_alloc(1, 25);\n            vInner = caml_alloc(5, 0);\n\n            getNonFocusedMousePosition(SDL_GetWindowFromID(event->drop.windowID), &mousePosX, &mousePosY);\n\n            Store_field(vInner, 0, Val_int(event->drop.windowID));\n            Store_field(vInner, 1, caml_copy_string(event->drop.file));\n            Store_field(vInner, 2, Val_int(event->drop.timestamp));\n            Store_field(vInner, 3, Val_int(mousePosX));\n            Store_field(vInner, 4, Val_int(mousePosY));\n\n            Store_field(v, 0, vInner);\n            SDL_free(event->drop.file);\n            break;\n        case SDL_DROPFILE:\n            v = caml_alloc(1, 26);\n            vInner = caml_alloc(5, 0);\n\n            getNonFocusedMousePosition(SDL_GetWindowFromID(event->drop.windowID), &mousePosX, &mousePosY);\n\n            Store_field(vInner, 0, Val_int(event->drop.windowID));\n            Store_field(vInner, 1, caml_copy_string(event->drop.file));\n            Store_field(vInner, 2, Val_int(event->drop.timestamp));\n            Store_field(vInner, 3, Val_int(mousePosX));\n            Store_field(vInner, 4, Val_int(mousePosY));\n\n            Store_field(v, 0, vInner);\n            SDL_free(event->drop.file);\n            break;\n        case SDL_DROPBEGIN:\n            v = caml_alloc(1, 27);\n            vInner = caml_alloc(4, 0);\n\n            getNonFocusedMousePosition(SDL_GetWindowFromID(event->drop.windowID), &mousePosX, &mousePosY);\n\n            Store_field(vInner, 0, Val_int(event->drop.windowID));\n            Store_field(vInner, 1, Val_int(event->drop.timestamp));\n            Store_field(vInner, 2, Val_int(mousePosX));\n            Store_field(vInner, 3, Val_int(mousePosY));\n\n            Store_field(v, 0, vInner);\n            SDL_free(event->drop.file);\n            break;\n        case SDL_DROPCOMPLETE:\n            v = caml_alloc(1, 28);\n            vInner = caml_alloc(4, 0);\n\n            getNonFocusedMousePosition(SDL_GetWindowFromID(event->drop.windowID), &mousePosX, &mousePosY);\n\n            Store_field(vInner, 0, Val_int(event->drop.windowID));\n            Store_field(vInner, 1, Val_int(event->drop.timestamp));\n            Store_field(vInner, 2, Val_int(mousePosX));\n            Store_field(vInner, 3, Val_int(mousePosY));\n\n            Store_field(v, 0, vInner);\n            SDL_free(event->drop.file);\n            break;\n        case SDL_KEYMAPCHANGED:\n            v = Val_int(2);\n            break;\n        case SDL_WINDOWEVENT:\n            switch (event->window.event) {\n            case SDL_WINDOWEVENT_SHOWN:\n                v = Val_SDL_WindowEvent(8, event->window.windowID);\n                break;\n            case SDL_WINDOWEVENT_HIDDEN:\n                v = Val_SDL_WindowEvent(9, event->window.windowID);\n                break;\n            case SDL_WINDOWEVENT_EXPOSED:\n                v = Val_SDL_WindowEvent(10, event->window.windowID);\n                break;\n            case SDL_WINDOWEVENT_MOVED:\n                v = Val_SDL_WindowEventWithData(11, event->window.windowID,\n                                                event->window.data1, event->window.data2);\n                break;\n            case SDL_WINDOWEVENT_RESIZED:\n                v = Val_SDL_WindowEventWithData(12, event->window.windowID,\n                                                event->window.data1, event->window.data2);\n                break;\n            case SDL_WINDOWEVENT_SIZE_CHANGED:\n                v = Val_SDL_WindowEventWithData(13, event->window.windowID,\n                                                event->window.data1, event->window.data2);\n                break;\n            case SDL_WINDOWEVENT_MINIMIZED:\n                v = Val_SDL_WindowEvent(14, event->window.windowID);\n                break;\n            case SDL_WINDOWEVENT_MAXIMIZED:\n                v = Val_SDL_WindowEvent(15, event->window.windowID);\n                break;\n            case SDL_WINDOWEVENT_FULLSCREEN:\n                v = Val_SDL_WindowEvent(29, event->window.windowID);\n                break;\n            case SDL_WINDOWEVENT_RESTORED:\n                v = Val_SDL_WindowEvent(16, event->window.windowID);\n                break;\n            case SDL_WINDOWEVENT_ENTER:\n                v = Val_SDL_WindowEvent(17, event->window.windowID);\n                break;\n            case SDL_WINDOWEVENT_LEAVE:\n                v = Val_SDL_WindowEvent(18, event->window.windowID);\n                break;\n            case SDL_WINDOWEVENT_FOCUS_GAINED:\n                v = Val_SDL_WindowEvent(19, event->window.windowID);\n                break;\n            case SDL_WINDOWEVENT_FOCUS_LOST:\n                v = Val_SDL_WindowEvent(20, event->window.windowID);\n                break;\n            case SDL_WINDOWEVENT_CLOSE:\n                v = Val_SDL_WindowEvent(21, event->window.windowID);\n                break;\n            case SDL_WINDOWEVENT_TAKE_FOCUS:\n                v = Val_SDL_WindowEvent(22, event->window.windowID);\n                break;\n            case SDL_WINDOWEVENT_HIT_TEST:\n                v = Val_SDL_WindowEvent(23, event->window.windowID);\n                break;\n            default:\n                v = Val_int(1);\n            };\n\n            break;\n        default:\n            v = Val_int(1);\n        }\n\n        CAMLreturn(v);\n    };\n\n    CAMLprim value resdl_SDL_PollEvent() {\n        CAMLparam0();\n        CAMLlocal2(ret, evt);\n        SDL_Event e;\n\n        int result = SDL_PollEvent(&e);\n\n        if (result == 0) {\n            ret = Val_none;\n        } else {\n            evt = Val_SDL_Event(&e);\n            ret = Val_some(evt);\n        }\n\n        CAMLreturn(ret);\n    }\n\n    CAMLprim value resdl_SDL_WaitEvent() {\n        CAMLparam0();\n        CAMLlocal2(ret, evt);\n        SDL_Event e;\n\n        int result = SDL_WaitEvent(&e);\n\n        if (result == 1) {\n            evt = Val_SDL_Event(&e);\n            ret = Val_ok(evt);\n        } else {\n            ret = Val_error(caml_copy_string(SDL_GetError()));\n        }\n\n        CAMLreturn(ret);\n    }\n\n    CAMLprim value resdl_SDL_PushEvent() {\n        CAMLparam0();\n\n        SDL_Event user_event;\n        user_event.type = SDL_USEREVENT;\n        SDL_PushEvent(&user_event);\n\n        CAMLreturn(Val_unit);\n    };\n\n    CAMLprim value resdl_SDL_WaitTimeoutEvent(value vTimeout) {\n        CAMLparam1(vTimeout);\n        CAMLlocal2(ret, evt);\n        int timeout = Int_val(vTimeout);\n        SDL_Event e;\n\n        int result = SDL_WaitEventTimeout(&e, timeout);\n\n        if (result == 1) {\n            evt = Val_SDL_Event(&e);\n            ret = Val_some(evt);\n        } else {\n            ret = Val_none;\n        }\n\n        CAMLreturn(ret);\n    }\n\n    CAMLprim value resdl_SDL_GetTicks() {\n        CAMLparam0();\n\n        int result = SDL_GetTicks();\n\n        CAMLreturn(Val_int(result));\n    }\n\n    CAMLprim value resdl_SDL_GetWindowSize(value vWindow) {\n        CAMLparam1(vWindow);\n        CAMLlocal1(ret);\n        SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWindow);\n        int width, height = 0;\n        SDL_GetWindowSize(win, &width, &height);\n        ret = caml_alloc(2, 0);\n        Store_field(ret, 0, Val_int(width));\n        Store_field(ret, 1, Val_int(height));\n        CAMLreturn(ret);\n    }\n\n    CAMLprim value resdl_SDL_GetWindowPosition(value vWindow) {\n        CAMLparam1(vWindow);\n        CAMLlocal1(position);\n\n        SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWindow);\n        int x, y = 0;\n\n        SDL_GetWindowPosition(win, &x, &y);\n\n        position = caml_alloc(2, 0);\n        Store_field(position, 0, Val_int(x));\n        Store_field(position, 1, Val_int(y));\n\n        CAMLreturn(position);\n    }\n\n    CAMLprim value resdl_SDL_GL_GetDrawableSize(value vWindow) {\n        CAMLparam1(vWindow);\n        CAMLlocal1(ret);\n        SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWindow);\n        int width, height = 0;\n        SDL_GL_GetDrawableSize(win, &width, &height);\n        ret = caml_alloc(2, 0);\n        Store_field(ret, 0, Val_int(width));\n        Store_field(ret, 1, Val_int(height));\n        CAMLreturn(ret);\n    }\n\n    CAMLprim value resdl_SDL_SetWindowIcon(value vWindow, value vIcon) {\n        CAMLparam2(vWindow, vIcon);\n\n        SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWindow);\n        SDL_Surface *surface = (SDL_Surface *)resdl_unwrapPointer(vIcon);\n        SDL_SetWindowIcon(win, surface);\n\n        CAMLreturn(Val_unit);\n    };\n\n    CAMLprim value resdl_SDL_SetWindowTransparency(value vWindow,\n            value vTransparency) {\n        CAMLparam2(vWindow, vTransparency);\n\n        SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWindow);\n        double transparency = Double_val(vTransparency);\n\n        int result;\n        result = SDL_SetWindowOpacity(win, transparency);\n\n        if (result == -1) {\n            printf(\"WARNING: Setting transparency not supported!\");\n        }\n\n        CAMLreturn(Val_unit);\n    }\n\n    CAMLprim value resdl_SDL_CreateSystemCursor(value vCursor) {\n        CAMLparam1(vCursor);\n        CAMLlocal1(vCursorRet);\n\n        SDL_Cursor *cursor = NULL;\n\n        SDL_SystemCursor id = SDL_SYSTEM_CURSOR_ARROW;\n        switch (Int_val(vCursor)) {\n        case 1:\n            id = SDL_SYSTEM_CURSOR_IBEAM;\n            break;\n        case 2:\n            id = SDL_SYSTEM_CURSOR_WAIT;\n            break;\n        case 3:\n            id = SDL_SYSTEM_CURSOR_CROSSHAIR;\n            break;\n        case 4:\n            id = SDL_SYSTEM_CURSOR_WAITARROW;\n            break;\n        case 5:\n            id = SDL_SYSTEM_CURSOR_SIZENWSE;\n            break;\n        case 6:\n            id = SDL_SYSTEM_CURSOR_SIZENESW;\n            break;\n        case 7:\n            id = SDL_SYSTEM_CURSOR_SIZEWE;\n            break;\n        case 8:\n            id = SDL_SYSTEM_CURSOR_SIZENS;\n            break;\n        case 9:\n            id = SDL_SYSTEM_CURSOR_SIZEALL;\n            break;\n        case 10:\n            id = SDL_SYSTEM_CURSOR_NO;\n            break;\n        case 11:\n            id = SDL_SYSTEM_CURSOR_HAND;\n            break;\n        default:\n            id = SDL_SYSTEM_CURSOR_ARROW;\n            break;\n        }\n\n        cursor = SDL_CreateSystemCursor(id);\n        vCursorRet = resdl_wrapPointer(cursor);\n        CAMLreturn(vCursorRet);\n    }\n\n    CAMLprim value resdl_SDL_StartTextInput() {\n        SDL_StartTextInput();\n        return Val_unit;\n    }\n\n    CAMLprim value resdl_SDL_StopTextInput() {\n        SDL_StopTextInput();\n        return Val_unit;\n    }\n\n    CAMLprim value resdl_SDL_SetTextInputRect(value vX, value vY, value vWidth,\n            value vHeight) {\n        int x = Int_val(vX);\n        int y = Int_val(vY);\n        int width = Int_val(vWidth);\n        int height = Int_val(vHeight);\n\n        SDL_Rect rect;\n        rect.x = x;\n        rect.y = y;\n        rect.w = width;\n        rect.h = height;\n\n        SDL_SetTextInputRect(&rect);\n        return Val_unit;\n    }\n\n    CAMLprim value resdl_SDL_IsTextInputActive() {\n        return Val_bool(SDL_IsTextInputActive());\n    }\n\n    CAMLprim value resdl_SDL_SetCursor(value vCursor) {\n        CAMLparam1(vCursor);\n        SDL_Cursor *cursor = (SDL_Cursor *)resdl_unwrapPointer(vCursor);\n        SDL_SetCursor(cursor);\n        CAMLreturn(Val_unit);\n    }\n\n    static SDL_AudioFormat SDL_AudioFormat_val(value tag) {\n        switch (Int_val(tag)) {\n        case 0:\n            return AUDIO_S8;\n        case 1:\n            return AUDIO_U8;\n        case 2:\n            return AUDIO_S16LSB;\n        case 3:\n            return AUDIO_S16MSB;\n        case 4:\n            return AUDIO_U16LSB;\n        case 5:\n            return AUDIO_U16MSB;\n        case 6:\n            return AUDIO_S32LSB;\n        case 7:\n            return AUDIO_S32MSB;\n        case 8:\n            return AUDIO_F32LSB;\n        case 9:\n            return AUDIO_F32MSB;\n        default:\n            return 0;\n        }\n    }\n\n    CAMLprim value Val_SDL_AudioFormat(SDL_AudioFormat format) {\n        CAMLparam0();\n        int tag;\n        switch (format) {\n        case AUDIO_S8:\n            tag = 0;\n            break;\n        case AUDIO_U8:\n            tag = 1;\n            break;\n        case AUDIO_S16LSB:\n            tag = 2;\n            break;\n        case AUDIO_S16MSB:\n            tag = 3;\n            break;\n        case AUDIO_U16LSB:\n            tag = 4;\n            break;\n        case AUDIO_U16MSB:\n            tag = 5;\n            break;\n        case AUDIO_S32LSB:\n            tag = 6;\n            break;\n        case AUDIO_S32MSB:\n            tag = 7;\n            break;\n        case AUDIO_F32LSB:\n            tag = 8;\n            break;\n        case AUDIO_F32MSB:\n            tag = 9;\n            break;\n        }\n        CAMLreturn(Val_int(tag));\n    }\n\n    CAMLprim value Val_SDL_AudioSpec(SDL_AudioSpec spec) {\n        CAMLparam0();\n        CAMLlocal1(ret);\n        ret = caml_alloc(7, 0);\n        Store_field(ret, 0, Val_int(spec.freq));\n        Store_field(ret, 1, Val_SDL_AudioFormat(spec.format));\n        Store_field(ret, 2, Val_int(spec.channels));\n        Store_field(ret, 3, Val_int(spec.silence));\n        Store_field(ret, 4, Val_int(spec.samples));\n        Store_field(ret, 5, Val_int(spec.padding));\n        Store_field(ret, 6, Val_int(spec.size));\n        CAMLreturn(ret);\n    }\n\n    CAMLprim value resdl_SDL_LoadWAV(value vPath) {\n        CAMLparam1(vPath);\n        CAMLlocal3(ret, vSpec, vTup3);\n        SDL_AudioSpec spec;\n        Uint8 *audioBuf;\n        Uint32 audioLen;\n        SDL_LoadWAV(String_val(vPath), &spec, &audioBuf, &audioLen);\n        if (audioBuf == NULL) {\n            ret = Val_error(caml_copy_string(SDL_GetError()));\n            CAMLreturn(ret);\n        }\n        vSpec = Val_SDL_AudioSpec(spec);\n        vTup3 = caml_alloc_tuple(3);\n        Store_field(vTup3, 0, vSpec);\n        Store_field(vTup3, 1, resdl_wrapPointer(audioBuf));\n        Store_field(vTup3, 2, Val_int(audioLen));\n        ret = Val_ok(vTup3);\n        CAMLreturn(ret);\n    }\n\n    CAMLprim value resdl_SDL_OpenAudioDevice(\n        value vDeviceNameOpt,\n        value vIsCapture,\n        value vWant,\n        value vAllowedChanges) {\n        CAMLparam4(vDeviceNameOpt, vIsCapture, vWant, vAllowedChanges);\n        CAMLlocal3(ret, vHave, vTup2);\n        const char *deviceName = Is_block(vDeviceNameOpt)\n                                 ? String_val(Field(vDeviceNameOpt, 0))\n                                 : NULL;\n        int isCapture = Bool_val(vIsCapture);\n        SDL_AudioSpec want = {0};\n        want.freq = Int_val(Field(vWant, 0));\n        want.format = SDL_AudioFormat_val(Field(vWant, 1));\n        want.channels = Int_val(Field(vWant, 2));\n        want.silence = Int_val(Field(vWant, 3));\n        want.samples = Int_val(Field(vWant, 4));\n        want.padding = Int_val(Field(vWant, 5));\n        want.size = Int_val(Field(vWant, 6));\n        SDL_AudioSpec have = {0};\n        int allowedChanges = Int_val(vAllowedChanges);\n        SDL_AudioDeviceID device = SDL_OpenAudioDevice(\n                                       deviceName,\n                                       isCapture,\n                                       &want,\n                                       &have,\n                                       allowedChanges);\n        if (device == 0) {\n            ret = Val_error(caml_copy_string(SDL_GetError()));\n            CAMLreturn(ret);\n        }\n        vHave = Val_SDL_AudioSpec(have);\n        vTup2 = caml_alloc_tuple(2);\n        Store_field(vTup2, 0, Val_int(device));\n        Store_field(vTup2, 1, vHave);\n        ret = Val_ok(vTup2);\n        CAMLreturn(ret);\n    }\n\n    CAMLprim value resdl_SDL_CloseAudioDevice(value vDevice) {\n        CAMLparam1(vDevice);\n        SDL_AudioDeviceID device = Int_val(vDevice);\n        SDL_CloseAudioDevice(device);\n        CAMLreturn(Val_unit);\n    }\n\n    CAMLprim value resdl_SDL_GetAudioDeviceStatus(value vDevice) {\n        CAMLparam1(vDevice);\n        SDL_AudioDeviceID device = Int_val(vDevice);\n        SDL_AudioStatus status = SDL_GetAudioDeviceStatus(device);\n        CAMLreturn(Val_int(status));\n    }\n\n    CAMLprim value resdl_SDL_PauseAudioDevice(value vDevice, value vPauseOn) {\n        CAMLparam2(vDevice, vPauseOn);\n        SDL_AudioDeviceID device = Int_val(vDevice);\n        int pauseOn = Bool_val(vPauseOn);\n        SDL_PauseAudioDevice(device, pauseOn);\n        CAMLreturn(Val_unit);\n    }\n\n    CAMLprim value resdl_SDL_QueueAudio(value vDevice, value vBuf, value vLen) {\n        CAMLparam3(vDevice, vBuf, vLen);\n        CAMLlocal1(ret);\n        SDL_AudioDeviceID device = Int_val(vDevice);\n        const void *buf = resdl_unwrapPointer(vBuf);\n        Uint32 len = Int_val(vLen);\n        if (SDL_QueueAudio(device, buf, len) < 0) {\n            ret = Val_error(caml_copy_string(SDL_GetError()));\n            CAMLreturn(ret);\n        }\n        ret = Val_ok(Val_unit);\n        CAMLreturn(ret);\n    }\n\n    CAMLprim value resdl_SDL_ClearQueuedAudio(value vDevice) {\n        CAMLparam1(vDevice);\n        SDL_AudioDeviceID device = Int_val(vDevice);\n        SDL_ClearQueuedAudio(device);\n        CAMLreturn(Val_unit);\n    }\n\n    CAMLprim value resdl_SDL_GetQueuedAudioSize(value vDevice) {\n        CAMLparam1(vDevice);\n        SDL_AudioDeviceID device = Int_val(vDevice);\n        Uint32 bytes = SDL_GetQueuedAudioSize(device);\n        CAMLreturn(Val_int(bytes));\n    }\n\n    CAMLprim value resdl_SDL_CreateRGBSurfaceFromImage(value vPath) {\n        CAMLparam1(vPath);\n        CAMLlocal1(ret);\n        // FROM:\n        // https://wiki.libsdl.org/SDL_CreateRGBSurfaceFrom\n\n        int req_format = STBI_rgb_alpha;\n        int width, height, orig_format;\n        unsigned char *data =\n            stbi_load(String_val(vPath), &width, &height, &orig_format, req_format);\n        if (data == NULL) {\n\n            ret = Val_error(caml_copy_string(stbi_failure_reason()));\n        } else {\n\n            // Set up the pixel format color masks for RGB(A) byte arrays.\n            // Only STBI_rgb (3) and STBI_rgb_alpha (4) are supported here!\n            Uint32 rmask, gmask, bmask, amask;\n#if SDL_BYTEORDER == SDL_BIG_ENDIAN\n            int shift = (req_format == STBI_rgb) ? 8 : 0;\n            rmask = 0xff000000 >> shift;\n            gmask = 0x00ff0000 >> shift;\n            bmask = 0x0000ff00 >> shift;\n            amask = 0x000000ff >> shift;\n#else // little endian, like x86\n            rmask = 0x000000ff;\n            gmask = 0x0000ff00;\n            bmask = 0x00ff0000;\n            amask = (req_format == STBI_rgb) ? 0 : 0xff000000;\n#endif\n\n            int depth, pitch;\n            if (req_format == STBI_rgb) {\n                depth = 24;\n                pitch = 3 * width; // 3 bytes per pixel * pixels per row\n            } else {             // STBI_rgb_alpha (RGBA)\n                depth = 32;\n                pitch = 4 * width;\n            }\n\n            SDL_Surface *surf = SDL_CreateRGBSurfaceFrom(\n                                    (void *)data, width, height, depth, pitch, rmask, gmask, bmask, amask);\n            if (surf == NULL) {\n                ret = Val_error(caml_copy_string(SDL_GetError()));\n                stbi_image_free(data);\n            } else {\n                ret = Val_ok(resdl_wrapPointer(surf));\n            }\n        }\n\n        CAMLreturn(ret);\n    };\n\n    CAMLprim value resdl_SDL_GL_SwapWindow(value vWin) {\n        CAMLparam1(vWin);\n        SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);\n        caml_release_runtime_system();\n        SDL_GL_SwapWindow(win);\n        caml_acquire_runtime_system();\n        CAMLreturn(Val_unit);\n    }\n\n    SDL_HitTestResult hittest(SDL_Window *win, const SDL_Point *area, void *data) {\n        return SDL_HITTEST_DRAGGABLE;\n    }\n\n    CAMLprim value resdl_SDL_SetWindowSize(value vWin, value vW, value vH) {\n        CAMLparam3(vWin, vW, vH);\n\n        SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);\n        int w = Int_val(vW);\n        int h = Int_val(vH);\n        SDL_SetWindowSize(win, w, h);\n\n        CAMLreturn(Val_unit);\n    }\n\n    CAMLprim value resdl_SDL_SetWindowMinimumSize(value vWin, value vW, value vH) {\n        CAMLparam3(vWin, vW, vH);\n\n        SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);\n        int w = Int_val(vW);\n        int h = Int_val(vH);\n        SDL_SetWindowMinimumSize(win, w, h);\n\n        CAMLreturn(Val_unit);\n    }\n\n    CAMLprim value resdl_SDL_SetWindowTitle(value vWin, value vTitle) {\n        CAMLparam2(vWin, vTitle);\n\n        SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);\n        const char *title = (const char *)String_val(vTitle);\n        SDL_SetWindowTitle(win, title);\n\n        CAMLreturn(Val_unit);\n    }\n\n    CAMLprim value resdl_SDL_SetWindowPosition(value vWin, value vX, value vY) {\n        CAMLparam3(vWin, vX, vY);\n\n        SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);\n        int x = Int_val(vX);\n        int y = Int_val(vY);\n        SDL_SetWindowPosition(win, x, y);\n\n        CAMLreturn(Val_unit);\n    }\n\n    CAMLprim value resdl_SDL_WindowCenter(value vWin) {\n        CAMLparam1(vWin);\n\n        SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);\n        SDL_SetWindowPosition(win, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);\n\n        CAMLreturn(Val_unit);\n    }\n\n    int resdl_eventWatcher(void *data, SDL_Event *event) {\n        if (event->type == SDL_WINDOWEVENT &&\n                event->window.event == SDL_WINDOWEVENT_RESIZED) {\n            value args[] = {Val_unit};\n\n            static const value *resizeCallback = NULL;\n            if (resizeCallback == NULL) {\n                resizeCallback = caml_named_value(\"__sdl2_caml_resize__\");\n            }\n\n            if (resizeCallback) {\n                caml_callbackN(*resizeCallback, 1, args);\n            }\n        }\n\n        return 0;\n    }\n\n    CAMLprim value resdl_SDL_CreateWindow(value vName, value vX, value vY,\n                                          value vWidth, value vHeight, value vAcceleration) {\n        CAMLparam5(vName, vX, vY, vWidth, vHeight);\n        CAMLxparam1(vAcceleration);\n        CAMLlocal1(vWindow);\n\n        int x;\n        if (vX == hash_variant(\"Centered\")) {\n            x = SDL_WINDOWPOS_CENTERED;\n        } else if (Is_block(vX) && Field(vX, 0) == hash_variant(\"Absolute\")) {\n            x = Int_val(Field(vX, 1));\n        } else {\n            x = SDL_WINDOWPOS_UNDEFINED;\n        };\n\n        int y;\n        if (vY == hash_variant(\"Centered\")) {\n            y = SDL_WINDOWPOS_CENTERED;\n        } else if (Is_block(vY) && Field(vY, 0) == hash_variant(\"Absolute\")) {\n            y = Int_val(Field(vY, 1));\n        } else {\n            y = SDL_WINDOWPOS_UNDEFINED;\n        };\n\n        int width = Int_val(vWidth);\n        int height = Int_val(vHeight);\n\n        // According to the docs - `SDL_GL_SetAttribute` needs\n        // to be called prior to creating the window.\n\n        // Attributes pulled from:\n        // https://github.com/google/skia/blob/master/example/SkiaSDLExample.cpp\n        static const int kStencilBits = 8; // Skia needs 8 stencil bits\n#ifdef WIN32\n        SDL_SetHint(SDL_HINT_OPENGL_ES_DRIVER, \"1\");\n        SDL_GL_SetAttribute(SDL_GL_CONTEXT_EGL, 1);\n        SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);\n#elif SDL_VIDEO_OPENGL\n        SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);\n#else\n        SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);\n#endif\n\n        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);\n        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);\n\n#ifdef SDL_VIDEO_DRIVER_X11\n        // Disable compositing suppression - https://github.com/onivim/oni2/issues/2003\n        SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, \"0\");\n#endif\n        SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);\n        SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);\n        SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);\n        SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);\n        SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);\n        SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0);\n        SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, kStencilBits);\n\n        // If vAcceleration is Auto, don't set SDL_GL_ACCELERATED_VISUAL at all.\n        if (vAcceleration == hash_variant(\"ForceHardware\")) {\n            SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);\n        } else if (vAcceleration == hash_variant(\"ForceSoftware\")) {\n            SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 0);\n        }\n\n        SDL_Window *win = (SDL_CreateWindow(\n                               String_val(vName), x, y, width, height,\n                               SDL_WINDOW_OPENGL | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_RESIZABLE));\n\n        if (!win) {\n            SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, \"SDL_CreateWindow failed: %s\\n\",\n                            SDL_GetError());\n        }\n\n// Only add the resize-event-watcher for OSX, since it's the platform that requires this to get\n// real-time resize notifications + re-renders.\n// This change caused a crash in Linux + Wayland: https://github.com/onivim/oni2/issues/3646\n// (with the crash callstack pointing to an error acquiring runtime lock after rendering)\n#ifdef __APPLE__\n        SDL_AddEventWatch(resdl_eventWatcher, NULL);\n#endif\n\n        vWindow = resdl_wrapPointer(win);\n        CAMLreturn(vWindow);\n    }\n\n    CAMLprim value resdl_SDL_CreateWindow_byte(value *argv, int argn) {\n        return resdl_SDL_CreateWindow(\n                   argv[0],\n                   argv[1],\n                   argv[2],\n                   argv[3],\n                   argv[4],\n                   argv[5]\n               );\n    }\n\n    CAMLprim value resdl_SDL_SetWindowBordered(value vWin, value vBordered) {\n        CAMLparam2(vWin, vBordered);\n\n        SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);\n        SDL_bool bordered = Int_val(vBordered) == 1 ? SDL_TRUE : SDL_FALSE;\n\n        SDL_SetWindowBordered(win, bordered);\n        CAMLreturn(Val_unit);\n    }\n\n    CAMLprim value resdl_SDL_SetWindowResizable(value vWin, value vResizable) {\n        CAMLparam2(vWin, vResizable);\n\n        SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);\n        SDL_bool resize = Int_val(vResizable) == 1 ? SDL_TRUE : SDL_FALSE;\n\n        SDL_SetWindowResizable(win, resize);\n        CAMLreturn(Val_unit);\n    }\n\n    CAMLprim value resdl_SDL_HideWindow(value vWin) {\n        CAMLparam1(vWin);\n\n        SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);\n        SDL_HideWindow(win);\n\n        CAMLreturn(Val_unit);\n    }\n\n    CAMLprim value resdl_SDL_MaximizeWindow(value vWin) {\n        CAMLparam1(vWin);\n\n        SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);\n        SDL_MaximizeWindow(win);\n\n        CAMLreturn(Val_unit);\n    }\n\n    CAMLprim value resdl_SDL_IsWindowMaximized(value vWin) {\n        CAMLparam1(vWin);\n\n        SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);\n        Uint32 flags = SDL_GetWindowFlags(win);\n        bool hasMaximizedFlag = (flags & SDL_WINDOW_MAXIMIZED) != 0;\n\n        CAMLreturn(Val_bool(hasMaximizedFlag));\n    }\n\n    CAMLprim value resdl_SDL_IsWindowFullscreen(value vWin) {\n        CAMLparam1(vWin);\n        // SDL's fullscreen window flags don't work on macOS\n        SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);\n        bool isFullscreen;\n#ifdef SDL_VIDEO_DRIVER_COCOA\n        SDL_SysWMinfo wmInfo;\n        SDL_VERSION(&wmInfo.version);\n        SDL_GetWindowWMInfo(win, &wmInfo);\n        NSWindow *nWindow = wmInfo.info.cocoa.window;\n        isFullscreen = (([nWindow styleMask] & NSWindowStyleMaskFullScreen) == NSWindowStyleMaskFullScreen);\n#else\n        Uint32 flags = SDL_GetWindowFlags(win);\n        isFullscreen = (flags & SDL_WINDOW_FULLSCREEN) != 0;\n#endif\n\n        CAMLreturn(Val_bool(isFullscreen));\n    }\n\n    CAMLprim value resdl_SDL_MinimizeWindow(value vWin) {\n        CAMLparam1(vWin);\n\n        SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);\n        SDL_MinimizeWindow(win);\n\n        CAMLreturn(Val_unit);\n    }\n\n    CAMLprim value resdl_SDL_RaiseWindow(value vWin) {\n        CAMLparam1(vWin);\n\n        SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);\n        SDL_RaiseWindow(win);\n\n        CAMLreturn(Val_unit);\n    }\n\n    CAMLprim value resdl_SDL_RestoreWindow(value vWin) {\n        CAMLparam1(vWin);\n\n        SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);\n        SDL_RestoreWindow(win);\n\n        CAMLreturn(Val_unit);\n    }\n\n    CAMLprim value resdl_SDL_ShowWindow(value vWin) {\n        CAMLparam1(vWin);\n\n        SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWin);\n        SDL_ShowWindow(win);\n\n        CAMLreturn(Val_unit);\n    }\n\n    CAMLprim value resdl_SDL_GetWindowId(value vWindow) {\n        CAMLparam1(vWindow);\n        SDL_Window *win = (SDL_Window *)resdl_unwrapPointer(vWindow);\n        int id = SDL_GetWindowID(win);\n        CAMLreturn(Val_int(id));\n    }\n\n    void resdl_onLog(void *unused, int category, SDL_LogPriority priority,\n                     const char *message) {\n        CAMLparam0();\n        CAMLlocal1(messageString);\n\n        static const value *reason_sdl_onLog = NULL;\n\n        if (reason_sdl_onLog == NULL) {\n            reason_sdl_onLog = caml_named_value(\"reason_sdl2_onLog\");\n        }\n\n        int iCategory, iPriority;\n\n        switch (category) {\n        case SDL_LOG_CATEGORY_APPLICATION:\n            iCategory = 0;\n            break;\n        case SDL_LOG_CATEGORY_ERROR:\n            iCategory = 1;\n            break;\n        case SDL_LOG_CATEGORY_ASSERT:\n            iCategory = 2;\n            break;\n        case SDL_LOG_CATEGORY_SYSTEM:\n            iCategory = 3;\n            break;\n        case SDL_LOG_CATEGORY_AUDIO:\n            iCategory = 4;\n            break;\n        case SDL_LOG_CATEGORY_VIDEO:\n            iCategory = 5;\n            break;\n        case SDL_LOG_CATEGORY_RENDER:\n            iCategory = 6;\n            break;\n        case SDL_LOG_CATEGORY_INPUT:\n            iCategory = 7;\n            break;\n        case SDL_LOG_CATEGORY_TEST:\n            iCategory = 8;\n        case SDL_LOG_CATEGORY_CUSTOM:\n            iCategory = 9;\n            break;\n        default:\n            iCategory = 10;\n            break;\n        }\n\n        switch (priority) {\n        case SDL_LOG_PRIORITY_VERBOSE:\n            iPriority = 0;\n            break;\n        case SDL_LOG_PRIORITY_DEBUG:\n            iPriority = 1;\n            break;\n        case SDL_LOG_PRIORITY_INFO:\n            iPriority = 2;\n            break;\n        case SDL_LOG_PRIORITY_WARN:\n            iPriority = 3;\n            break;\n        case SDL_LOG_PRIORITY_ERROR:\n            iPriority = 4;\n            break;\n        case SDL_LOG_PRIORITY_CRITICAL:\n            iPriority = 5;\n            break;\n        default:\n            iPriority = 0;\n            break;\n        }\n\n        messageString = caml_copy_string(message);\n        caml_callback3(*reason_sdl_onLog, Val_int(iCategory), Val_int(iPriority),\n                       messageString);\n\n        CAMLreturn0;\n    }\n\n    CAMLprim value resdl_SDL_Init() {\n        CAMLparam0();\n\n        SDL_LogSetOutputFunction(&resdl_onLog, NULL);\n\n        int ret = SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO);\n\n        if (ret < 0) {\n            SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, \"SDL_Init failed: %s\\n\",\n                            SDL_GetError());\n        }\n\n        CAMLreturn(Val_int(ret));\n    }\n\n#if SDL_VIDEO_DRIVER_UIKIT\n    value resdl_uikit_main_callback;\n    int resdl_uikit_SDL_main (int argc, char *argv[]) {\n        caml_callback(resdl_uikit_main_callback, Val_unit);\n        return 0;\n    }\n#endif\n\n    CAMLprim value resdl_SDL_main(value ml_argc, value ml_argv, value closure) {\n        CAMLparam3(ml_argc, ml_argv, closure);\n        CAMLlocal1(tmp);\n\n#if SDL_VIDEO_DRIVER_UIKIT\n        int argc = Int_val(ml_argc);\n        char **argv = (char**)calloc(sizeof(char*), argc + 1);\n        for (int i = 0; i < argc; i++) {\n            argv[i] = (char*)String_val(Field(ml_argv, i));\n        }\n\n        resdl_uikit_main_callback = closure;\n        SDL_UIKitRunApp(argc, argv, resdl_uikit_SDL_main);\n#else\n        caml_callback(closure, Val_unit);\n#endif\n\n        CAMLreturn(Val_unit);\n    }\n\n    CAMLprim value resdl_SDL_GetScancodeName(value vScancode) {\n        CAMLparam1(vScancode);\n        CAMLlocal1(ret);\n\n        SDL_Scancode scancode = (SDL_Scancode)Int_val(vScancode);\n        ret = caml_copy_string(SDL_GetScancodeName(scancode));\n        CAMLreturn(ret);\n    }\n\n    CAMLprim value resdl_SDL_GetKeyFromName(value vKeyName) {\n        CAMLparam1(vKeyName);\n\n        int keycode = (int)SDL_GetKeyFromName(String_val(vKeyName));\n\n        CAMLreturn(Val_int(keycode));\n    }\n\n    CAMLprim value resdl_SDL_GetScancodeFromName(value vScancodeName) {\n        CAMLparam1(vScancodeName);\n\n        int scancode = (int)SDL_GetScancodeFromName(String_val(vScancodeName));\n\n        CAMLreturn(Val_int(scancode));\n    }\n\n    CAMLprim value resdl_SDL_GetKeyName(value vKey) {\n        CAMLparam1(vKey);\n        CAMLlocal1(ret);\n\n        SDL_Keycode key = (SDL_Keycode)Int_val(vKey);\n        ret = caml_copy_string(SDL_GetKeyName(key));\n        CAMLreturn(ret);\n    }\n\n    CAMLprim value resdl_SDL_GetKeyFromScancode(value vScancode) {\n        SDL_Scancode scancode = (SDL_Scancode)Int_val(vScancode);\n        return Val_int(SDL_GetKeyFromScancode(scancode));\n    }\n\n    CAMLprim value resdl_SDL_GetScancodeFromKey(value vKey) {\n        SDL_Keycode key = (SDL_Keycode)Int_val(vKey);\n        return Val_int(SDL_GetScancodeFromKey(key));\n    }\n\n    CAMLprim value resdl_SDL_ModLeftShift(value vMod) {\n        return Val_bool((Int_val(vMod) & KMOD_LSHIFT) == KMOD_LSHIFT);\n    };\n\n    CAMLprim value resdl_SDL_ModRightShift(value vMod) {\n        return Val_bool((Int_val(vMod) & KMOD_RSHIFT) == KMOD_RSHIFT);\n    };\n\n    CAMLprim value resdl_SDL_ModLeftControl(value vMod) {\n        return Val_bool((Int_val(vMod) & KMOD_LCTRL) == KMOD_LCTRL);\n    };\n\n    CAMLprim value resdl_SDL_ModRightControl(value vMod) {\n        return Val_bool((Int_val(vMod) & KMOD_RCTRL) == KMOD_RCTRL);\n    };\n\n    CAMLprim value resdl_SDL_ModLeftAlt(value vMod) {\n        return Val_bool((Int_val(vMod) & KMOD_LALT) == KMOD_LALT);\n    };\n\n    CAMLprim value resdl_SDL_ModRightAlt(value vMod) {\n        return Val_bool((Int_val(vMod) & KMOD_RALT) == KMOD_RALT);\n    };\n\n    CAMLprim value resdl_SDL_ModLeftGui(value vMod) {\n        return Val_bool((Int_val(vMod) & KMOD_LGUI) == KMOD_LGUI);\n    };\n\n    CAMLprim value resdl_SDL_ModRightGui(value vMod) {\n        return Val_bool((Int_val(vMod) & KMOD_RGUI) == KMOD_RGUI);\n    };\n\n    CAMLprim value resdl_SDL_ModNumLockDown(value vMod) {\n        return Val_bool((Int_val(vMod) & KMOD_NUM) == KMOD_NUM);\n    };\n\n    CAMLprim value resdl_SDL_ModCapsLockDown(value vMod) {\n        return Val_bool((Int_val(vMod) & KMOD_CAPS) == KMOD_CAPS);\n    };\n\n    CAMLprim value resdl_SDL_ModAltGrDown(value vMod) {\n        return Val_bool((Int_val(vMod) & KMOD_MODE) == KMOD_MODE);\n    };\n\n    CAMLprim value resdl_SDL_GetModState(value vUnit) {\n        return Val_int(SDL_GetModState());\n    };\n\n    CAMLprim value resdl_SDL_SetModState(value vMod) {\n        SDL_SetModState((SDL_Keymod)Int_val(vMod));\n        return Val_unit;\n    };\n\n    CAMLprim value resdl_SDL_GetCompiledVersion(value vUnit) {\n        CAMLparam0();\n        CAMLlocal1(ret);\n\n        SDL_version compiled;\n        SDL_VERSION(&compiled);\n\n        ret = caml_alloc(3, 0);\n        Store_field(ret, 0, Val_int(compiled.major));\n        Store_field(ret, 1, Val_int(compiled.minor));\n        Store_field(ret, 2, Val_int(compiled.patch));\n\n        CAMLreturn(ret);\n    }\n\n    CAMLprim value resdl_SDL_GetLinkedVersion(value vUnit) {\n        CAMLparam0();\n        CAMLlocal1(ret);\n\n        SDL_version linked;\n        SDL_GetVersion(&linked);\n\n        ret = caml_alloc(3, 0);\n        Store_field(ret, 0, Val_int(linked.major));\n        Store_field(ret, 1, Val_int(linked.minor));\n        Store_field(ret, 2, Val_int(linked.patch));\n\n        CAMLreturn(ret);\n    }\n\n    CAMLprim value resdl_SDL_ShowSimpleMessageBox(value vFlags, value vTitle,\n            value vMessage, value vWindow) {\n        CAMLparam4(vFlags, vTitle, vMessage, vWindow);\n        int flags = SDL_MESSAGEBOX_INFORMATION;\n\n        switch (Int_val(vFlags)) {\n        case 0:\n            flags = SDL_MESSAGEBOX_ERROR;\n            break;\n        case 1:\n            flags = SDL_MESSAGEBOX_WARNING;\n            break;\n        default:\n            flags = SDL_MESSAGEBOX_INFORMATION;\n            break;\n        }\n\n        const char *title = String_val(vTitle);\n        const char *msg = String_val(vMessage);\n\n        SDL_Window *win = NULL;\n\n        if (Is_block(vWindow)) {\n            win = (SDL_Window *)resdl_unwrapPointer(Field(vWindow, 0));\n        }\n\n        SDL_ShowSimpleMessageBox(flags, title, msg, win);\n\n        CAMLreturn(Val_unit);\n    }\n\n    CAMLprim value resdl_SDL_CaptureMouse(value vEnabled) {\n        CAMLparam0();\n        SDL_bool enabled = Int_val(vEnabled) == 1 ? SDL_TRUE : SDL_FALSE;\n        CAMLreturn(Val_int(SDL_CaptureMouse(enabled)));\n    }\n\n    CAMLprim value resdl_SDL_GetGlobalMouseState(value vUnit) {\n        CAMLparam0();\n        CAMLlocal1(ret);\n\n        int x, y;\n        SDL_GetGlobalMouseState(&x, &y);\n\n        ret = caml_alloc(2, 0);\n        Store_field(ret, 0, Val_int(x));\n        Store_field(ret, 1, Val_int(y));\n\n        CAMLreturn(ret);\n    }\n\n    CAMLprim value resdl_PassThrough(value v) {\n        return v;\n    };\n\n    CAMLprim value resdl__javascript__renderloop() {\n        return Val_unit;\n    }\n};\n"
  },
  {
    "path": "packages/reason-sdl2/src/stb_image.cpp",
    "content": "#define STB_IMAGE_IMPLEMENTATION\n#include \"stb_image.h\"\n"
  },
  {
    "path": "packages/reason-skia/bench/BenchFramework.re",
    "content": "include Reperf.Make({\n  let config = Reperf.Config.create(~snapshotDir=\"bench/__snapshots__\", ());\n});\n"
  },
  {
    "path": "packages/reason-skia/bench/CanvasBench.re",
    "content": "open BenchFramework;\n\nopen Skia;\n\nlet options = Reperf.Options.create(~iterations=100000, ());\n\nmodule Data = {\n  let makeSurface = (width, height) => {\n    let imageInfo = ImageInfo.make(width, height, Rgba8888, Premul, None);\n    Surface.makeRaster(imageInfo, 0, None);\n  };\n\n  let surface = makeSurface(800l, 600l) |> Option.get;\n  let canvas = Surface.getCanvas(surface);\n  let paint = Paint.make();\n};\n\nlet drawRectLtwh = () => {\n  Skia.Canvas.drawRectLtwh(Data.canvas, 1.0, 2.0, 100., 200., Data.paint);\n};\n\nbench(\n  ~name=\"Canvas: drawRectLtwh\",\n  ~options,\n  ~setup=() => (),\n  ~f=drawRectLtwh,\n  (),\n);\n"
  },
  {
    "path": "packages/reason-skia/bench/ColorBench.re",
    "content": "open BenchFramework;\n\nlet options = Reperf.Options.create(~iterations=100000, ());\n\nmodule Data = {\n  let initialColor = Skia.Color.makeArgb(0xFFl, 0xFFl, 0xFFl, 0xFFl);\n};\n\nlet makeArgb = () => {\n  let _: Skia.Color.t = Skia.Color.makeArgb(0xFFl, 0xFFl, 0xFFl, 0xFFl);\n  ();\n};\n\nlet makeArgbFloat = () => {\n  let _: Skia.Color.t = Skia.Color.Float.makeArgb(0.1, 0.2, 0.3, 0.4);\n  ();\n};\n\nlet getA = () => {\n  let _: int32 = Skia.Color.getA(Data.initialColor);\n  ();\n};\n\nlet getAFloat = () => {\n  let _: float = Skia.Color.Float.getA(Data.initialColor);\n  ();\n};\n\nbench(\n  ~name=\"Color: makeArgb (float)\",\n  ~options,\n  ~setup=() => (),\n  ~f=makeArgbFloat,\n  (),\n);\nbench(\n  ~name=\"Color: getA (float)\",\n  ~options,\n  ~setup=() => (),\n  ~f=getAFloat,\n  (),\n);\nbench(~name=\"Color: makeArgb\", ~options, ~setup=() => (), ~f=makeArgb, ());\nbench(~name=\"Color: getA\", ~options, ~setup=() => (), ~f=makeArgb, ());\n"
  },
  {
    "path": "packages/reason-skia/bench/MatrixBench.re",
    "content": "open BenchFramework;\n\nlet options = Reperf.Options.create(~iterations=100000, ());\n\nmodule Data = {\n  let initialMatrix = Skia.Matrix.make();\n\n  let scale = {\n    let mat = Skia.Matrix.make();\n    Skia.Matrix.setScale(mat, 1.0, 2.0, 3.0, 4.0);\n    mat;\n  };\n\n  let translate = {\n    let mat = Skia.Matrix.make();\n    Skia.Matrix.setTranslate(mat, 1.0, 2.0);\n    mat;\n  };\n\n  let out = Skia.Matrix.make();\n\n  let rect = Skia.Rect.makeLtrb(1., 1., 100., 200.);\n  let outRect = Skia.Rect.makeLtrb(0., 0., 0., 0.);\n};\n\nlet setScale = () => {\n  let () = Skia.Matrix.setScale(Data.initialMatrix, 1.0, 2.0, 1.0, 1.0);\n  ();\n};\n\nlet setTranslate = () => {\n  let () = Skia.Matrix.setTranslate(Data.initialMatrix, 1.0, 2.0);\n  ();\n};\n\nlet concat = () => {\n  let () = Skia.Matrix.concat(Data.out, Data.scale, Data.translate);\n  ();\n};\n\nlet preConcat = () => {\n  let () = Skia.Matrix.preConcat(Data.out, Data.scale);\n  ();\n};\n\nlet postConcat = () => {\n  let () = Skia.Matrix.postConcat(Data.out, Data.translate);\n  ();\n};\n\nlet mapRect = () => {\n  let () = Skia.Matrix.mapRect(Data.scale, Data.rect, Data.outRect);\n  ();\n};\n\nbench(~name=\"Matrix: mapRect\", ~options, ~setup=() => (), ~f=mapRect, ());\n\nbench(~name=\"Matrix: preConcat\", ~options, ~setup=() => (), ~f=preConcat, ());\n\nbench(\n  ~name=\"Matrix: postConcat\",\n  ~options,\n  ~setup=() => (),\n  ~f=postConcat,\n  (),\n);\n\nbench(~name=\"Matrix: concat\", ~options, ~setup=() => (), ~f=concat, ());\n\nbench(\n  ~name=\"Matrix: setTranslate\",\n  ~options,\n  ~setup=() => (),\n  ~f=setTranslate,\n  (),\n);\nbench(~name=\"Matrix: setScale\", ~options, ~setup=() => (), ~f=setScale, ());\n"
  },
  {
    "path": "packages/reason-skia/bench/RectBench.re",
    "content": "open BenchFramework;\n\nlet options = Reperf.Options.create(~iterations=100000, ());\n\nmodule Data = {\n  let initialRect = Skia.Rect.makeLtrb(0., 0., 0., 0.);\n};\n\nlet setLtrb = () => {\n  let () =\n    Skia.Rect.Mutable.setLtrb(~out=Data.initialRect, 1.0, 2.0, 3.0, 4.0);\n  ();\n};\n\nlet makeLtrb = () => {\n  let _: Skia.Rect.t = Skia.Rect.makeLtrb(1.0, 2.0, 3.0, 4.0);\n  ();\n};\n\nlet getLeft = () => {\n  let _: float = Skia.Rect.getLeft(Data.initialRect);\n  ();\n};\n\nbench(~name=\"Rect: makeLtrb\", ~options, ~setup=() => (), ~f=makeLtrb, ());\n\nbench(~name=\"Rect: setLtrb\", ~options, ~setup=() => (), ~f=setLtrb, ());\n\nbench(~name=\"Rect: getLeft\", ~options, ~setup=() => (), ~f=getLeft, ());\n"
  },
  {
    "path": "packages/reason-skia/bench/SkiaPaintBench.re",
    "content": "open BenchFramework;\n\nopen Skia;\n\nlet options = Reperf.Options.create(~iterations=100000, ());\n\nmodule Data = {\n  let initialPaint = Paint.make();\n  let initialColor = Color.makeArgb(0xFFl, 0xFFl, 0xFFl, 0xFFl);\n};\n\nlet make = () => {\n  let _: Paint.t = Paint.make();\n  ();\n};\n\nlet setColor = () => {\n  let () = Paint.setColor(Data.initialPaint, Data.initialColor);\n  ();\n};\n\nbench(~name=\"Paint: make\", ~options, ~setup=() => (), ~f=make, ());\n\nbench(~name=\"Paint: setColor\", ~options, ~setup=() => (), ~f=setColor, ());\n"
  },
  {
    "path": "packages/reason-skia/bench/dune",
    "content": "(library\n (name Skia_Bench)\n (ocamlopt_flags -linkall)\n (libraries reason-skia reason-skia.wrapped reason-skia.wrapped.bindings\n   reperf.lib))\n"
  },
  {
    "path": "packages/reason-skia/examples/skia-cli/LICENSE_FiraCode.txt",
    "content": "Copyright (c) 2014, The Fira Code Project Authors (https://github.com/tonsky/FiraCode)\n\nThis Font Software is licensed under the SIL Open Font License, Version 1.1.\nThis license is copied below, and is also available with a FAQ at:\nhttp://scripts.sil.org/OFL\n\n\n-----------------------------------------------------------\nSIL OPEN FONT LICENSE Version 1.1 - 26 February 2007\n-----------------------------------------------------------\n\nPREAMBLE\nThe goals of the Open Font License (OFL) are to stimulate worldwide\ndevelopment of collaborative font projects, to support the font creation\nefforts of academic and linguistic communities, and to provide a free and\nopen framework in which fonts may be shared and improved in partnership\nwith others.\n\nThe OFL allows the licensed fonts to be used, studied, modified and\nredistributed freely as long as they are not sold by themselves. The\nfonts, including any derivative works, can be bundled, embedded,\nredistributed and/or sold with any software provided that any reserved\nnames are not used by derivative works. The fonts and derivatives,\nhowever, cannot be released under any other type of license. The\nrequirement for fonts to remain under this license does not apply\nto any document created using the fonts or their derivatives.\n\nDEFINITIONS\n\"Font Software\" refers to the set of files released by the Copyright\nHolder(s) under this license and clearly marked as such. This may\ninclude source files, build scripts and documentation.\n\n\"Reserved Font Name\" refers to any names specified as such after the\ncopyright statement(s).\n\n\"Original Version\" refers to the collection of Font Software components as\ndistributed by the Copyright Holder(s).\n\n\"Modified Version\" refers to any derivative made by adding to, deleting,\nor substituting -- in part or in whole -- any of the components of the\nOriginal Version, by changing formats or by porting the Font Software to a\nnew environment.\n\n\"Author\" refers to any designer, engineer, programmer, technical\nwriter or other person who contributed to the Font Software.\n\nPERMISSION & CONDITIONS\nPermission is hereby granted, free of charge, to any person obtaining\na copy of the Font Software, to use, study, copy, merge, embed, modify,\nredistribute, and sell modified and unmodified copies of the Font\nSoftware, subject to the following conditions:\n\n1) Neither the Font Software nor any of its individual components,\nin Original or Modified Versions, may be sold by itself.\n\n2) Original or Modified Versions of the Font Software may be bundled,\nredistributed and/or sold with any software, provided that each copy\ncontains the above copyright notice and this license. These can be\nincluded either as stand-alone text files, human-readable headers or\nin the appropriate machine-readable metadata fields within text or\nbinary files as long as those fields can be easily viewed by the user.\n\n3) No Modified Version of the Font Software may use the Reserved Font\nName(s) unless explicit written permission is granted by the corresponding\nCopyright Holder. This restriction only applies to the primary font name as\npresented to the users.\n\n4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font\nSoftware shall not be used to promote, endorse or advertise any\nModified Version, except to acknowledge the contribution(s) of the\nCopyright Holder(s) and the Author(s) or with their explicit written\npermission.\n\n5) The Font Software, modified or unmodified, in part or in whole,\nmust be distributed entirely under this license, and must not be\ndistributed under any other license. The requirement for fonts to\nremain under this license does not apply to any document created\nusing the Font Software.\n\nTERMINATION\nThis license becomes null and void if any of the above conditions are\nnot met.\n\nDISCLAIMER\nTHE FONT SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT\nOF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE\nCOPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\nINCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL\nDAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM\nOTHER DEALINGS IN THE FONT SOFTWARE.\n"
  },
  {
    "path": "packages/reason-skia/examples/skia-cli/LICENSE_ORBITRON.md",
    "content": "\n    Copyright (c) 2009, Matt McInerney <matt@pixelspread.com>, with Reserved Font Name: \"Orbitron\".\n    \n\tThis Font Software is licensed under the SIL Open Font License, Version 1.1.\n\tThis license is copied below, and is also available with a FAQ at:\n\thttp://scripts.sil.org/OFL\n\n\tVersion 1.1 - 26 February 2007\n\n\nSIL Open Font License\n====================================================\n\n\nPreamble\n----------\n\nThe goals of the Open Font License (OFL) are to stimulate worldwide\ndevelopment of collaborative font projects, to support the font creation\nefforts of academic and linguistic communities, and to provide a free and\nopen framework in which fonts may be shared and improved in partnership\nwith others.\n\nThe OFL allows the licensed fonts to be used, studied, modified and\nredistributed freely as long as they are not sold by themselves. The\nfonts, including any derivative works, can be bundled, embedded, \nredistributed and/or sold with any software provided that any reserved\nnames are not used by derivative works. The fonts and derivatives,\nhowever, cannot be released under any other type of license. The\nrequirement for fonts to remain under this license does not apply\nto any document created using the fonts or their derivatives.\n\nDefinitions\n-------------\n\n`\"Font Software\"` refers to the set of files released by the Copyright\nHolder(s) under this license and clearly marked as such. This may\ninclude source files, build scripts and documentation.\n\n`\"Reserved Font Name\"` refers to any names specified as such after the\ncopyright statement(s).\n\n`\"Original Version\"` refers to the collection of Font Software components as\ndistributed by the Copyright Holder(s).\n\n`\"Modified Version\"` refers to any derivative made by adding to, deleting,\nor substituting -- in part or in whole -- any of the components of the\nOriginal Version, by changing formats or by porting the Font Software to a\nnew environment.\n\n`\"Author\"` refers to any designer, engineer, programmer, technical\nwriter or other person who contributed to the Font Software.\n\nPermission & Conditions\n------------------------\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of the Font Software, to use, study, copy, merge, embed, modify,\nredistribute, and sell modified and unmodified copies of the Font\nSoftware, subject to the following conditions:\n\n1. Neither the Font Software nor any of its individual components,\nin Original or Modified Versions, may be sold by itself.\n\n2. Original or Modified Versions of the Font Software may be bundled,\nredistributed and/or sold with any software, provided that each copy\ncontains the above copyright notice and this license. These can be\nincluded either as stand-alone text files, human-readable headers or\nin the appropriate machine-readable metadata fields within text or\nbinary files as long as those fields can be easily viewed by the user.\n\n3. No Modified Version of the Font Software may use the Reserved Font\nName(s) unless explicit written permission is granted by the corresponding\nCopyright Holder. This restriction only applies to the primary font name as\npresented to the users.\n\n4. The name(s) of the Copyright Holder(s) or the Author(s) of the Font\nSoftware shall not be used to promote, endorse or advertise any\nModified Version, except to acknowledge the contribution(s) of the\nCopyright Holder(s) and the Author(s) or with their explicit written\npermission.\n\n5. The Font Software, modified or unmodified, in part or in whole,\nmust be distributed entirely under this license, and must not be\ndistributed under any other license. The requirement for fonts to\nremain under this license does not apply to any document created\nusing the Font Software.\n\nTermination\n-----------\n\nThis license becomes null and void if any of the above conditions are\nnot met.\n\n\n\tDISCLAIMER\n\n\tTHE FONT SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n\tEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF\n\tMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT\n\tOF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE\n\tCOPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\n\tINCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL\n\tDAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n\tFROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM\n\tOTHER DEALINGS IN THE FONT SOFTWARE.\n"
  },
  {
    "path": "packages/reason-skia/examples/skia-cli/SkiaCli.re",
    "content": "open Skia;\n\nlet makeSurface = (width, height) => {\n  let imageInfo = ImageInfo.make(width, height, Rgba8888, Premul, None);\n  Surface.makeRaster(imageInfo, 0, None);\n};\n\nlet emitPng = (path, surface) => {\n  let image = Surface.makeImageSnapshot(surface);\n  let data = Image.encodeToData(image);\n  let dataString = Data.makeString(data);\n  let fileOutputChannel = open_out_bin(path);\n  output_string(fileOutputChannel, dataString);\n  close_out(fileOutputChannel);\n};\n\nlet draw = canvas => {\n  let fill = Paint.make();\n  Paint.setColor(fill, Color.makeArgb(0xFFl, 0x00l, 0x00l, 0xFFl));\n  Canvas.drawPaint(canvas, fill);\n\n  Paint.setColor(fill, Color.makeArgb(0xFFl, 0x00l, 0xFFl, 0xFFl));\n  let rect = Rect.makeLtrb(100., 100., 540., 380.);\n  Canvas.drawRect(canvas, rect, fill);\n\n  let stroke = Paint.make();\n  Paint.setColor(stroke, Color.makeArgb(0xFFl, 0xFFl, 0x00l, 0x00l));\n  Paint.setAntiAlias(stroke, true);\n  Paint.setStyle(stroke, Stroke);\n  Paint.setStrokeWidth(stroke, 5.);\n\n  let path = Path.make();\n  Path.moveTo(path, 50., 50.);\n  Path.lineTo(path, 590., 50.);\n  Path.cubicTo(path, -490., 50., 1130., 430., 50., 430.);\n  Path.lineTo(path, 590., 430.);\n  Path.close(path);\n  Path.addCircle(path, 100., 200., ~radius=75., ());\n\n  let roundRect = Rect.makeLtrb(300., 400., 240., 280.);\n  Path.addRoundRect(path, roundRect, 25., 25., ());\n  Path.addCircle(path, 100., 300., ~radius=100., ~direction=`cw, ());\n  Canvas.drawPath(canvas, path, stroke);\n\n  Paint.setColor(fill, Color.makeArgb(0xCCl, 0x00l, 0xFFl, 0x00l));\n  Paint.setImageFilter(\n    fill,\n    Some(\n      ImageFilter.makeDropShadow(\n        10.,\n        10.,\n        3.,\n        3.,\n        Color.makeArgb(0xAAl, 0x00l, 0x00l, 0x00l),\n        DrawShadowAndForeground,\n        None,\n        None,\n      ),\n    ),\n  );\n  let rect2 = Rect.makeLtrb(120., 120., 520., 360.);\n\n  let ovalPaint = Skia.Paint.make();\n  Skia.Paint.setColor(\n    ovalPaint,\n    Skia.Color.makeArgb(0xFFl, 0xFFl, 0xFFl, 0x00l),\n  );\n  let innerPath = Skia.Path.make();\n  Skia.Path.lineTo(innerPath, 5., 5.);\n  Skia.Path.lineTo(innerPath, 15., -5.);\n  Skia.Path.lineTo(innerPath, 20., 0.);\n\n  let translate = Skia.Matrix.makeScale(20., 20., 20., 20.);\n  Skia.Paint.setAntiAlias(ovalPaint, true);\n  Skia.Paint.setStyle(ovalPaint, Stroke);\n  Skia.Paint.setStrokeWidth(ovalPaint, 1.);\n  let pathEffect = Skia.PathEffect.create2dPath(~matrix=translate, innerPath);\n  Skia.Paint.setPathEffect(ovalPaint, pathEffect);\n  Canvas.drawOval(canvas, rect2, ovalPaint);\n\n  let fill3 = Paint.make();\n  Paint.setColor(fill3, Color.makeArgb(0xFFl, 0xFFl, 0xFFl, 0xFFl));\n  Paint.setTextSize(fill3, 30.);\n\n  let nonExistentTypeface = Typeface.makeFromFile(\"non-existent-font.ttf\", 0);\n  assert(nonExistentTypeface == None);\n\n  // Draw text\n  let filePath =\n    Sys.getcwd()\n    ++ \"/packages/reason-skia/examples/skia-cli/Orbitron Medium.ttf\";\n  print_endline(\"Loading font: \" ++ filePath);\n  let maybeTypeface = Typeface.makeFromFile(filePath, 0);\n  switch (maybeTypeface) {\n  | None => failwith(\"Unable to load font: \" ++ filePath)\n  | Some(typeFace) =>\n    print_endline(__LOC__ ++ \": we will set.\");\n    Paint.setTypeface(fill3, typeFace);\n    print_endline(__LOC__ ++ \": setTypeface is OK.\");\n    Canvas.drawText(canvas, \"Hello, world!\", 30., 30., fill3);\n    let metrics = FontMetrics.make();\n    let _ret: float = Paint.getFontMetrics(fill3, metrics, 1.0);\n\n    print_endline(\n      \"-- Top: \" ++ string_of_float(FontMetrics.getTop(metrics)),\n    );\n    print_endline(\n      \"-- Bottom: \" ++ string_of_float(FontMetrics.getBottom(metrics)),\n    );\n    print_endline(\n      \"-- Underline position: \"\n      ++ string_of_float(FontMetrics.getUnderlinePosition(metrics)),\n    );\n    print_endline(\n      \"-- Underline thickness: \"\n      ++ string_of_float(FontMetrics.getUnderlineThickness(metrics)),\n    );\n    print_endline(\"-- Ret: \" ++ string_of_float(_ret));\n\n    print_endline(__LOC__ ++ \": We return.\");\n\n    // Measure\n    let measurement = Paint.measureText(fill3, \"Hello, world!\", None);\n    print_endline(\"Measured text: \" ++ string_of_float(measurement));\n    Paint.setTextSize(fill3, 50.);\n    let largeMeasurement = Paint.measureText(fill3, \"Hello, world!\", None);\n    print_endline(\n      \"Large measured text: \" ++ string_of_float(largeMeasurement),\n    );\n  };\n\n  // Turn off drop shadow\n  Paint.setImageFilter(fill, None);\n\n  // Validate loading a non-existent file returns None, but doesn't crash\n  let nonExistentData = Data.makeFromFileName(\"file-that-does-not-exist.png\");\n  switch (nonExistentData) {\n  | Some(_) => failwith(\"Somehow we loaded a non-existent file...\")\n  | None => print_endline(\"Got None for file that doesn't exist.\")\n  };\n\n  // Load and draw image\n  let imgPath = Sys.getcwd() ++ \"/assets/uv.png\";\n  print_endline(\"Loading image: \" ++ imgPath);\n  let maybeImgData = Data.makeFromFileName(imgPath);\n  let maybeImg =\n    switch (maybeImgData) {\n    | Some(imgData) =>\n      let strLen = String.length(Data.makeString(imgData));\n      print_endline(\"Bytes loaded: \" ++ string_of_int(strLen));\n      let ret = Image.makeFromEncoded(imgData, None);\n      print_endline(\"Got image!\");\n      ret;\n    | None => failwith(\"Unable to load img: \" ++ imgPath)\n    };\n\n  switch (maybeImg) {\n  | None => failwith(\"Unable to load image: uv.png\")\n  | Some(img) =>\n    print_endline(\n      Printf.sprintf(\n        \"%s Image dimensions: %dx%d\",\n        imgPath,\n        Skia.Image.width(img),\n        Skia.Image.height(img),\n      ),\n    );\n    let imgFill = Paint.make();\n    Paint.setAlpha(imgFill, 0.0);\n    Canvas.drawImage(canvas, img, 250., 250., Some(imgFill));\n\n    let sourceRect = Rect.makeLtrb(0., 0., 128., 128.);\n    let destRect = Rect.makeLtrb(200., 200., 264., 264.);\n    Canvas.drawImageRect(\n      canvas,\n      img,\n      Some(sourceRect),\n      destRect,\n      Some(imgFill),\n    );\n  };\n\n  // Draw text w/ ligature\n  let filePath =\n    Sys.getcwd()\n    ++ \"/packages/reason-skia/examples/skia-cli/FiraCode-Regular.ttf\";\n  print_endline(\"Loading font: \" ++ filePath);\n  let maybeTypeface = Typeface.makeFromFile(filePath, 0);\n  switch (maybeTypeface) {\n  | None => failwith(\"Unable to load font: \" ++ filePath)\n  | Some(typeFace) =>\n    let fill = Paint.make();\n    Paint.setColor(fill, Color.makeArgb(0xFFl, 0xFFl, 0xFFl, 0xFFl));\n    Paint.setTextSize(fill, 30.);\n    Paint.setTypeface(fill, typeFace);\n    Paint.setSubpixelText(fill, true);\n    Paint.setTextEncoding(fill, GlyphId);\n\n    let glyphsToString = glyphs => {\n      let len = List.length(glyphs);\n      let bytes = Bytes.create(len * 2);\n\n      let rec loop = (glyphs, idx) => {\n        switch (glyphs) {\n        | [hd, ...tail] =>\n          let lowerBit = hd land 255;\n          let highBit = (hd land 255 lsl 8) lsr 8;\n          Bytes.set(bytes, idx * 2 + 0, Char.chr(lowerBit));\n          Bytes.set(bytes, idx * 2 + 1, Char.chr(highBit));\n          loop(tail, idx + 1);\n        | [] => ()\n        };\n      };\n\n      loop(glyphs, 0);\n\n      Bytes.to_string(bytes);\n    };\n\n    // For FiraCode, this is a==>b\n    let str = glyphsToString([136, 1624, 1624, 1495, 148]);\n    Canvas.drawText(canvas, str, 50., 100., fill);\n  };\n\n  let fill = Paint.make();\n  Paint.setColor(fill, Color.makeArgb(0xFFl, 0xFFl, 0x00l, 0xFFl));\n  Canvas.drawRectLtwh(canvas, 50., 75., 100., 200., fill);\n\n  let fill = Paint.make();\n  Paint.setColor(fill, Color.makeArgb(0xFFl, 0xFFl, 0x00l, 0x00l));\n  Canvas.drawCircle(canvas, 320., 240., 30., fill);\n\n  // Creating empty shader\n  let _emptyShader = Skia.Shader.makeEmpty();\n\n  let red = Color.makeArgb(0xFFl, 0xFFl, 0x00l, 0x00l);\n  let green = Color.makeArgb(0xFFl, 0x00l, 0xFFl, 0x00l);\n  let blue = Color.makeArgb(0xFFl, 0x00l, 0x00l, 0xFFl);\n\n  // Creating a 2-stop linear gradient\n  let linearGradient2 =\n    Skia.Shader.makeLinearGradient2(\n      ~startPoint=Skia.Point.make(0.0, 0.0),\n      ~stopPoint=Skia.Point.make(100.0, 100.0),\n      ~startColor=red,\n      ~stopColor=blue,\n      ~tileMode=`repeat,\n    );\n  let fill = Paint.make();\n  Paint.setColor(fill, Color.makeArgb(0xFFl, 0xFFl, 0x00l, 0x00l));\n  Paint.setShader(fill, linearGradient2);\n\n  Canvas.drawRectLtwh(canvas, 0., 0., 100., 100., fill);\n\n  // Creating a 3-stop linear gradient\n  let linearGradient3 =\n    Skia.Shader.makeLinearGradient(\n      ~startPoint=Skia.Point.make(0.0, 0.0),\n      ~stopPoint=Skia.Point.make(100.0, 0.0),\n      ~colorStops=\n        Skia.Shader.[\n          {color: red, position: 0.0},\n          {color: blue, position: 0.25},\n          {color: green, position: 1.0},\n        ],\n      ~tileMode=`repeat,\n    );\n  let fill = Paint.make();\n  Paint.setShader(fill, linearGradient3);\n  Canvas.drawRectLtwh(canvas, 100., 100., 100., 100., fill);\n};\n\nlet drawSvg = canvas => {\n  let drawFromFile = dX => {\n    let stream = Stream.makeFileStream(\"./assets/revery.svg\") |> Option.get;\n\n    let svg = SVG.makeFromStream(stream) |> Option.get;\n\n    Printf.printf(\n      \"SVG: file container size: w=%f h=%f\\n\",\n      SVG.getContainerWidth(svg),\n      SVG.getContainerHeight(svg),\n    );\n\n    Canvas.translate(canvas, dX, 0.);\n\n    SVG.render(svg, canvas);\n    SVG.getContainerWidth(svg);\n  };\n\n  let drawFromString = dX => {\n    let svgStr = {|\n      <svg width=\"100%\" height=\"10%\">\n        <path id=\"lineAB\" d=\"M 100 350 l 150 -300\" stroke=\"red\" stroke-width=\"3\" fill=\"none\" />\n        <path id=\"lineBC\" d=\"M 250 50 l 150 300\" stroke=\"red\" stroke-width=\"3\" fill=\"none\" />\n        <path d=\"M 175 200 l 150 0\" stroke=\"green\" stroke-width=\"3\" fill=\"none\" />\n        <path d=\"M 100 350 q 150 -300 300 0\" stroke=\"blue\" stroke-width=\"5\" fill=\"none\" />\n        <g stroke=\"black\" stroke-width=\"3\" fill=\"black\">\n          <circle id=\"pointA\" cx=\"100\" cy=\"350\" r=\"3\" />\n          <circle id=\"pointB\" cx=\"250\" cy=\"50\" r=\"3\" />\n          <circle id=\"pointC\" cx=\"400\" cy=\"350\" r=\"3\" />\n        </g>\n      </svg>\n    |};\n\n    let stream =\n      Stream.makeMemoryStreamFromString(svgStr, String.length(svgStr));\n\n    let svg = SVG.makeFromStream(stream) |> Option.get;\n\n    Printf.printf(\n      \"SVG: string container size: w=%f h=%f\\n\",\n      SVG.getContainerWidth(svg),\n      SVG.getContainerHeight(svg),\n    );\n\n    Canvas.translate(canvas, dX, 0.);\n\n    SVG.render(svg, canvas);\n    SVG.getContainerWidth(svg);\n  };\n\n  drawFromFile(0.) |> drawFromString |> ignore;\n};\n\nlet surface = makeSurface(640l, 480l) |> Option.get;\nlet svgSurface = makeSurface(1280l, 1280l) |> Option.get;\n\nlet canvas = Surface.getCanvas(surface);\nlet svgCanvas = Surface.getCanvas(svgSurface);\n\ndraw(canvas);\ndrawSvg(svgCanvas);\n\nemitPng(\"skia-c-example.png\", surface);\nemitPng(\"skia-svg-example.png\", svgSurface);\n\nGc.full_major();\n\nprint_endline(\"Done!\");\n"
  },
  {
    "path": "packages/reason-skia/examples/skia-cli/dune",
    "content": "(executables\n (names SkiaCli)\n (package ReveryExamples)\n (public_names SkiaCli)\n (modes native byte)\n (libraries reason-skia reason-skia.wrapped.bindings reason-skia.wrapped\n   reason-native-crash-utils.asan))\n\n(install\n (section bin)\n (package ReveryExamples)\n (files SkiaCli.bc))\n"
  },
  {
    "path": "packages/reason-skia/examples/skia-font-manager-cli/SkiaFontManagerCli.re",
    "content": "open Skia;\n\nlet emitPng = (path, surface) => {\n  let image = Surface.makeImageSnapshot(surface);\n  let data = Image.encodeToData(image);\n  let dataString = Data.makeString(data);\n  let fileOutputChannel = open_out_bin(path);\n  output_string(fileOutputChannel, dataString);\n  close_out(fileOutputChannel);\n};\n\nlet makeSurface = (width, height) => {\n  let imageInfo = ImageInfo.make(width, height, Rgba8888, Premul, None);\n  Surface.makeRaster(imageInfo, 0, None);\n};\n\nlet draw = canvas => {\n  let fontManager = FontManager.makeDefault();\n  let style = FontStyle.make(400, 5, Upright);\n  let maybeTypeface =\n    FontManager.matchFamilyStyle(fontManager, \"Arial\", style);\n  let fill = Paint.make();\n\n  Paint.setColor(fill, Color.makeArgb(0xFFl, 0xFFl, 0xFFl, 0xFFl));\n  Paint.setTextSize(fill, 16.);\n  Paint.setSubpixelText(fill, true);\n\n  switch (maybeTypeface) {\n  | None =>\n    print_endline(\"Normal Arial not found. Ensure you have it available.\")\n  | Some(typeface) =>\n    Paint.setTypeface(fill, typeface);\n    Canvas.drawText(canvas, \"Arial (System)\", 10., 20., fill);\n    let stream = Typeface.toStream(typeface);\n    let length = Stream.getLength(stream);\n    Printf.printf(\"Stream length: %d\\n\", length);\n    let data = Data.makeFromStream(stream, length);\n    let oc = open_out(\"Arial.ttf\");\n    print_endline(\"Writing Arial Normal to Arial.ttf...\");\n    Printf.fprintf(oc, \"%s\", Data.makeString(data));\n    print_endline(\"Written!\");\n    close_out(oc);\n  };\n\n  let maybeTypeface =\n    FontManager.matchFamilyStyle(fontManager, \"Times New Roman\", style);\n  switch (maybeTypeface) {\n  | None =>\n    print_endline(\n      \"Normal Times New Roman not found. Ensure you have it available.\",\n    )\n  | Some(typeface) =>\n    Paint.setTypeface(fill, typeface);\n    Canvas.drawText(canvas, \"Times New Roman (System)\", 10., 40., fill);\n  };\n\n  let maybeTypeface =\n    FontManager.matchFamilyStyle(fontManager, \"Consolas\", style);\n  switch (maybeTypeface) {\n  | None =>\n    print_endline(\n      \"Normal Consolas not found. Ensure your system has it available.\",\n    )\n  | Some(typeface) =>\n    Paint.setTypeface(fill, typeface);\n    let metrics = FontMetrics.make();\n    let _ret: float = Paint.getFontMetrics(fill, metrics, 1.0);\n    print_endline(\"__Consolas__\");\n    print_endline(\n      \"-- Average character width: \"\n      ++ string_of_float(FontMetrics.getAvgCharacterWidth(metrics)),\n    );\n    print_endline(\n      \"-- Maximum character width: \"\n      ++ string_of_float(FontMetrics.getMaxCharacterWidth(metrics)),\n    );\n  };\n\n  let filePath =\n    Sys.getcwd()\n    ++ \"/packages/reason-skia/examples/skia-cli/FiraCode-Regular.ttf\";\n  print_endline(\"Loading font: \" ++ filePath);\n  let maybeTypeface = Typeface.makeFromFile(filePath, 0);\n  switch (maybeTypeface) {\n  | None => failwith(\"Unable to load font: \" ++ filePath)\n  | Some(typeface) =>\n    Paint.setTypeface(fill, typeface);\n    let metrics = FontMetrics.make();\n    let _ret: float = Paint.getFontMetrics(fill, metrics, 1.0);\n    print_endline(\"__Fira Code__\");\n    print_endline(\n      \"-- Average character width: \"\n      ++ string_of_float(FontMetrics.getAvgCharacterWidth(metrics)),\n    );\n    print_endline(\n      \"-- Maximum character width: \"\n      ++ string_of_float(FontMetrics.getMaxCharacterWidth(metrics)),\n    );\n  };\n\n  let emoji = \"😃\";\n  let char = Zed_utf8.get(emoji, 0);\n  let maybeTypeface =\n    FontManager.matchFamilyStyleCharacter(\n      fontManager,\n      \"Arial\",\n      style,\n      [\"en_US\"],\n      char,\n    );\n  switch (maybeTypeface) {\n  | Some(typeface) =>\n    print_endline(\n      \"Found font for emoji: \" ++ Typeface.getFamilyName(typeface),\n    );\n    Paint.setTypeface(fill, typeface);\n    Canvas.drawText(canvas, emoji, 10., 60., fill);\n  | None => print_endline(\"No emoji font found\")\n  };\n\n  let japanese = \"鬼\";\n  let char = Zed_utf8.get(japanese, 0);\n  let maybeTypeface =\n    FontManager.matchFamilyStyleCharacter(\n      fontManager,\n      \"Arial\",\n      style,\n      [\"en_US\"],\n      char,\n    );\n  switch (maybeTypeface) {\n  | Some(typeface) =>\n    print_endline(\n      \"Found font for Japanese: \" ++ Typeface.getFamilyName(typeface),\n    );\n    Paint.setTypeface(fill, typeface);\n    Canvas.drawText(canvas, japanese, 10., 80., fill);\n  | None => print_endline(\"No Japanese font found\")\n  };\n};\n\nlet surface = makeSurface(640l, 480l) |> Option.get;\nlet canvas = Surface.getCanvas(surface);\ndraw(canvas);\nemitPng(\"skia-font-manager-output.png\", surface);\nprint_endline(\"Done!\");\n"
  },
  {
    "path": "packages/reason-skia/examples/skia-font-manager-cli/dune",
    "content": "(executables\n (names SkiaFontManagerCli)\n (package ReveryExamples)\n (public_names SkiaFontManagerCli)\n (modes native byte)\n (libraries reason-skia reason-native-crash-utils.asan\n   reason-skia.wrapped.bindings reason-skia.wrapped Revery.zed))\n\n(install\n (section bin)\n (package ReveryExamples)\n (files SkiaFontManagerCli.bc run-skia-fontmanager.sh))\n"
  },
  {
    "path": "packages/reason-skia/examples/skia-font-manager-cli/run-skia-fontmanager.sh",
    "content": "set -e\n\nCWD=$(dirname $0)\nif [[ $1 == 'windows' ]]; then executable=\"SkiaFontManagerCli.exe\"; else executable=\"SkiaFontManagerCli\"; fi\n\nLSAN_OPTIONS=suppressions=lsan.supp $CWD/$executable\n"
  },
  {
    "path": "packages/reason-skia/examples/skia-sdl2/SkiaSdl.re",
    "content": "open Skia;\n\nlet ctx = ref(None);\n\nlet printEnv = env =>\n  switch (Sys.getenv_opt(env)) {\n  | None => print_endline(env ++ \" : not defined \")\n  | Some(v) => print_endline(env ++ \" : \" ++ v)\n  };\n\nprintEnv(\"WAYLAND_DISPLAY\");\nprintEnv(\"XDG_SESSION_TYPE\");\n\nlet createSkiaGraphicsContext = (_window: Sdl2.Window.t) => {\n  print_endline(\"Creating graphics context\");\n  let nativeInterface = Skia.Gr.Gl.Interface.makeNative();\n  switch (nativeInterface) {\n  | Some(_) => print_endline(\"Native interface created successfully.\")\n  | None => print_endline(\"Native interface is null\")\n  };\n\n  let interface = Skia.Gr.Gl.Interface.(Sys.win32 ? makeSdl2ES() : makeSdl2());\n  print_endline(\"Have interface...\");\n  let context = Skia.Gr.Context.makeGl(interface);\n\n  switch (context) {\n  | None => failwith(\"Unable to create graphics context\")\n  | Some(glContext) =>\n    print_endline(\"glContext created successfully\");\n    ctx := Some(glContext);\n    glContext;\n  };\n};\n\nlet createSkiaSurface =\n    (window: Sdl2.Window.t, skiaContext: Skia.Gr.Context.t) => {\n  let framebufferInfo =\n    Gr.Gl.FramebufferInfo.make(\n      Unsigned.UInt.of_int(0),\n      Unsigned.UInt.of_int(0x8058),\n    );\n\n  let framebufferSize = Sdl2.Gl.getDrawableSize(window);\n  let backendRenderTarget =\n    Gr.BackendRenderTarget.makeGl(\n      framebufferSize.width,\n      framebufferSize.height,\n      0,\n      8,\n      framebufferInfo,\n    );\n\n  let surfaceProps = SurfaceProps.make(Unsigned.UInt32.of_int(0), RgbH);\n  switch (\n    Surface.makeFromBackendRenderTarget(\n      skiaContext,\n      backendRenderTarget,\n      BottomLeft,\n      Rgba8888,\n      None,\n      Some(surfaceProps),\n    )\n  ) {\n  | None => failwith(\"Unable to create skia surface.\")\n  | Some(surface) =>\n    Printf.printf(\n      \"Successfully created canvas: %dx%d\\n\",\n      framebufferSize.width,\n      framebufferSize.height,\n    );\n    surface;\n  };\n};\n\nlet run = () => {\n  let _ = Sdl2.init();\n  let attachResult = Sdl2.Platform.win32AttachConsole();\n\n  // If we were unable to attach a console, try allocating a new one\n  let _code =\n    if (attachResult == 0) {\n      Sdl2.Platform.win32AllocConsole();\n    } else {\n      attachResult;\n    };\n\n  let primaryWindow =\n    Sdl2.Window.create(\"test\", `Undefined, `Undefined, 100, 100, `Auto);\n  let glContext = Sdl2.Gl.setup(primaryWindow);\n\n  Sdl2.Gl.setSwapInterval(1);\n\n  Sdl2.Window.setTitle(primaryWindow, \"reason-skia-sdl2 example\");\n  Sdl2.Window.setWin32ProcessDPIAware(primaryWindow);\n\n  let _scale = Sdl2.Window.getWin32ScaleFactor(primaryWindow);\n  let _display = Sdl2.Window.getDisplay(primaryWindow);\n\n  Sdl2.Window.setSize(primaryWindow, 800, 600);\n  Sdl2.Window.center(primaryWindow);\n\n  Sdl2.Window.show(primaryWindow);\n\n  Sdl2.Window.setResizable(primaryWindow, true);\n  Sdl2.Window.setMinimumSize(primaryWindow, 200, 100);\n  Sdl2.Gl.makeCurrent(primaryWindow, glContext);\n\n  let skiaContext = createSkiaGraphicsContext(primaryWindow);\n  let skiaSurface = createSkiaSurface(primaryWindow, skiaContext);\n  let canvas = Skia.Surface.getCanvas(skiaSurface);\n\n  let render = (window, context, surface) => {\n    //print_endline(\"-- Render: start\");\n    ignore(context);\n    ignore(surface);\n\n    let color = Skia.Color.makeArgb(0xFFl, 0xFFl, 0x00l, 0x00l);\n\n    Skia.Canvas.clear(canvas, color);\n\n    let paint = Skia.Paint.make();\n    Skia.Paint.setColor(\n      paint,\n      Skia.Color.makeArgb(0xFFl, 0xFFl, 0xFFl, 0xFFl),\n    );\n\n    Skia.Canvas.drawText(canvas, \"Hello, world!\", 50., 50., paint);\n\n    Skia.Canvas.flush(canvas);\n    Sdl2.Gl.swapWindow(window);\n    //print_endline(\"-- Render: end\");\n  };\n\n  //let frame = ref(0);\n  Sdl2.renderLoop(() => {\n    switch (Sdl2.Event.poll()) {\n    | None => ()\n    | Some(evt) =>\n      switch (evt) {\n      | Sdl2.Event.Quit => exit(0)\n      | _ => ()\n      }\n    };\n\n    render(primaryWindow, skiaContext, skiaSurface);\n    Gc.full_major();\n    false;\n  });\n\n  Lwt.return();\n};\n\nrun();\n"
  },
  {
    "path": "packages/reason-skia/examples/skia-sdl2/dune",
    "content": "(executables\n (names SkiaSdl)\n (package ReveryExamples)\n (public_names SkiaSdl)\n (modes native byte)\n (libraries reason-skia sdl2 lwt integers reason-skia.wrapped.bindings\n   reason-skia.wrapped))\n\n(install\n (section bin)\n (package ReveryExamples)\n (files SkiaSdl.bc))\n"
  },
  {
    "path": "packages/reason-skia/src/Skia.re",
    "content": "type colorType = SkiaWrapped.colorType;\ntype alphaType = SkiaWrapped.alphaType;\n\nmodule Color = {\n  type t = int32;\n  [@noalloc]\n  external makeArgb:\n    ([@unboxed] int32, [@unboxed] int32, [@unboxed] int32, [@unboxed] int32) =>\n    [@unboxed] int32 =\n    \"reason_skia_stub_sk_color_set_argb_byte\"\n    \"reason_skia_stub_sk_color_set_argb\";\n\n  [@noalloc]\n  external getA: ([@unboxed] int32) => [@unboxed] int32 =\n    \"reason_skia_stub_sk_color_get_a_byte\" \"reason_skia_stub_sk_color_get_a\";\n\n  [@noalloc]\n  external getR: ([@unboxed] int32) => [@unboxed] int32 =\n    \"reason_skia_stub_sk_color_get_r_byte\" \"reason_skia_stub_sk_color_get_r\";\n\n  [@noalloc]\n  external getG: ([@unboxed] int32) => [@unboxed] int32 =\n    \"reason_skia_stub_sk_color_get_g_byte\" \"reason_skia_stub_sk_color_get_g\";\n\n  [@noalloc]\n  external getB: ([@unboxed] int32) => [@unboxed] int32 =\n    \"reason_skia_stub_sk_color_get_b_byte\" \"reason_skia_stub_sk_color_get_b\";\n\n  module Float = {\n    external makeArgb:\n      (\n        [@unboxed] float,\n        [@unboxed] float,\n        [@unboxed] float,\n        [@unboxed] float\n      ) =>\n      [@unboxed] int32 =\n      \"reason_skia_color_float_make_argb_byte\"\n      \"reason_skia_color_float_make_argb\";\n\n    [@noalloc]\n    external getA: ([@unboxed] int32) => [@unboxed] float =\n      \"reason_skia_stub_sk_color_float_get_a_byte\"\n      \"reason_skia_stub_sk_color_float_get_a\";\n\n    external getR: ([@unboxed] int32) => [@unboxed] float =\n      \"reason_skia_stub_sk_color_float_get_r_byte\"\n      \"reason_skia_stub_sk_color_float_get_r\";\n\n    external getG: ([@unboxed] int32) => [@unboxed] float =\n      \"reason_skia_stub_sk_color_float_get_g_byte\"\n      \"reason_skia_stub_sk_color_float_get_g\";\n\n    external getB: ([@unboxed] int32) => [@unboxed] float =\n      \"reason_skia_stub_sk_color_float_get_b_byte\"\n      \"reason_skia_stub_sk_color_float_get_b\";\n  };\n};\n\nmodule FontMetrics = {\n  type t = SkiaWrapped.FontMetrics.t;\n  let make = SkiaWrapped.FontMetrics.make;\n\n  let getAscent = SkiaWrapped.FontMetrics.getAscent;\n  let getDescent = SkiaWrapped.FontMetrics.getDescent;\n  let getTop = SkiaWrapped.FontMetrics.getTop;\n  let getBottom = SkiaWrapped.FontMetrics.getBottom;\n  let getUnderlineThickness = SkiaWrapped.FontMetrics.getUnderlineThickness;\n  let getUnderlinePosition = SkiaWrapped.FontMetrics.getUnderlinePosition;\n  let getAvgCharacterWidth = SkiaWrapped.FontMetrics.getAvgCharacterWidth;\n  let getMaxCharacterWidth = SkiaWrapped.FontMetrics.getMaxCharacterWidth;\n};\n\nmodule FilterQuality = {\n  type t = SkiaWrapped.FilterQuality.t;\n};\n\nmodule Hinting = {\n  type t = SkiaWrapped.Hinting.t;\n};\n\nmodule TextEncoding = {\n  type t = SkiaWrapped.TextEncoding.t;\n};\n\nmodule ImageFilter = {\n  type t = SkiaWrapped.ImageFilter.t;\n\n  module CropRect = {\n    type t = SkiaWrapped.ImageFilter.CropRect.t;\n  };\n\n  module DropShadow = {\n    type shadowMode = SkiaWrapped.ImageFilter.DropShadow.shadowMode;\n\n    let make =\n        (\n          dx,\n          dy,\n          sigmaX,\n          sigmaY,\n          color,\n          shadowMode,\n          inputOption,\n          cropRectOption,\n        ) => {\n      let imageFilter =\n        SkiaWrapped.ImageFilter.DropShadow.allocate(\n          dx,\n          dy,\n          sigmaX,\n          sigmaY,\n          // TODO: Make fast\n          Unsigned.UInt32.of_int32(color),\n          shadowMode,\n          inputOption,\n          cropRectOption,\n        );\n      Gc.finalise(SkiaWrapped.ImageFilter.delete, imageFilter);\n      imageFilter;\n    };\n  };\n\n  let makeDropShadow = DropShadow.make;\n};\n\nmodule Paint = {\n  type t = SkiaWrapped.Paint.t;\n  type style = SkiaWrapped.Paint.style;\n\n  module CI = Cstubs_internals;\n\n  let make = () => {\n    let paint = SkiaWrapped.Paint.allocate();\n    Gc.finalise(SkiaWrapped.Paint.delete, paint);\n    paint;\n  };\n\n  let measureText = (paint, text, rectOpt) => {\n    SkiaWrapped.Paint.measureText(paint, text, String.length(text), rectOpt);\n  };\n\n  [@noalloc]\n  external _setColor: (CI.fatptr(_), [@unboxed] int32) => unit =\n    \"reason_skia_paint_set_color_byte\" \"reason_skia_paint_set_color\";\n\n  [@noalloc]\n  external _setAlphaf: (CI.fatptr(_), [@unboxed] float) => unit =\n    \"reason_skia_paint_set_alphaf_byte\" \"reason_skia_paint_set_alphaf\";\n\n  let setColor = (paint, color) => _setColor(CI.cptr(paint), color);\n  let setAlpha = (paint, alpha) => _setAlphaf(CI.cptr(paint), alpha);\n\n  let getFilterQuality = SkiaWrapped.Paint.getFilterQuality;\n  let setFilterQuality = SkiaWrapped.Paint.setFilterQuality;\n\n  let setHinting = SkiaWrapped.Paint.setHinting;\n  let getHinting = SkiaWrapped.Paint.getHinting;\n\n  let isAutohinted = SkiaWrapped.Paint.isAutohinted;\n  let setAutohinted = SkiaWrapped.Paint.setAutohinted;\n\n  let setAntiAlias = SkiaWrapped.Paint.setAntiAlias;\n  let setStyle = SkiaWrapped.Paint.setStyle;\n  let setStrokeWidth = SkiaWrapped.Paint.setStrokeWidth;\n  let setLcdRenderText = SkiaWrapped.Paint.setLcdRenderText;\n  let setSubpixelText = SkiaWrapped.Paint.setSubpixelText;\n  let setTextSize = SkiaWrapped.Paint.setTextSize;\n  let setTypeface = SkiaWrapped.Paint.setTypeface;\n  let getFontMetrics = SkiaWrapped.Paint.getFontMetrics;\n  let setImageFilter = SkiaWrapped.Paint.setImageFilter;\n  let setPathEffect = SkiaWrapped.Paint.setPathEffect;\n  let getPathEffect = SkiaWrapped.Paint.getPathEffect;\n\n  let setTextEncoding = SkiaWrapped.Paint.setTextEncoding;\n  let getTextEncoding = SkiaWrapped.Paint.getTextEncoding;\n\n  let setShader = SkiaWrapped.Paint.setShader;\n};\n\nmodule Point = {\n  type t = SkiaWrapped.Point.t;\n\n  let make = SkiaWrapped.Point.make;\n\n  let getX = SkiaWrapped.Point.getX;\n  let getY = SkiaWrapped.Point.getY;\n};\n\nmodule Vector = {\n  type t = SkiaWrapped.Vector.t;\n\n  let make = SkiaWrapped.Vector.make;\n\n  let getX = SkiaWrapped.Vector.getX;\n  let getY = SkiaWrapped.Vector.getY;\n};\n\nmodule Shader = {\n  type t = SkiaWrapped.Shader.t;\n\n  type tileMode = SkiaWrapped.Shader.tileMode;\n\n  let makeEmpty = () => {\n    let empty = SkiaWrapped.Shader.empty();\n    Gc.finalise(SkiaWrapped.Shader.unref, empty);\n    empty;\n  };\n\n  let makeLinearGradient2 =\n      (~startPoint, ~stopPoint, ~startColor, ~stopColor, ~tileMode) => {\n    let gradient =\n      SkiaWrapped.Shader.makeLinearGradient2(\n        startPoint,\n        stopPoint,\n        Unsigned.UInt32.of_int32(startColor),\n        Unsigned.UInt32.of_int32(stopColor),\n        tileMode,\n      );\n    Gc.finalise(SkiaWrapped.Shader.unref, gradient);\n    gradient;\n  };\n\n  type colorStop = {\n    color: Color.t,\n    position: float,\n  };\n\n  let makeLinearGradient = (~startPoint, ~stopPoint, ~colorStops, ~tileMode) => {\n    let (colors, positions) =\n      List.fold_left(\n        (acc, curr) => {\n          let (accColors, accPositions) = acc;\n          let {color, position}: colorStop = curr;\n          (\n            [Unsigned.UInt32.of_int32(color), ...accColors],\n            [position, ...accPositions],\n          );\n        },\n        ([], []),\n        colorStops,\n      );\n\n    Ctypes.(\n      {\n        let colorsArray = CArray.of_list(uint32_t, colors |> List.rev);\n        let positionsArray = CArray.of_list(float, positions |> List.rev);\n\n        let gradient =\n          SkiaWrapped.Shader.makeLinearGradient(\n            startPoint,\n            stopPoint,\n            CArray.start(colorsArray),\n            CArray.start(positionsArray),\n            CArray.length(colorsArray),\n            tileMode,\n          );\n        Gc.finalise(SkiaWrapped.Shader.unref, gradient);\n        gradient;\n      }\n    );\n  };\n};\n\nmodule Matrix44 = {\n  type t = SkiaWrapped.Matrix44.t;\n\n  let makeEmpty = () => {\n    let mat = SkiaWrapped.Matrix44.allocate();\n    Gc.finalise(SkiaWrapped.Matrix44.destroy, mat);\n    mat;\n  };\n\n  let makeIdentity = () => {\n    let mat = SkiaWrapped.Matrix44.allocate_identity();\n    Gc.finalise(SkiaWrapped.Matrix44.destroy, mat);\n    mat;\n  };\n\n  let get = SkiaWrapped.Matrix44.get;\n  let set = SkiaWrapped.Matrix44.set;\n  let setRotateAboutDegrees = SkiaWrapped.Matrix44.setRotateAboutDegrees;\n  let setRotateAboutRadians = SkiaWrapped.Matrix44.setRotateAboutRadians;\n\n  let setTranslate = SkiaWrapped.Matrix44.setTranslate;\n  let preTranslate = SkiaWrapped.Matrix44.preTranslate;\n  let postTranslate = SkiaWrapped.Matrix44.postTranslate;\n\n  let setScale = SkiaWrapped.Matrix44.setScale;\n  let preScale = SkiaWrapped.Matrix44.preScale;\n  let postScale = SkiaWrapped.Matrix44.postScale;\n\n  let setConcat = SkiaWrapped.Matrix44.setConcat;\n  let preConcat = SkiaWrapped.Matrix44.preConcat;\n  let postConcat = SkiaWrapped.Matrix44.postConcat;\n\n  let toMatrix = SkiaWrapped.Matrix44.toMatrix;\n};\n\nmodule IRect = {\n  type t = SkiaWrapped.IRect.t;\n\n  let makeEmpty = SkiaWrapped.IRect.makeEmpty;\n  let makeLtrb = SkiaWrapped.IRect.makeLtrb;\n};\n\nmodule Rect = {\n  type t = SkiaWrapped.Rect.t;\n  module CI = Cstubs_internals;\n\n  module Mutable = {\n    [@noalloc]\n    external _set:\n      (\n        CI.fatptr(_),\n        [@unboxed] float,\n        [@unboxed] float,\n        [@unboxed] float,\n        [@unboxed] float\n      ) =>\n      unit =\n      \"reason_skia_rect_set_byte\" \"reason_skia_rect_set\";\n\n    let setLtrb = (~out, left, top, right, bottom) =>\n      _set(CI.cptr(out), left, top, right, bottom);\n  };\n\n  [@noalloc]\n  external _getLeft: CI.fatptr(_) => [@unboxed] float =\n    \"reason_skia_rect_get_left_byte\" \"reason_skia_rect_get_left\";\n\n  [@noalloc]\n  external _getTop: CI.fatptr(_) => [@unboxed] float =\n    \"reason_skia_rect_get_top_byte\" \"reason_skia_rect_get_top\";\n\n  [@noalloc]\n  external _getRight: CI.fatptr(_) => [@unboxed] float =\n    \"reason_skia_rect_get_right_byte\" \"reason_skia_rect_get_right\";\n\n  [@noalloc]\n  external _getBottom: CI.fatptr(_) => [@unboxed] float =\n    \"reason_skia_rect_get_bottom_byte\" \"reason_skia_rect_get_bottom\";\n\n  let getLeft = rect => _getLeft(CI.cptr(rect));\n  let getTop = rect => _getTop(CI.cptr(rect));\n  let getBottom = rect => _getBottom(CI.cptr(rect));\n  let getRight = rect => _getRight(CI.cptr(rect));\n\n  let makeEmpty = SkiaWrapped.Rect.makeEmpty;\n  let makeLtrb = SkiaWrapped.Rect.makeLtrb;\n\n  let toString = rect => {\n    let left = getLeft(rect);\n    let right = getRight(rect);\n    let top = getTop(rect);\n    let bottom = getBottom(rect);\n\n    Printf.sprintf(\n      \"Rect - left: %f top: %f right: %f bottom: %f\",\n      left,\n      top,\n      right,\n      bottom,\n    );\n  };\n};\n\nmodule FontStyle = {\n  type t = SkiaWrapped.FontStyle.t;\n  type slant = SkiaWrapped.FontStyle.slant;\n\n  let getSlant = SkiaWrapped.FontStyle.getSlant;\n  let getWidth = SkiaWrapped.FontStyle.getWidth;\n  let getWeight = SkiaWrapped.FontStyle.getWeight;\n\n  let make = (weight, width, slant) => {\n    let style = SkiaWrapped.FontStyle.make(weight, width, slant);\n    Gc.finalise(SkiaWrapped.FontStyle.delete, style);\n    style;\n  };\n};\n\nmodule FontManager = {\n  type t = SkiaWrapped.FontManager.t;\n\n  let makeDefault = () => {\n    let mgr = SkiaWrapped.FontManager.makeDefault();\n    Gc.finalise(SkiaWrapped.FontManager.delete, mgr);\n    mgr;\n  };\n  let matchFamilyStyle = (mgr, family, style) => {\n    let typeface =\n      SkiaWrapped.FontManager.matchFamilyStyle(mgr, family, style);\n    switch (typeface) {\n    | Some(tf) =>\n      Gc.finalise(SkiaWrapped.Typeface.delete, tf);\n      Some(tf);\n    | None => None\n    };\n  };\n\n  let matchFamilyStyleCharacter = (mgr, family, style, locales, character) => {\n    open Ctypes;\n    let cLocales = CArray.of_list(string, locales);\n\n    let character32 = character |> Uchar.to_int |> Int32.of_int;\n\n    let maybeTypeface =\n      SkiaWrapped.FontManager.matchFamilyStyleCharacter(\n        mgr,\n        family,\n        style,\n        cLocales |> CArray.start,\n        CArray.length(cLocales),\n        character32,\n      );\n\n    switch (maybeTypeface) {\n    | Some(tf) as ret =>\n      Gc.finalise(SkiaWrapped.Typeface.delete, tf);\n      ret;\n    | None => None\n    };\n  };\n};\n\nmodule RRect = {\n  type t = SkiaWrapped.RRect.t;\n  type rRectType = SkiaWrapped.RRect.rRectType;\n  type corner = SkiaWrapped.RRect.corner;\n\n  let make = () => {\n    let rRect = SkiaWrapped.RRect.allocate();\n    Gc.finalise(SkiaWrapped.RRect.delete, rRect);\n    rRect;\n  };\n  let copy = originalRRect => {\n    let rRect = SkiaWrapped.RRect.allocateCopy(originalRRect);\n    Gc.finalise(SkiaWrapped.RRect.delete, rRect);\n    rRect;\n  };\n\n  let getType = SkiaWrapped.RRect.getType;\n  let getRect = SkiaWrapped.RRect.getRect;\n  let getRadii = SkiaWrapped.RRect.getRadii;\n  let getWidth = SkiaWrapped.RRect.getWidth;\n  let getHeight = SkiaWrapped.RRect.getHeight;\n  let setEmpty = SkiaWrapped.RRect.setEmpty;\n  let setRect = SkiaWrapped.RRect.setRect;\n  let setOval = SkiaWrapped.RRect.setOval;\n  let setRectXy = SkiaWrapped.RRect.setRectXy;\n  let setNinePatch = SkiaWrapped.RRect.setNinePatch;\n  let setRectRadii =\n      (\n        rRect,\n        rect,\n        topLeftRadii,\n        topRightRadii,\n        bottomRightRadii,\n        bottomLeftRadii,\n      ) => {\n    let radiiCArray =\n      Ctypes.CArray.make(SkiaWrappedBindings.SkiaTypes.Vector.t, 4);\n    Ctypes.CArray.unsafe_set(radiiCArray, 0, Ctypes.(!@topLeftRadii));\n    Ctypes.CArray.unsafe_set(radiiCArray, 1, Ctypes.(!@topRightRadii));\n    Ctypes.CArray.unsafe_set(radiiCArray, 2, Ctypes.(!@bottomRightRadii));\n    Ctypes.CArray.unsafe_set(radiiCArray, 3, Ctypes.(!@bottomLeftRadii));\n    let radiiPointer = Ctypes.CArray.start(radiiCArray);\n    SkiaWrapped.RRect.setRectRadii(rRect, rect, radiiPointer);\n  };\n  let inset = SkiaWrapped.RRect.inset;\n  let outset = SkiaWrapped.RRect.outset;\n  let offset = SkiaWrapped.RRect.offset;\n  let contains = SkiaWrapped.RRect.contains;\n  let isValid = SkiaWrapped.RRect.isValid;\n  let transform = SkiaWrapped.RRect.transform;\n};\n\nmodule Path = {\n  type t = SkiaWrapped.Path.t;\n  type pathDirection = SkiaWrapped.Path.pathDirection;\n\n  let make = () => {\n    let path = SkiaWrapped.Path.allocate();\n    Gc.finalise(SkiaWrapped.Path.delete, path);\n    path;\n  };\n\n  let addCircle = (path, x, y, ~radius, ~direction: pathDirection=`cw, ()) =>\n    SkiaWrapped.Path.addCircle(path, x, y, radius, direction);\n\n  let addRoundRect = (path, rect, rx, ry, ~direction: pathDirection=`cw, ()) =>\n    SkiaWrapped.Path.addRoundRect(path, rect, rx, ry, direction);\n\n  let moveTo = SkiaWrapped.Path.moveTo;\n  let rMoveTo = SkiaWrapped.Path.rMoveTo;\n  let lineTo = SkiaWrapped.Path.lineTo;\n  let rLineTo = SkiaWrapped.Path.rLineTo;\n  let cubicTo = SkiaWrapped.Path.cubicTo;\n  let rCubicTo = SkiaWrapped.Path.rCubicTo;\n  let quadTo = SkiaWrapped.Path.quadTo;\n  let rQuadTo = SkiaWrapped.Path.rQuadTo;\n  let arcTo = SkiaWrapped.Path.arcTo;\n  let rArcTo = SkiaWrapped.Path.rArcTo;\n  let close = SkiaWrapped.Path.close;\n\n  let getLastPoint = SkiaWrapped.Path.getLastPoint;\n};\n\nmodule Matrix = {\n  type t = SkiaWrapped.Matrix.t;\n\n  let make = SkiaWrapped.Matrix.make;\n  let setAll = SkiaWrapped.Matrix.setAll;\n  let get = SkiaWrapped.Matrix.get;\n  let set = SkiaWrapped.Matrix.set;\n\n  module CI = Cstubs_internals;\n\n  [@noalloc]\n  external _setScale:\n    (\n      CI.fatptr(_),\n      [@unboxed] float,\n      [@unboxed] float,\n      [@unboxed] float,\n      [@unboxed] float\n    ) =>\n    unit =\n    \"reason_skia_matrix_set_scale_byte\" \"reason_skia_matrix_set_scale\";\n\n  [@noalloc]\n  external _setTranslate:\n    (CI.fatptr(_), [@unboxed] float, [@unboxed] float) => unit =\n    \"reason_skia_matrix_set_translate_byte\" \"reason_skia_matrix_set_translate\";\n\n  let setScale = (mat, scaleX, scaleY, pivotX, pivotY) =>\n    _setScale(CI.cptr(mat), scaleX, scaleY, pivotX, pivotY);\n\n  let setTranslate = (matrix, translateX, translateY) =>\n    _setTranslate(CI.cptr(matrix), translateX, translateY);\n\n  let makeAll =\n      (\n        scaleX,\n        skewX,\n        translateX,\n        skewY,\n        scaleY,\n        translateY,\n        perspective0,\n        perspective1,\n        perspective2,\n      ) => {\n    let matrix = make();\n    setAll(\n      matrix,\n      scaleX,\n      skewX,\n      translateX,\n      skewY,\n      scaleY,\n      translateY,\n      perspective0,\n      perspective1,\n      perspective2,\n    );\n    matrix;\n  };\n  let makeScale = (scaleX, scaleY, pivotX, pivotY) => {\n    let matrix = make();\n    setScale(matrix, scaleX, scaleY, pivotX, pivotY);\n    matrix;\n  };\n  let makeTranslate = (translateX, translateY) => {\n    let matrix = make();\n    setTranslate(matrix, translateX, translateY);\n    matrix;\n  };\n\n  let getScaleX = matrix => get(matrix, 0);\n  let getScaleY = matrix => get(matrix, 4);\n  let getSkewX = matrix => get(matrix, 1);\n  let getSkewY = matrix => get(matrix, 3);\n  let getTranslateX = matrix => get(matrix, 2);\n  let getTranslateY = matrix => get(matrix, 5);\n  let getPerspX = matrix => get(matrix, 6);\n  let getPerspY = matrix => get(matrix, 7);\n\n  let setScaleX = (matrix, scaleX) => set(matrix, 0, scaleX);\n  let setScaleY = (matrix, scaleY) => set(matrix, 4, scaleY);\n  let setSkewX = (matrix, skewX) => set(matrix, 1, skewX);\n  let setSkewY = (matrix, skewY) => set(matrix, 3, skewY);\n  let setTranslateX = (matrix, translateX) => set(matrix, 2, translateX);\n  let setTranslateY = (matrix, translateY) => set(matrix, 5, translateY);\n  let setPerspX = (matrix, perspectiveX) => set(matrix, 6, perspectiveX);\n  let setPerspY = (matrix, perspectiveY) => set(matrix, 7, perspectiveY);\n  let setSkew = (matrix, skewX, skewY, pivotX, pivotY) =>\n    setAll(\n      matrix,\n      1.,\n      skewX,\n      -. skewX *. pivotY,\n      skewY,\n      1.,\n      -. skewY *. pivotX,\n      0.,\n      0.,\n      1.,\n    );\n  let setIdentity = matrix =>\n    setAll(matrix, 1., 0., 0., 0., 1., 0., 0., 0., 1.);\n  let reset = setIdentity;\n\n  let invert = SkiaWrapped.Matrix.invert;\n  let concat = SkiaWrapped.Matrix.concat;\n  let preConcat = SkiaWrapped.Matrix.preConcat;\n  let postConcat = SkiaWrapped.Matrix.postConcat;\n  let mapRect = SkiaWrapped.Matrix.mapRect;\n  let mapPoints = SkiaWrapped.Matrix.mapPoints;\n  let mapVectors = SkiaWrapped.Matrix.mapVectors;\n  let mapXy = SkiaWrapped.Matrix.mapXy;\n  let mapVector = SkiaWrapped.Matrix.mapVector;\n  let mapRadius = SkiaWrapped.Matrix.mapRadius;\n\n  let identity = makeAll(1., 0., 0., 0., 1., 0., 0., 0., 1.);\n};\n\nmodule PathEffect = {\n  module Style = {\n    type t = SkiaWrapped.PathEffect.Style.t;\n  };\n\n  type t = SkiaWrapped.PathEffect.t;\n\n  let create1d = (~style, ~advance, ~phase, path) => {\n    let pathEffect =\n      SkiaWrapped.PathEffect.allocate1d(path, advance, phase, style);\n    Gc.finalise(SkiaWrapped.PathEffect.delete, pathEffect);\n    pathEffect;\n  };\n\n  let create2dLine = (~width, ~matrix: Matrix.t) => {\n    let pathEffect = SkiaWrapped.PathEffect.allocate2dLine(width, matrix);\n    Gc.finalise(SkiaWrapped.PathEffect.delete, pathEffect);\n    pathEffect;\n  };\n\n  let create2dPath = (~matrix: Matrix.t, path) => {\n    let pathEffect = SkiaWrapped.PathEffect.allocate2dPath(matrix, path);\n    Gc.finalise(SkiaWrapped.PathEffect.delete, pathEffect);\n    pathEffect;\n  };\n};\n\nmodule ColorSpace = {\n  type t = SkiaWrapped.ColorSpace.t;\n};\n\ntype data = SkiaWrapped.data;\n\nmodule Stream = {\n  type t = SkiaWrapped.Stream.t;\n\n  let hasLength = SkiaWrapped.Stream.hasLength;\n  let getLength = SkiaWrapped.Stream.getLength;\n\n  let makeFileStream = path => {\n    let maybeStream = SkiaWrapped.Stream.makeFileStream(path);\n    maybeStream\n    |> Option.iter(stream =>\n         Gc.finalise(SkiaWrapped.Stream.deleteFileStream, stream)\n       );\n    maybeStream;\n  };\n\n  let makeMemoryStreamFromString = (str, length) => {\n    let stream =\n      SkiaWrapped.Stream.makeMemoryStreamFromString(str, length, true);\n    Gc.finalise(SkiaWrapped.Stream.deleteMemoryStream, stream);\n    stream;\n  };\n\n  let makeMemoryStreamFromData = data => {\n    let stream = SkiaWrapped.Stream.makeMemoryStreamFromData(data);\n    Gc.finalise(SkiaWrapped.Stream.deleteMemoryStream, stream);\n    stream;\n  };\n};\n\nmodule Data = {\n  type t = data;\n  let makeString = data => {\n    let dataPtr =\n      Ctypes.from_voidp(Ctypes.char, SkiaWrapped.Data.getData(data));\n    let dataSize = Unsigned.Size_t.to_int(SkiaWrapped.Data.getSize(data));\n    Ctypes.string_from_ptr(dataPtr, ~length=dataSize);\n  };\n\n  let makeFromFileName = path => {\n    let maybeData = SkiaWrapped.Data.makeFromFileName(path);\n    switch (maybeData) {\n    | Some(data) => Gc.finalise(SkiaWrapped.Data.delete, data)\n    | None => ()\n    };\n    maybeData;\n  };\n\n  let makeFromStream = (stream, length) => {\n    let data = SkiaWrapped.Data.makeFromStream(stream, length);\n    Gc.finalise(SkiaWrapped.Data.delete, data);\n    data;\n  };\n};\n\nmodule Typeface = {\n  type t = SkiaWrapped.Typeface.t;\n\n  let getFamilyName = tf => {\n    let skStr = SkiaWrapped.Typeface.getFamilyName(tf);\n    Gc.finalise(SkiaWrapped.String.delete, skStr);\n    skStr |> SkiaWrapped.String.toString;\n  };\n\n  let makeFromName = SkiaWrapped.Typeface.makeFromName;\n  let makeFromFile = SkiaWrapped.Typeface.makeFromFile;\n\n  let toStream = typeface => {\n    let stream = SkiaWrapped.Typeface.openStream(typeface, None);\n    Gc.finalise(SkiaWrapped.Stream.delete, stream);\n    stream;\n  };\n\n  let getFontStyle = typeface => {\n    let style = SkiaWrapped.Typeface.getFontStyle(typeface);\n    Gc.finalise(SkiaWrapped.FontStyle.delete, style);\n    style;\n  };\n\n  let getUniqueID = SkiaWrapped.Typeface.getUniqueID;\n\n  let equal = (tfA, tfB) => {\n    let styleA = getFontStyle(tfA);\n    let styleB = getFontStyle(tfB);\n\n    String.equal(getFamilyName(tfA), getFamilyName(tfB))\n    && FontStyle.getWidth(styleA) == FontStyle.getWidth(styleB)\n    && FontStyle.getSlant(styleA) == FontStyle.getSlant(styleB)\n    && FontStyle.getWeight(styleA) == FontStyle.getWeight(styleB);\n  };\n};\n\nmodule ImageInfo = {\n  type t = SkiaWrapped.ImageInfo.t;\n\n  let make = SkiaWrapped.ImageInfo.make;\n};\n\nmodule Image = {\n  type t =\n    Ctypes_static.ptr(\n      Ctypes.structure(SkiaWrappedBindings.SkiaTypes.Image.t),\n    );\n\n  let makeFromEncoded = (encodedData, subset) => {\n    switch (SkiaWrapped.Image.allocateFromEncoded(encodedData, subset)) {\n    | Some(image) =>\n      Gc.finalise(SkiaWrapped.Image.delete, image);\n      Some(image);\n    | None => None\n    };\n  };\n\n  let encodeToData = image => {\n    let data = SkiaWrapped.Image.encode(image);\n    Gc.finalise(SkiaWrapped.Data.delete, data);\n    data;\n  };\n\n  let width = SkiaWrapped.Image.width;\n  let height = SkiaWrapped.Image.height;\n};\n\ntype pixelGeometry = SkiaWrapped.pixelGeometry;\n\nmodule Gr = {\n  type surfaceOrigin = SkiaWrapped.Gr.surfaceOrigin;\n\n  module Gl = {\n    module Interface = {\n      type t = SkiaWrapped.Gr.Gl.Interface.t;\n\n      let makeNative = SkiaWrapped.Gr.Gl.Interface.makeNative;\n      let makeSdl2 = SkiaWrapped.Gr.Gl.Interface.makeSdl2;\n      let makeSdl2ES = SkiaWrapped.Gr.Gl.Interface.makeSdl2ES;\n    };\n\n    module FramebufferInfo = {\n      type t = SkiaWrapped.Gr.Gl.FramebufferInfo.t;\n\n      let make = SkiaWrapped.Gr.Gl.FramebufferInfo.make;\n    };\n  };\n\n  module Context = {\n    type t = SkiaWrapped.Gr.Context.t;\n\n    let makeGl = SkiaWrapped.Gr.Context.makeGl;\n  };\n\n  module BackendRenderTarget = {\n    type t = SkiaWrapped.Gr.BackendRenderTarget.t;\n\n    let makeGl = SkiaWrapped.Gr.BackendRenderTarget.makeGl;\n  };\n};\n\ntype clipOp = SkiaWrapped.clipOp;\n\nmodule Canvas = {\n  type t =\n    Ctypes_static.ptr(\n      Ctypes.structure(SkiaWrappedBindings.SkiaTypes.Canvas.t),\n    );\n\n  // TODO: Make fast\n  let clear = (canvas, color) =>\n    SkiaWrapped.Canvas.clear(canvas, Unsigned.UInt32.of_int32(color));\n\n  let drawPaint = SkiaWrapped.Canvas.drawPaint;\n  let drawRect = SkiaWrapped.Canvas.drawRect;\n  let drawRectLtwh = SkiaWrapped.Canvas.drawRectLtwh;\n  let drawRoundRect = SkiaWrapped.Canvas.drawRoundRect;\n  let drawRRect = SkiaWrapped.Canvas.drawRRect;\n  let drawOval = SkiaWrapped.Canvas.drawOval;\n  let drawPath = SkiaWrapped.Canvas.drawPath;\n  let drawCircle = SkiaWrapped.Canvas.drawCircle;\n\n  let drawText = (canvas, text, x, y, paint) => {\n    SkiaWrapped.Canvas.drawText(\n      canvas,\n      text,\n      String.length(text),\n      x,\n      y,\n      paint,\n    );\n  };\n  let drawImage = SkiaWrapped.Canvas.drawImage;\n  let drawImageRect = SkiaWrapped.Canvas.drawImageRect;\n\n  let concat = SkiaWrapped.Canvas.concat;\n  let setMatrix = SkiaWrapped.Canvas.setMatrix;\n  let translate = SkiaWrapped.Canvas.translate;\n  let scale = SkiaWrapped.Canvas.scale;\n  let rotate = SkiaWrapped.Canvas.rotate;\n  let skew = SkiaWrapped.Canvas.skew;\n  let resetMatrix = SkiaWrapped.Canvas.resetMatrix;\n\n  let clipRect = SkiaWrapped.Canvas.clipRect;\n  let clipPath = SkiaWrapped.Canvas.clipPath;\n  let clipRRect = (canvas, rrect, clipOp: clipOp, antiAlias) => {\n    SkiaWrapped.Canvas.clipRRect(canvas, rrect, clipOp, antiAlias);\n  };\n\n  let save = SkiaWrapped.Canvas.save;\n  let saveLayer = SkiaWrapped.Canvas.saveLayer;\n  let restore = SkiaWrapped.Canvas.restore;\n\n  let flush = SkiaWrapped.Canvas.flush;\n};\n\nmodule SurfaceProps = {\n  type t = SkiaWrapped.SurfaceProps.t;\n\n  let make = SkiaWrapped.SurfaceProps.make;\n};\n\nmodule Surface = {\n  type t =\n    Ctypes_static.ptr(\n      Ctypes.structure(SkiaWrappedBindings.SkiaTypes.Surface.t),\n    );\n\n  let makeRaster = (imageInfo, rowBytes, surfaceProps) => {\n    let maybeSurface =\n      SkiaWrapped.Surface.allocateRaster(\n        imageInfo,\n        Unsigned.Size_t.of_int(rowBytes),\n        surfaceProps,\n      );\n    switch (maybeSurface) {\n    | Some(surface) as surf =>\n      Gc.finalise(SkiaWrapped.Surface.delete, surface);\n      surf;\n    | None => None\n    };\n  };\n  let makeRenderTarget =\n      (\n        context,\n        shouldBeBudgeted,\n        imageInfo,\n        sampleCount,\n        surfaceOrigin,\n        surfacePropsOption,\n        shouldCreateWithMips,\n      ) => {\n    let surfaceOption =\n      SkiaWrapped.Surface.allocateRenderTarget(\n        context,\n        shouldBeBudgeted,\n        imageInfo,\n        sampleCount,\n        surfaceOrigin,\n        surfacePropsOption,\n        shouldCreateWithMips,\n      );\n    switch (surfaceOption) {\n    | Some(surface) =>\n      Gc.finalise(SkiaWrapped.Surface.delete, surface);\n      Some(surface);\n    | None => None\n    };\n  };\n  let makeFromBackendRenderTarget =\n      (\n        context,\n        backendRenderTarget,\n        surfaceOrigin,\n        colorType,\n        colorSpaceOption,\n        surfacePropsOption,\n      ) => {\n    let surfaceOption =\n      SkiaWrapped.Surface.allocateFromBackendRenderTarget(\n        context,\n        backendRenderTarget,\n        surfaceOrigin,\n        colorType,\n        colorSpaceOption,\n        surfacePropsOption,\n      );\n    switch (surfaceOption) {\n    | Some(surface) =>\n      Gc.finalise(SkiaWrapped.Surface.delete, surface);\n      Some(surface);\n    | None => None\n    };\n  };\n\n  let draw = (~paint=None, ~canvas, ~x, ~y, surface) =>\n    SkiaWrapped.Surface.draw(surface, canvas, x, y, paint);\n\n  let getCanvas = SkiaWrapped.Surface.getCanvas;\n  let makeImageSnapshot = surface => {\n    let imageSnapshot = SkiaWrapped.Surface.allocateImageSnapshot(surface);\n    Gc.finalise(SkiaWrapped.Image.delete, imageSnapshot);\n    imageSnapshot;\n  };\n\n  let getWidth = SkiaWrapped.Surface.getWidth;\n  let getHeight = SkiaWrapped.Surface.getHeight;\n  let getProps = SkiaWrapped.Surface.getProps;\n};\n\nmodule SVG = {\n  type t = {\n    svg: SkiaWrapped.SVG.t,\n    stream: SkiaWrapped.Stream.t,\n  };\n  let makeFromStream = stream =>\n    SkiaWrapped.SVG.makeFromStream(stream)\n    |> Option.map(svg => {\n         svg |> Gc.finalise(SkiaWrapped.SVG.delete);\n         {svg, stream};\n       });\n  let render = t => SkiaWrapped.SVG.render(t.svg);\n  let setContainerSize = t => SkiaWrapped.SVG.setContainerSize(t.svg);\n  let getContainerWidth = t => SkiaWrapped.SVG.getContainerWidth(t.svg);\n  let getContainerHeight = t => SkiaWrapped.SVG.getContainerHeight(t.svg);\n};\n"
  },
  {
    "path": "packages/reason-skia/src/Skia.rei",
    "content": "type colorType = SkiaWrapped.colorType;\ntype alphaType = SkiaWrapped.alphaType;\ntype data = SkiaWrapped.data;\n\nmodule Color: {\n  type t = int32;\n\n  let makeArgb: (int32, int32, int32, int32) => t;\n  let getA: t => int32;\n  let getR: t => int32;\n  let getG: t => int32;\n  let getB: t => int32;\n\n  module Float: {\n    let makeArgb: (float, float, float, float) => t;\n    let getA: t => float;\n    let getR: t => float;\n    let getG: t => float;\n    let getB: t => float;\n  };\n};\n\nmodule FontStyle: {\n  type t;\n  type slant = SkiaWrapped.FontStyle.slant;\n\n  let make: (int, int, slant) => t;\n\n  let getSlant: t => slant;\n  let getWeight: t => int;\n  let getWidth: t => int;\n};\n\nmodule FilterQuality: {type t = SkiaWrapped.FilterQuality.t;};\nmodule Hinting: {type t = SkiaWrapped.Hinting.t;};\nmodule TextEncoding: {type t = SkiaWrapped.TextEncoding.t;};\n\nmodule Stream: {\n  type t;\n\n  let hasLength: t => bool;\n  let getLength: t => int;\n  let makeFileStream: string => option(t);\n  let makeMemoryStreamFromString: (string, int) => t;\n  let makeMemoryStreamFromData: data => t;\n};\n\nmodule Data: {\n  type t = data;\n\n  let makeFromFileName: string => option(t);\n  let makeString: t => string;\n  let makeFromStream: (Stream.t, int) => t;\n};\n\nmodule Typeface: {\n  type t;\n\n  let getFamilyName: t => string;\n  let makeFromName: (string, FontStyle.t) => option(t);\n  let makeFromFile: (string, int) => option(t);\n  let toStream: t => Stream.t;\n  let getFontStyle: t => FontStyle.t;\n  let getUniqueID: t => int32;\n  let equal: (t, t) => bool;\n};\n\nmodule FontManager: {\n  type t;\n\n  let makeDefault: unit => t;\n  let matchFamilyStyle: (t, string, FontStyle.t) => option(Typeface.t);\n  let matchFamilyStyleCharacter:\n    (t, string, FontStyle.t, list(string), Uchar.t) => option(Typeface.t);\n};\n\nmodule FontMetrics: {\n  type t = SkiaWrapped.FontMetrics.t;\n\n  let make: unit => t;\n  let getAscent: t => float;\n  let getDescent: t => float;\n  let getTop: t => float;\n  let getBottom: t => float;\n  let getUnderlinePosition: t => float;\n  let getUnderlineThickness: t => float;\n  let getAvgCharacterWidth: t => float;\n  let getMaxCharacterWidth: t => float;\n};\n\nmodule ImageFilter: {\n  type t;\n\n  module CropRect: {type t;};\n\n  module DropShadow: {\n    type shadowMode = SkiaWrapped.ImageFilter.DropShadow.shadowMode;\n\n    let make:\n      (\n        float,\n        float,\n        float,\n        float,\n        Color.t,\n        shadowMode,\n        option(t),\n        option(CropRect.t)\n      ) =>\n      t;\n  };\n\n  let makeDropShadow:\n    (\n      float,\n      float,\n      float,\n      float,\n      Color.t,\n      DropShadow.shadowMode,\n      option(t),\n      option(CropRect.t)\n    ) =>\n    t;\n};\n\nmodule Rect: {\n  type t;\n\n  module Mutable: {let setLtrb: (~out: t, float, float, float, float) => unit;\n  };\n\n  let makeEmpty: unit => t;\n  let makeLtrb: (float, float, float, float) => t;\n\n  let getTop: t => float;\n  let getLeft: t => float;\n  let getRight: t => float;\n  let getBottom: t => float;\n\n  let toString: t => string;\n};\n\nmodule Point: {\n  type t;\n\n  let make: (float, float) => t;\n\n  let getX: t => float;\n  let getY: t => float;\n};\n\nmodule Vector: {\n  type t = Point.t;\n\n  let make: (float, float) => t;\n\n  let getX: t => float;\n  let getY: t => float;\n};\n\nmodule Shader: {\n  type t;\n\n  type tileMode = SkiaWrapped.Shader.tileMode;\n\n  let makeEmpty: unit => t;\n\n  type colorStop = {\n    color: Color.t,\n    position: float,\n  };\n\n  let makeLinearGradient2:\n    (\n      ~startPoint: Point.t,\n      ~stopPoint: Point.t,\n      ~startColor: Color.t,\n      ~stopColor: Color.t,\n      ~tileMode: tileMode\n    ) =>\n    t;\n\n  let makeLinearGradient:\n    (\n      ~startPoint: Point.t,\n      ~stopPoint: Point.t,\n      ~colorStops: list(colorStop),\n      ~tileMode: tileMode\n    ) =>\n    t;\n};\n\nmodule Path: {\n  type t;\n\n  let make: unit => t;\n\n  let addRoundRect:\n    (\n      t,\n      Rect.t,\n      float,\n      float,\n      ~direction: SkiaWrapped.Path.pathDirection=?,\n      unit\n    ) =>\n    unit;\n  let addCircle:\n    (\n      t,\n      float,\n      float,\n      ~radius: float,\n      ~direction: SkiaWrapped.Path.pathDirection=?,\n      unit\n    ) =>\n    unit;\n\n  let moveTo: (t, float, float) => unit;\n  let rMoveTo: (t, float, float) => unit;\n  let lineTo: (t, float, float) => unit;\n  let rLineTo: (t, float, float) => unit;\n  let cubicTo: (t, float, float, float, float, float, float) => unit;\n  let rCubicTo: (t, float, float, float, float, float, float) => unit;\n  let quadTo: (t, float, float, float, float) => unit;\n  let rQuadTo: (t, float, float, float, float) => unit;\n  let arcTo:\n    (\n      t,\n      float,\n      float,\n      float,\n      SkiaWrapped.Path.arcSize,\n      SkiaWrapped.Path.pathDirection,\n      float,\n      float\n    ) =>\n    unit;\n  let rArcTo:\n    (\n      t,\n      float,\n      float,\n      float,\n      SkiaWrapped.Path.arcSize,\n      SkiaWrapped.Path.pathDirection,\n      float,\n      float\n    ) =>\n    unit;\n  let close: t => unit;\n\n  let getLastPoint: (t, Point.t) => bool;\n};\n\nmodule Matrix: {\n  type t;\n\n  let identity: t;\n\n  let make: unit => t;\n  let makeAll:\n    (float, float, float, float, float, float, float, float, float) => t;\n  let makeTranslate: (float, float) => t;\n  let makeScale: (float, float, float, float) => t;\n\n  let setAll:\n    (t, float, float, float, float, float, float, float, float, float) => unit;\n  let get: (t, int) => float;\n  let getScaleX: t => float;\n  let getScaleY: t => float;\n  let getSkewX: t => float;\n  let getSkewY: t => float;\n  let getTranslateX: t => float;\n  let getTranslateY: t => float;\n  let getPerspX: t => float;\n  let getPerspY: t => float;\n\n  let set: (t, int, float) => unit;\n  let setScaleX: (t, float) => unit;\n  let setScaleY: (t, float) => unit;\n  let setSkewX: (t, float) => unit;\n  let setSkewY: (t, float) => unit;\n  let setTranslateX: (t, float) => unit;\n  let setTranslateY: (t, float) => unit;\n  let setPerspX: (t, float) => unit;\n  let setPerspY: (t, float) => unit;\n\n  let setTranslate: (t, float, float) => unit;\n  let setScale: (t, float, float, float, float) => unit;\n  let setSkew: (t, float, float, float, float) => unit;\n  let setIdentity: t => unit;\n  let reset: t => unit;\n\n  let invert: (t, t) => bool;\n  let concat: (t, t, t) => unit;\n  let preConcat: (t, t) => unit;\n  let postConcat: (t, t) => unit;\n  let mapRect: (t, Rect.t, Rect.t) => unit;\n  let mapPoints: (t, Point.t, Point.t, int) => unit;\n  let mapVectors: (t, Vector.t, Vector.t, int) => unit;\n  let mapXy: (t, float, float, Point.t) => unit;\n  let mapVector: (t, float, float, Vector.t) => unit;\n  let mapRadius: (t, float) => float;\n};\n\nmodule PathEffect: {\n  module Style: {type t = [ | `translate | `rotate | `morph];};\n\n  type t;\n\n  let create1d: (~style: Style.t, ~advance: float, ~phase: float, Path.t) => t;\n  let create2dLine: (~width: float, ~matrix: Matrix.t) => t;\n  let create2dPath: (~matrix: Matrix.t, Path.t) => t;\n  //let createDiscrete: (~length: float, ~deviation: float, unit) => t;\n};\n\nmodule Paint: {\n  type t;\n  type style = SkiaWrapped.Paint.style;\n\n  let make: unit => t;\n\n  let setColor: (t, Color.t) => unit;\n  let setAntiAlias: (t, bool) => unit;\n\n  let setAutohinted: (t, bool) => unit;\n  let isAutohinted: t => bool;\n\n  let setHinting: (t, Hinting.t) => unit;\n  let getHinting: t => Hinting.t;\n\n  let setFilterQuality: (t, FilterQuality.t) => unit;\n  let getFilterQuality: t => FilterQuality.t;\n\n  let setPathEffect: (t, PathEffect.t) => unit;\n  let getPathEffect: t => PathEffect.t;\n\n  let setStyle: (t, style) => unit;\n  let setStrokeWidth: (t, float) => unit;\n  let setImageFilter: (t, option(ImageFilter.t)) => unit;\n  let setTypeface: (t, Typeface.t) => unit;\n  let setLcdRenderText: (t, bool) => unit;\n  let setSubpixelText: (t, bool) => unit;\n  let setTextSize: (t, float) => unit;\n  let setAlpha: (t, float) => unit;\n  let getFontMetrics: (t, FontMetrics.t, float) => float;\n  let measureText: (t, string, option(Rect.t)) => float;\n\n  let setTextEncoding: (t, TextEncoding.t) => unit;\n  let getTextEncoding: t => TextEncoding.t;\n\n  let setShader: (t, Shader.t) => unit;\n};\n\nmodule IRect: {\n  type t;\n\n  let makeEmpty: unit => t;\n  let makeLtrb: (int32, int32, int32, int32) => t;\n};\n\nmodule Matrix44: {\n  type t;\n\n  let makeIdentity: unit => t;\n  let makeEmpty: unit => t;\n\n  let setRotateAboutDegrees: (t, float, float, float, float) => unit;\n  let setRotateAboutRadians: (t, float, float, float, float) => unit;\n\n  let setTranslate: (t, float, float, float) => unit;\n  let preTranslate: (t, float, float, float) => unit;\n  let postTranslate: (t, float, float, float) => unit;\n\n  let setScale: (t, float, float, float) => unit;\n  let preScale: (t, float, float, float) => unit;\n  let postScale: (t, float, float, float) => unit;\n\n  let setConcat: (t, t, t) => unit;\n  let preConcat: (t, t) => unit;\n  let postConcat: (t, t) => unit;\n\n  let get: (t, int, int) => float;\n  let set: (t, int, int, float) => unit;\n  let toMatrix: (t, Matrix.t) => unit;\n};\n\nmodule RRect: {\n  type t;\n\n  type rRectType = SkiaWrapped.RRect.rRectType;\n  type corner = SkiaWrapped.RRect.corner;\n\n  let make: unit => t;\n  let copy: t => t;\n\n  let getType: t => rRectType;\n  let getRect: (t, Rect.t) => unit;\n  let getRadii: (t, corner, Vector.t) => unit;\n  let getWidth: t => float;\n  let getHeight: t => float;\n  let setEmpty: t => unit;\n  let setRect: (t, Rect.t) => unit;\n  let setOval: (t, Rect.t) => unit;\n  let setRectXy: (t, Rect.t, float, float) => unit;\n  let setNinePatch: (t, Rect.t, float, float, float, float) => unit;\n  let setRectRadii:\n    (t, Rect.t, Vector.t, Vector.t, Vector.t, Vector.t) => unit;\n  let inset: (t, float, float) => unit;\n  let outset: (t, float, float) => unit;\n  let offset: (t, float, float) => unit;\n  let contains: (t, Rect.t) => bool;\n  let isValid: t => bool;\n  let transform: (t, Matrix.t, t) => bool;\n};\n\nmodule ColorSpace: {type t;};\n\nmodule ImageInfo: {\n  type t;\n\n  let make: (int32, int32, colorType, alphaType, option(ColorSpace.t)) => t;\n};\n\nmodule Image: {\n  type t;\n\n  let makeFromEncoded: (Data.t, option(IRect.t)) => option(t);\n  let encodeToData: t => Data.t;\n\n  let width: t => int;\n  let height: t => int;\n};\n\ntype pixelGeometry = SkiaWrapped.pixelGeometry;\n\nmodule Gr: {\n  type surfaceOrigin = SkiaWrapped.Gr.surfaceOrigin;\n\n  module Gl: {\n    module Interface: {\n      type t;\n\n      let makeNative: unit => option(t);\n      let makeSdl2: unit => option(t);\n      let makeSdl2ES: unit => option(t);\n    };\n\n    module FramebufferInfo: {\n      type t;\n\n      let make: (Unsigned.UInt.t, Unsigned.UInt.t) => t;\n    };\n  };\n\n  module Context: {\n    type t;\n\n    let makeGl: option(Gl.Interface.t) => option(t);\n  };\n\n  module BackendRenderTarget: {\n    type t;\n\n    let makeGl: (int, int, int, int, Gl.FramebufferInfo.t) => t;\n  };\n};\n\ntype clipOp = SkiaWrapped.clipOp;\n\nmodule Canvas: {\n  type t;\n\n  let clear: (t, Color.t) => unit;\n  let drawPaint: (t, Paint.t) => unit;\n  let drawRect: (t, Rect.t, Paint.t) => unit;\n  let drawRectLtwh: (t, float, float, float, float, Paint.t) => unit;\n  let drawRoundRect: (t, Rect.t, float, float, Paint.t) => unit;\n  let drawOval: (t, Rect.t, Paint.t) => unit;\n  let drawCircle: (t, float, float, float, Paint.t) => unit;\n  let drawRRect: (t, RRect.t, Paint.t) => unit;\n  let drawPath: (t, Path.t, Paint.t) => unit;\n  let drawText: (t, string, float, float, Paint.t) => unit;\n  let drawImage: (t, Image.t, float, float, option(Paint.t)) => unit;\n  let drawImageRect:\n    (t, Image.t, option(Rect.t), Rect.t, option(Paint.t)) => unit;\n\n  let concat: (t, Matrix.t) => unit;\n  let setMatrix: (t, Matrix.t) => unit;\n  let translate: (t, float, float) => unit;\n  let scale: (t, float, float) => unit;\n  let rotate: (t, float) => unit;\n  let skew: (t, float, float) => unit;\n  let resetMatrix: t => unit;\n\n  let clipRect: (t, Rect.t, clipOp, bool) => unit;\n  let clipPath: (t, Path.t, clipOp, bool) => unit;\n  let clipRRect: (t, RRect.t, clipOp, bool) => unit;\n  let save: t => int;\n  let saveLayer: (t, option(Rect.t), option(Paint.t)) => int;\n  let restore: t => unit;\n  let flush: t => unit;\n};\n\nmodule SurfaceProps: {\n  type t;\n\n  let make: (Unsigned.UInt32.t, pixelGeometry) => t;\n};\n\nmodule Surface: {\n  type t;\n\n  let makeRaster: (ImageInfo.t, int, option(SurfaceProps.t)) => option(t);\n  let makeRenderTarget:\n    (\n      Gr.Context.t,\n      bool,\n      ImageInfo.t,\n      int,\n      Gr.surfaceOrigin,\n      option(SurfaceProps.t),\n      bool\n    ) =>\n    option(t);\n  let makeFromBackendRenderTarget:\n    (\n      Gr.Context.t,\n      Gr.BackendRenderTarget.t,\n      Gr.surfaceOrigin,\n      colorType,\n      option(ColorSpace.t),\n      option(SurfaceProps.t)\n    ) =>\n    option(t);\n\n  // Draw surface [surface] onto a [canvas] at the specified [x,y] points.\n  let draw:\n    (~paint: option(Paint.t)=?, ~canvas: Canvas.t, ~x: float, ~y: float, t) =>\n    unit;\n  let makeImageSnapshot: t => Image.t;\n  let getCanvas: t => Canvas.t;\n  let getWidth: t => int;\n  let getHeight: t => int;\n  let getProps: t => SurfaceProps.t;\n};\n\nmodule SVG: {\n  type t;\n\n  let makeFromStream: Stream.t => option(t);\n  let render: (t, Canvas.t) => unit;\n  let setContainerSize: (t, float, float) => unit;\n  let getContainerWidth: t => float;\n  let getContainerHeight: t => float;\n};\n"
  },
  {
    "path": "packages/reason-skia/src/config/discover.re",
    "content": "module Configurator = Configurator.V1;\n\nlet getenv = name =>\n  try(Sys.getenv(name)) {\n  | Not_found => failwith(\"Error: Undefined environment variable: \" ++ name)\n  };\n\ntype os =\n  | Android\n  | IOS\n  | Linux\n  | Mac\n  | Windows;\n\nlet detect_system_header = {|\n  #if __APPLE__\n    #include <TargetConditionals.h>\n    #if TARGET_OS_IPHONE\n      #define PLATFORM_NAME \"ios\"\n    #else\n      #define PLATFORM_NAME \"mac\"\n    #endif\n  #elif __linux__\n    #if __ANDROID__\n      #define PLATFORM_NAME \"android\"\n    #else\n      #define PLATFORM_NAME \"linux\"\n    #endif\n  #elif WIN32\n    #define PLATFORM_NAME \"windows\"\n  #endif\n|};\n\nlet sdl2FilePath = Sys.getenv(\"SDL2_LIB_PATH\") ++ \"/libSDL2.a\";\n\nlet get_os = t => {\n  let header = {\n    let file = Filename.temp_file(\"discover\", \"os.h\");\n    let fd = open_out(file);\n    output_string(fd, detect_system_header);\n    close_out(fd);\n    file;\n  };\n  let platform =\n    Configurator.C_define.import(\n      t,\n      ~includes=[header],\n      [(\"PLATFORM_NAME\", String)],\n    );\n  switch (platform) {\n  | [(_, String(\"android\"))] => Android\n  | [(_, String(\"ios\"))] => IOS\n  | [(_, String(\"linux\"))] => Linux\n  | [(_, String(\"mac\"))] => Mac\n  | [(_, String(\"windows\"))] => Windows\n  | _ => failwith(\"Unknown operating system\")\n  };\n};\n\nlet ccopt = s => [\"-ccopt\", s];\nlet cclib = s => [\"-cclib\", s];\nlet framework = s => [\"-framework\", s];\nlet flags = os =>\n  switch (os) {\n  | Android =>\n    []\n    @ [\"-verbose\"]\n    @ cclib(\"-lfreetype\")\n    @ cclib(\"-lz\")\n    @ cclib(\"-lskia\")\n    @ cclib(\"-lGLESv2\")\n    @ cclib(\"-lGLESv1_CM\")\n    @ cclib(\"-lm\")\n    @ cclib(\"-llog\")\n    @ cclib(\"-landroid\")\n    @ ccopt(\"-L\" ++ getenv(\"FREETYPE2_LIB_PATH\"))\n    @ ccopt(\"-L\" ++ getenv(\"SDL2_LIB_PATH\"))\n    @ ccopt(\"-L\" ++ getenv(\"SKIA_LIB_PATH\"))\n    @ ccopt(\"-L\" ++ getenv(\"JPEG_LIB_PATH\"))\n    @ ccopt(\"-I\" ++ getenv(\"FREETYPE2_INCLUDE_PATH\"))\n    @ ccopt(\"-I\" ++ getenv(\"SKIA_INCLUDE_PATH\"))\n    @ cclib(\"-ljpeg\")\n    @ ccopt(\"-I/usr/include\")\n    @ ccopt(\"-lstdc++\")\n  | IOS\n  | Mac => []\n  | Linux =>\n    []\n    @ [\"-verbose\"]\n    @ cclib(\"-lfontconfig\")\n    @ cclib(\"-lfreetype\")\n    @ cclib(\"-lz\")\n    @ cclib(\"-lbz2\")\n    @ cclib(\"-lskia\")\n    @ cclib(sdl2FilePath)\n    @ ccopt(\"-L\" ++ getenv(\"FREETYPE2_LIB_PATH\"))\n    @ ccopt(\"-L\" ++ getenv(\"SDL2_LIB_PATH\"))\n    @ ccopt(\"-L\" ++ getenv(\"SKIA_LIB_PATH\"))\n    @ ccopt(\"-L\" ++ getenv(\"JPEG_LIB_PATH\"))\n    @ ccopt(\"-I\" ++ getenv(\"FREETYPE2_INCLUDE_PATH\"))\n    @ ccopt(\"-I\" ++ getenv(\"SKIA_INCLUDE_PATH\"))\n    @ cclib(\"-ljpeg\")\n    @ ccopt(\"-I/usr/include\")\n    @ ccopt(\"-lstdc++\")\n    @ ccopt(\"-fPIC\")\n  | Windows =>\n    []\n    @ cclib(\"-lskia\")\n    @ cclib(\"-lSDL2\")\n    @ ccopt(\"-L\" ++ getenv(\"SDL2_LIB_PATH\"))\n    @ ccopt(\"-L\" ++ getenv(\"SKIA_LIB_PATH\"))\n  };\n\nlet skiaIncludeFlags = {\n  let skiaIncludePath = getenv(\"SKIA_INCLUDE_PATH\");\n  Sys.readdir(skiaIncludePath)\n  |> Array.map(path => \"-I\" ++ skiaIncludePath ++ \"/\" ++ path)\n  |> Array.append([|\"-I\" ++ skiaIncludePath|])\n  |> Array.to_list;\n};\nlet cflags = os => {\n  switch (os) {\n  | Android =>\n    []\n    @ [sdl2FilePath]\n    @ [\"-lGLESv2\"]\n    @ [\"-lGLESv1_CM\"]\n    @ [\"-lm\"]\n    @ [\"-llog\"]\n    @ [\"-landroid\"]\n    @ [\"-lskia\"]\n    @ [\"-I\" ++ getenv(\"SDL2_INCLUDE_PATH\")]\n    @ skiaIncludeFlags\n    @ [\"-L\" ++ getenv(\"SKIA_LIB_PATH\")]\n    @ [\"-L\" ++ getenv(\"SDL2_LIB_PATH\")]\n    @ [\"-L\" ++ getenv(\"JPEG_LIB_PATH\")]\n    @ [\"-lstdc++\"]\n    @ [\"-ljpeg\"]\n  | Linux =>\n    []\n    @ [sdl2FilePath]\n    @ [\"-lskia\"]\n    @ [\"-I\" ++ getenv(\"SDL2_INCLUDE_PATH\")]\n    @ skiaIncludeFlags\n    @ [\"-L\" ++ getenv(\"SKIA_LIB_PATH\")]\n    @ [\"-L\" ++ getenv(\"SDL2_LIB_PATH\")]\n    @ [\"-L\" ++ getenv(\"JPEG_LIB_PATH\")]\n    @ [\"-lstdc++\"]\n    @ [\"-ljpeg\"]\n    @ [\"-fPIC\"]\n  | IOS\n  | Mac => [] @ [\"-I\" ++ getenv(\"SDL2_INCLUDE_PATH\")] @ skiaIncludeFlags\n  | Windows =>\n    []\n    @ [\"-std=c++1y\"]\n    @ [\"-I\" ++ getenv(\"SDL2_INCLUDE_PATH\")]\n    @ [\"-L\" ++ getenv(\"SKIA_LIB_PATH\")]\n    @ [\"-lskia\"]\n    @ skiaIncludeFlags\n  };\n};\n\nlet libs = os =>\n  switch (os) {\n  | Android =>\n    []\n    @ [\n      \"-L\" ++ getenv(\"SDL2_LIB_PATH\"),\n      \"-L\" ++ getenv(\"SKIA_LIB_PATH\"),\n      \"-L\" ++ getenv(\"FREETYPE2_LIB_PATH\"),\n      sdl2FilePath,\n      \"-lGLESv2\",\n      \"-lGLESv1_CM\",\n      \"-lm\",\n      \"-llog\",\n      \"-landroid\",\n      \"-lskia\",\n      \"-lfreetype\",\n      \"-lz\",\n      \"-L\" ++ getenv(\"JPEG_LIB_PATH\"),\n      \"-ljpeg\",\n      \"-lstdc++\",\n    ]\n  | IOS =>\n    []\n    @ [\"-L\" ++ getenv(\"JPEG_LIB_PATH\")]\n    @ [\"-L\" ++ getenv(\"SKIA_LIB_PATH\")]\n    @ [\"-L\" ++ getenv(\"FREETYPE2_LIB_PATH\")]\n    @ [\"-L\" ++ getenv(\"SDL2_LIB_PATH\")]\n    @ framework(\"OpenGLES\")\n    @ framework(\"UIKit\")\n    @ framework(\"Foundation\")\n    @ framework(\"GameController\")\n    @ framework(\"AVFoundation\")\n    @ framework(\"QuartzCore\")\n    @ framework(\"CoreMotion\")\n    @ framework(\"CoreFoundation\")\n    @ framework(\"CoreAudio\")\n    @ framework(\"CoreVideo\")\n    @ framework(\"CoreServices\")\n    @ framework(\"CoreGraphics\")\n    @ framework(\"CoreText\")\n    @ framework(\"CoreFoundation\")\n    @ framework(\"AudioToolbox\")\n    @ framework(\"IOKit\")\n    @ framework(\"Metal\")\n    @ [\"-liconv\"]\n    @ [sdl2FilePath]\n    @ [\"-lskia\"]\n    @ [\"-lstdc++\"]\n    @ [getenv(\"JPEG_LIB_PATH\") ++ \"/libturbojpeg.a\"]\n  | Linux =>\n    []\n    @ [\n      \"-L\" ++ getenv(\"SDL2_LIB_PATH\"),\n      \"-L\" ++ getenv(\"SKIA_LIB_PATH\"),\n      \"-L\" ++ getenv(\"FREETYPE2_LIB_PATH\"),\n      sdl2FilePath,\n      \"-lskia\",\n      \"-lfreetype\",\n      \"-lfontconfig\",\n      \"-lz\",\n      \"-lbz2\",\n      \"-L\" ++ getenv(\"JPEG_LIB_PATH\"),\n      \"-ljpeg\",\n      \"-lpthread\",\n      \"-lstdc++\",\n      \"-fPIC\",\n    ]\n  | Mac =>\n    []\n    @ [\"-L\" ++ getenv(\"JPEG_LIB_PATH\")]\n    @ [\"-L\" ++ getenv(\"SKIA_LIB_PATH\")]\n    @ [\"-L\" ++ getenv(\"FREETYPE2_LIB_PATH\")]\n    @ [\"-L\" ++ getenv(\"SDL2_LIB_PATH\")]\n    @ framework(\"Carbon\")\n    @ framework(\"Cocoa\")\n    @ framework(\"CoreFoundation\")\n    @ framework(\"CoreAudio\")\n    @ framework(\"CoreVideo\")\n    @ framework(\"CoreServices\")\n    @ framework(\"CoreGraphics\")\n    @ framework(\"CoreText\")\n    @ framework(\"CoreFoundation\")\n    @ framework(\"AudioToolbox\")\n    @ framework(\"ForceFeedback\")\n    @ framework(\"IOKit\")\n    @ framework(\"Metal\")\n    @ [\"-liconv\"]\n    @ [sdl2FilePath]\n    @ [\"-lskia\"]\n    @ [\"-lstdc++\"]\n    @ [getenv(\"JPEG_LIB_PATH\") ++ \"/libturbojpeg.a\"]\n  | Windows =>\n    []\n    @ [\"-luser32\"]\n    @ [\"-lskia\"]\n    @ [\"-lSDL2\"]\n    @ [\"-lstdc++\"]\n    @ [\"-L\" ++ getenv(\"SDL2_LIB_PATH\")]\n    @ [\"-L\" ++ getenv(\"SKIA_LIB_PATH\")]\n  };\n\nConfigurator.main(~name=\"reason-sdl2\", conf => {\n  let os = get_os(conf);\n  Configurator.Flags.write_sexp(\"flags.sexp\", flags(os));\n  Configurator.Flags.write_lines(\"c_flags.txt\", cflags(os));\n  Configurator.Flags.write_sexp(\"c_flags.sexp\", cflags(os));\n  Configurator.Flags.write_sexp(\"c_library_flags.sexp\", libs(os));\n  Configurator.Flags.write_lines(\"c_library_flags.txt\", libs(os));\n  Configurator.Flags.write_sexp(\n    \"cclib_c_library_flags.sexp\",\n    libs(os) |> List.map(o => [\"-cclib\", o]) |> List.flatten,\n  );\n});\n"
  },
  {
    "path": "packages/reason-skia/src/config/dune",
    "content": "(executable\n (name discover)\n (libraries dune.configurator))\n"
  },
  {
    "path": "packages/reason-skia/src/dune",
    "content": "(rule\n (targets c_library_flags.sexp c_flags.sexp flags.sexp)\n (deps\n  (:discover config/discover.exe))\n (action\n  (run %{discover})))\n\n(library\n (name skia)\n (public_name reason-skia)\n (library_flags\n  (:include flags.sexp))\n (c_library_flags\n  (:include c_library_flags.sexp))\n (libraries SkiaWrapped))\n"
  },
  {
    "path": "packages/reason-skia/src/wrapped/bindings/SkiaWrappedBindings.re",
    "content": "open Ctypes;\n\nmodule SkiaTypes = SkiaWrappedTypes.M(Skia_generated_type_stubs);\n\nmodule M = (F: FOREIGN) => {\n  // module Ctypes_for_stubs = {\n  //   include Ctypes;\n\n  //   let (@->) = F.(@->);\n  //   let returning = F.returning;\n  //   let foreign = F.foreign;\n  // };\n  // open Ctypes_for_stubs;\n  open F;\n\n  type colorType = SkiaTypes.colorType;\n  let colorType = SkiaTypes.colorType;\n  type alphaType = SkiaTypes.alphaType;\n  let alphaType = SkiaTypes.alphaType;\n\n  module Color = {\n    type t = Unsigned.uint32;\n    let t = uint32_t;\n  };\n\n  type data = ptr(structure(SkiaTypes.Data.t));\n  let data = ptr(SkiaTypes.Data.t);\n\n  module Stream = {\n    type t = ptr(structure(SkiaTypes.Stream.t));\n    let t = ptr(SkiaTypes.Stream.t);\n    let maybeT = ptr_opt(SkiaTypes.Stream.t);\n\n    let hasLength = foreign(\"sk_stream_has_length\", t @-> returning(bool));\n\n    let getLength = foreign(\"sk_stream_get_length\", t @-> returning(int));\n\n    let delete = foreign(\"sk_stream_destroy\", t @-> returning(void));\n\n    let makeFileStream =\n      foreign(\"sk_filestream_new\", string @-> returning(maybeT));\n\n    let deleteFileStream =\n      foreign(\"sk_filestream_destroy\", t @-> returning(void));\n\n    let makeMemoryStreamFromString =\n      foreign(\n        \"sk_memorystream_new_with_data\",\n        string @-> int @-> bool @-> returning(t),\n      );\n\n    let makeMemoryStreamFromData =\n      foreign(\"sk_memorystream_new_with_skdata\", data @-> returning(t));\n\n    let deleteMemoryStream =\n      foreign(\"sk_memorystream_destroy\", t @-> returning(void));\n  };\n\n  module Data = {\n    type t = data;\n    let t = data;\n\n    let makeFromFileName =\n      foreign(\n        \"sk_data_new_from_file\",\n        string @-> returning(ptr_opt(SkiaTypes.Data.t)),\n      );\n    let delete = foreign(\"sk_data_unref\", t @-> returning(void));\n\n    let getData = foreign(\"sk_data_get_data\", t @-> returning(ptr(void)));\n    let getSize = foreign(\"sk_data_get_size\", t @-> returning(size_t));\n\n    let makeFromStream =\n      foreign(\"sk_data_new_from_stream\", Stream.t @-> int @-> returning(t));\n  };\n\n  module String = {\n    type t = ptr(structure(SkiaTypes.String.t));\n    let t = ptr(SkiaTypes.String.t);\n\n    let toString = foreign(\"sk_string_get_c_str\", t @-> returning(string));\n\n    let delete = foreign(\"sk_string_destructor\", t @-> returning(void));\n  };\n\n  module FontStyle = {\n    type t = ptr(structure(SkiaTypes.FontStyle.t));\n    let t = ptr(SkiaTypes.FontStyle.t);\n\n    type slant = SkiaTypes.FontStyle.slant;\n    let slant = SkiaTypes.FontStyle.slant;\n\n    let make =\n      foreign(\"sk_fontstyle_new\", int @-> int @-> slant @-> returning(t));\n\n    let getSlant = foreign(\"sk_fontstyle_get_slant\", t @-> returning(slant));\n\n    let getWeight = foreign(\"sk_fontstyle_get_weight\", t @-> returning(int));\n\n    let getWidth = foreign(\"sk_fontstyle_get_width\", t @-> returning(int));\n\n    let delete = foreign(\"sk_fontstyle_delete\", t @-> returning(void));\n  };\n\n  module TextEncoding = {\n    type t = SkiaTypes.TextEncoding.t;\n    let t = SkiaTypes.TextEncoding.t;\n  };\n\n  module FilterQuality = {\n    type t = SkiaTypes.FilterQuality.t;\n    let t = SkiaTypes.FilterQuality.t;\n  };\n\n  module Hinting = {\n    type t = SkiaTypes.Hinting.t;\n    let t = SkiaTypes.Hinting.t;\n  };\n\n  module Typeface = {\n    type t = ptr(structure(SkiaTypes.Typeface.t));\n    let t = ptr(SkiaTypes.Typeface.t);\n\n    let getFamilyName =\n      foreign(\"sk_typeface_get_family_name\", t @-> returning(String.t));\n\n    let makeFromName =\n      foreign(\n        \"sk_typeface_create_from_name_with_font_style\",\n        string @-> FontStyle.t @-> returning(ptr_opt(SkiaTypes.Typeface.t)),\n      );\n\n    let getFontStyle =\n      foreign(\"sk_typeface_get_fontstyle\", t @-> returning(FontStyle.t));\n\n    let getUniqueID =\n      foreign(\"sk_typeface_get_unique_id\", t @-> returning(int32_t));\n\n    let makeFromFile =\n      foreign(\n        \"sk_typeface_create_from_file\",\n        string @-> int @-> returning(ptr_opt(SkiaTypes.Typeface.t)),\n      );\n    let openStream =\n      foreign(\n        \"sk_typeface_open_stream\",\n        t @-> ptr_opt(int) @-> returning(Stream.t),\n      );\n    let delete = foreign(\"sk_typeface_unref\", t @-> returning(void));\n  };\n\n  module FontManager = {\n    type t = ptr(structure(SkiaTypes.FontManager.t));\n    let t = ptr(SkiaTypes.FontManager.t);\n\n    let makeDefault =\n      foreign(\"sk_fontmgr_create_default\", void @-> returning(t));\n\n    let matchFamilyStyle =\n      foreign(\n        \"sk_fontmgr_match_family_style\",\n        t\n        @-> string\n        @-> FontStyle.t\n        @-> returning(ptr_opt(SkiaTypes.Typeface.t)),\n      );\n\n    let matchFamilyStyleCharacter =\n      foreign(\n        \"sk_fontmgr_match_family_style_character\",\n        t\n        @-> string\n        @-> FontStyle.t\n        @-> ptr(string)\n        @-> int\n        @-> int32_t\n        @-> returning(ptr_opt(SkiaTypes.Typeface.t)),\n      );\n\n    let delete = foreign(\"sk_fontmgr_unref\", t @-> returning(void));\n  };\n\n  module FontMetrics = {\n    type t = ptr(structure(SkiaTypes.FontMetrics.t));\n    let t = ptr(SkiaTypes.FontMetrics.t);\n\n    let make = () => allocate_n(~count=1, SkiaTypes.FontMetrics.t);\n\n    let getAscent = metrics => getf(!@metrics, SkiaTypes.FontMetrics.ascent);\n    let getDescent = metrics =>\n      getf(!@metrics, SkiaTypes.FontMetrics.descent);\n    let getTop = metrics => getf(!@metrics, SkiaTypes.FontMetrics.top);\n    let getBottom = metrics => getf(!@metrics, SkiaTypes.FontMetrics.bottom);\n    let getUnderlineThickness = metrics =>\n      getf(!@metrics, SkiaTypes.FontMetrics.underlineThickness);\n    let getUnderlinePosition = metrics =>\n      getf(!@metrics, SkiaTypes.FontMetrics.underlinePosition);\n    let getMaxCharacterWidth = metrics =>\n      getf(!@metrics, SkiaTypes.FontMetrics.maxCharacterWidth);\n    let getAvgCharacterWidth = metrics =>\n      getf(!@metrics, SkiaTypes.FontMetrics.avgCharacterWidth);\n  };\n\n  module ImageFilter = {\n    type t = ptr(structure(SkiaTypes.ImageFilter.t));\n    let t = ptr(SkiaTypes.ImageFilter.t);\n\n    module CropRect = {\n      type t = ptr(structure(SkiaTypes.ImageFilter.CropRect.t));\n      let t = ptr(SkiaTypes.ImageFilter.CropRect.t);\n    };\n\n    let delete = foreign(\"sk_imagefilter_unref\", t @-> returning(void));\n\n    module DropShadow = {\n      type shadowMode = SkiaTypes.ImageFilter.DropShadow.shadowMode;\n      let shadowMode = SkiaTypes.ImageFilter.DropShadow.shadowMode;\n\n      let allocate =\n        foreign(\n          \"sk_imagefilter_new_drop_shadow\",\n          float\n          @-> float\n          @-> float\n          @-> float\n          @-> Color.t\n          @-> shadowMode\n          @-> ptr_opt(SkiaTypes.ImageFilter.t)\n          @-> ptr_opt(SkiaTypes.ImageFilter.CropRect.t)\n          @-> returning(t),\n        );\n    };\n  };\n\n  module Point = {\n    type t = ptr(structure(SkiaTypes.Point.t));\n    let t = ptr(SkiaTypes.Point.t);\n\n    let make = (x, y) => {\n      let point = allocate_n(SkiaTypes.Point.t, ~count=1);\n      setf(!@point, SkiaTypes.Point.x, x);\n      setf(!@point, SkiaTypes.Point.y, y);\n      point;\n    };\n\n    let getX = point => {\n      getf(!@point, SkiaTypes.Point.x);\n    };\n    let getY = point => {\n      getf(!@point, SkiaTypes.Point.y);\n    };\n  };\n\n  module Shader = {\n    type t = ptr(structure(SkiaTypes.Shader.t));\n    let t = ptr(SkiaTypes.Shader.t);\n\n    type tileMode = SkiaTypes.Shader.tileMode;\n    let tileMode = SkiaTypes.Shader.tileMode;\n\n    let empty =\n      foreign(\n        \"sk_shader_new_empty\",\n        void @-> returning(ptr(SkiaTypes.Shader.t)),\n      );\n\n    let makeLinearGradient2 =\n      foreign(\n        \"reason_skia_stub_linear_gradient2\",\n        Point.t\n        @-> Point.t\n        @-> Color.t\n        @-> Color.t\n        @-> tileMode\n        @-> returning(ptr(SkiaTypes.Shader.t)),\n      );\n\n    let makeLinearGradient =\n      foreign(\n        \"reason_skia_stub_linear_gradient\",\n        Point.t\n        @-> Point.t\n        @-> ptr(Color.t)\n        @-> ptr(float)\n        @-> int\n        @-> tileMode\n        @-> returning(ptr(SkiaTypes.Shader.t)),\n      );\n\n    let unref =\n      foreign(\n        \"sk_shader_unref\",\n        ptr(SkiaTypes.Shader.t) @-> returning(void),\n      );\n  };\n\n  module Rect = {\n    type t = ptr(structure(SkiaTypes.Rect.t));\n    let t = ptr(SkiaTypes.Rect.t);\n\n    let makeEmpty = () => {\n      let rect = allocate_n(SkiaTypes.Rect.t, ~count=1);\n      setf(!@rect, SkiaTypes.Rect.left, 0.);\n      setf(!@rect, SkiaTypes.Rect.top, 0.);\n      setf(!@rect, SkiaTypes.Rect.right, 0.);\n      setf(!@rect, SkiaTypes.Rect.bottom, 0.);\n      rect;\n    };\n    let makeLtrb = (left, top, right, bottom) => {\n      let rect = allocate_n(SkiaTypes.Rect.t, ~count=1);\n      setf(!@rect, SkiaTypes.Rect.left, left);\n      setf(!@rect, SkiaTypes.Rect.top, top);\n      setf(!@rect, SkiaTypes.Rect.right, right);\n      setf(!@rect, SkiaTypes.Rect.bottom, bottom);\n      rect;\n    };\n    let getLeft = rect => {\n      getf(!@rect, SkiaTypes.Rect.left);\n    };\n    let getTop = rect => {\n      getf(!@rect, SkiaTypes.Rect.top);\n    };\n    let getRight = rect => {\n      getf(!@rect, SkiaTypes.Rect.right);\n    };\n    let getBottom = rect => {\n      getf(!@rect, SkiaTypes.Rect.bottom);\n    };\n  };\n\n  module Path = {\n    type t = ptr(structure(SkiaTypes.Path.t));\n    let t = ptr(SkiaTypes.Path.t);\n\n    let allocate = foreign(\"sk_path_new\", void @-> returning(t));\n    let delete = foreign(\"sk_path_delete\", t @-> returning(void));\n\n    type arcSize = SkiaTypes.Path.arcSize;\n    let arcSize = SkiaTypes.Path.arcSize;\n\n    type pathDirection = SkiaTypes.Path.pathDirection;\n    let pathDirection = SkiaTypes.Path.pathDirection;\n\n    let addRoundRect =\n      foreign(\n        \"sk_path_add_rounded_rect\",\n        t @-> Rect.t @-> float @-> float @-> pathDirection @-> returning(void),\n      );\n    let addCircle =\n      foreign(\n        \"sk_path_add_circle\",\n        t @-> float @-> float @-> float @-> pathDirection @-> returning(void),\n      );\n    let moveTo =\n      foreign(\"sk_path_move_to\", t @-> float @-> float @-> returning(void));\n    let rMoveTo =\n      foreign(\"sk_path_rmove_to\", t @-> float @-> float @-> returning(void));\n    let lineTo =\n      foreign(\"sk_path_line_to\", t @-> float @-> float @-> returning(void));\n    let rLineTo =\n      foreign(\"sk_path_rline_to\", t @-> float @-> float @-> returning(void));\n    let cubicTo =\n      foreign(\n        \"sk_path_cubic_to\",\n        t\n        @-> float\n        @-> float\n        @-> float\n        @-> float\n        @-> float\n        @-> float\n        @-> returning(void),\n      );\n    let rCubicTo =\n      foreign(\n        \"sk_path_rcubic_to\",\n        t\n        @-> float\n        @-> float\n        @-> float\n        @-> float\n        @-> float\n        @-> float\n        @-> returning(void),\n      );\n    let quadTo =\n      foreign(\n        \"sk_path_quad_to\",\n        t @-> float @-> float @-> float @-> float @-> returning(void),\n      );\n    let rQuadTo =\n      foreign(\n        \"sk_path_rquad_to\",\n        t @-> float @-> float @-> float @-> float @-> returning(void),\n      );\n    let arcTo =\n      foreign(\n        \"sk_path_arc_to\",\n        t\n        @-> float\n        @-> float\n        @-> float\n        @-> arcSize\n        @-> pathDirection\n        @-> float\n        @-> float\n        @-> returning(void),\n      );\n    let rArcTo =\n      foreign(\n        \"sk_path_rarc_to\",\n        t\n        @-> float\n        @-> float\n        @-> float\n        @-> arcSize\n        @-> pathDirection\n        @-> float\n        @-> float\n        @-> returning(void),\n      );\n    let close = foreign(\"sk_path_close\", t @-> returning(void));\n    let getLastPoint =\n      foreign(\"sk_path_get_last_point\", t @-> Point.t @-> returning(bool));\n  };\n\n  module Vector = {\n    type t = ptr(structure(SkiaTypes.Vector.t));\n    let t = ptr(SkiaTypes.Vector.t);\n\n    let make = (x, y) => {\n      let point = allocate_n(SkiaTypes.Vector.t, ~count=1);\n      setf(!@point, SkiaTypes.Vector.x, x);\n      setf(!@point, SkiaTypes.Vector.y, y);\n      point;\n    };\n\n    let getX = point => {\n      getf(!@point, SkiaTypes.Vector.x);\n    };\n    let getY = point => {\n      getf(!@point, SkiaTypes.Vector.y);\n    };\n  };\n\n  module Matrix = {\n    type t = ptr(structure(SkiaTypes.Matrix.t));\n    let t = ptr(SkiaTypes.Matrix.t);\n\n    let make = () => allocate_n(SkiaTypes.Matrix.t, ~count=1);\n\n    let setAll =\n        (\n          matrix,\n          scaleX,\n          skewX,\n          translateX,\n          skewY,\n          scaleY,\n          translateY,\n          perspective0,\n          perspective1,\n          perspective2,\n        ) => {\n      let mat = getf(!@matrix, SkiaTypes.Matrix.mat);\n      CArray.set(mat, 0, scaleX);\n      CArray.set(mat, 1, skewX);\n      CArray.set(mat, 2, translateX);\n      CArray.set(mat, 3, skewY);\n      CArray.set(mat, 4, scaleY);\n      CArray.set(mat, 5, translateY);\n      CArray.set(mat, 6, perspective0);\n      CArray.set(mat, 7, perspective1);\n      CArray.set(mat, 8, perspective2);\n    };\n    let get = (matrix, index) => {\n      let mat = getf(!@matrix, SkiaTypes.Matrix.mat);\n      CArray.get(mat, index);\n    };\n\n    let set = (matrix, index, value) => {\n      let mat = getf(!@matrix, SkiaTypes.Matrix.mat);\n      CArray.set(mat, index, value);\n    };\n\n    let invert =\n      foreign(\"sk_matrix_try_invert\", t @-> t @-> returning(bool));\n    let concat =\n      foreign(\"sk_matrix_concat\", t @-> t @-> t @-> returning(void));\n    let preConcat =\n      foreign(\"sk_matrix_pre_concat\", t @-> t @-> returning(void));\n    let postConcat =\n      foreign(\"sk_matrix_post_concat\", t @-> t @-> returning(void));\n    let mapRect =\n      foreign(\n        \"sk_matrix_map_rect\",\n        t @-> Rect.t @-> Rect.t @-> returning(void),\n      );\n    let mapPoints =\n      foreign(\n        \"sk_matrix_map_points\",\n        t @-> Point.t @-> Point.t @-> int @-> returning(void),\n      );\n    let mapVectors =\n      foreign(\n        \"sk_matrix_map_vectors\",\n        t @-> Vector.t @-> Vector.t @-> int @-> returning(void),\n      );\n    let mapXy =\n      foreign(\n        \"sk_matrix_map_xy\",\n        t @-> float @-> float @-> Point.t @-> returning(void),\n      );\n    let mapVector =\n      foreign(\n        \"sk_matrix_map_vector\",\n        t @-> float @-> float @-> Vector.t @-> returning(void),\n      );\n    let mapRadius =\n      foreign(\"sk_matrix_map_radius\", t @-> float @-> returning(float));\n  };\n\n  module PathEffect = {\n    module Style = {\n      type t = SkiaTypes.PathEffect.Style.t;\n      let t = SkiaTypes.PathEffect.Style.t;\n    };\n    type t = ptr(structure(SkiaTypes.PathEffect.t));\n    let t = ptr(SkiaTypes.PathEffect.t);\n\n    let delete = foreign(\"sk_path_effect_unref\", t @-> returning(void));\n    let allocate1d =\n      foreign(\n        \"sk_path_effect_create_1d_path\",\n        Path.t\n        @-> float\n        @-> float\n        @-> SkiaTypes.PathEffect.Style.t\n        @-> returning(t),\n      );\n\n    let allocate2dLine =\n      foreign(\n        \"sk_path_effect_create_2d_line\",\n        float @-> Matrix.t @-> returning(t),\n      );\n\n    let allocate2dPath =\n      foreign(\n        \"sk_path_effect_create_2d_path\",\n        Matrix.t @-> Path.t @-> returning(t),\n      );\n  };\n\n  module Paint = {\n    type t = ptr(structure(SkiaTypes.Paint.t));\n    let t = ptr(SkiaTypes.Paint.t);\n    type style = SkiaTypes.Paint.style;\n    let style = SkiaTypes.Paint.style;\n\n    let allocate = foreign(\"sk_paint_new\", void @-> returning(t));\n    let delete = foreign(\"sk_paint_delete\", t @-> returning(void));\n\n    let setColor =\n      foreign(\"sk_paint_set_color\", t @-> Color.t @-> returning(void));\n    let setAntiAlias =\n      foreign(\"sk_paint_set_antialias\", t @-> bool @-> returning(void));\n    let setStyle =\n      foreign(\"sk_paint_set_style\", t @-> style @-> returning(void));\n    let setStrokeWidth =\n      foreign(\"sk_paint_set_stroke_width\", t @-> float @-> returning(void));\n\n    let setTypeface =\n      foreign(\"sk_paint_set_typeface\", t @-> Typeface.t @-> returning(void));\n\n    let setLcdRenderText =\n      foreign(\n        \"sk_paint_set_lcd_render_text\",\n        t @-> bool @-> returning(void),\n      );\n\n    let setSubpixelText =\n      foreign(\"sk_paint_set_subpixel_text\", t @-> bool @-> returning(void));\n\n    let setTextSize =\n      foreign(\"sk_paint_set_textsize\", t @-> float @-> returning(void));\n\n    let getFontMetrics =\n      foreign(\n        \"sk_paint_get_fontmetrics\",\n        t @-> ptr(SkiaTypes.FontMetrics.t) @-> float @-> returning(float),\n      );\n\n    let isAutohinted =\n      foreign(\"sk_paint_is_autohinted\", t @-> returning(bool));\n\n    let setAutohinted =\n      foreign(\"sk_paint_set_autohinted\", t @-> bool @-> returning(void));\n\n    let isAutohinted =\n      foreign(\"sk_paint_is_autohinted\", t @-> returning(bool));\n\n    let getHinting =\n      foreign(\"sk_paint_get_hinting\", t @-> returning(Hinting.t));\n\n    let setHinting =\n      foreign(\"sk_paint_set_hinting\", t @-> Hinting.t @-> returning(void));\n\n    let setFilterQuality =\n      foreign(\n        \"sk_paint_set_filter_quality\",\n        t @-> FilterQuality.t @-> returning(void),\n      );\n\n    let getFilterQuality =\n      foreign(\n        \"sk_paint_get_filter_quality\",\n        t @-> returning(FilterQuality.t),\n      );\n\n    let measureText =\n      foreign(\n        \"sk_paint_measure_text\",\n        t\n        @-> string\n        @-> int\n        @-> ptr_opt(SkiaTypes.Rect.t)\n        @-> returning(float),\n      );\n\n    let setImageFilter =\n      foreign(\n        \"sk_paint_set_imagefilter\",\n        t @-> ptr_opt(SkiaTypes.ImageFilter.t) @-> returning(void),\n      );\n\n    let setPathEffect =\n      foreign(\n        \"sk_paint_set_path_effect\",\n        t @-> ptr(SkiaTypes.PathEffect.t) @-> returning(void),\n      );\n\n    let getPathEffect =\n      foreign(\n        \"sk_paint_get_path_effect\",\n        t @-> returning(ptr(SkiaTypes.PathEffect.t)),\n      );\n\n    let getTextEncoding =\n      foreign(\"sk_paint_get_text_encoding\", t @-> returning(TextEncoding.t));\n\n    let setTextEncoding =\n      foreign(\n        \"sk_paint_set_text_encoding\",\n        t @-> TextEncoding.t @-> returning(void),\n      );\n\n    let setShader =\n      foreign(\"sk_paint_set_shader\", t @-> Shader.t @-> returning(void));\n  };\n\n  module IRect = {\n    type t = ptr(structure(SkiaTypes.IRect.t));\n    let t = ptr(SkiaTypes.IRect.t);\n\n    let makeEmpty = () => {\n      let iRect = allocate_n(SkiaTypes.IRect.t, ~count=1);\n      setf(!@iRect, SkiaTypes.IRect.left, Int32.of_int(0));\n      setf(!@iRect, SkiaTypes.IRect.top, Int32.of_int(0));\n      setf(!@iRect, SkiaTypes.IRect.right, Int32.of_int(0));\n      setf(!@iRect, SkiaTypes.IRect.bottom, Int32.of_int(0));\n      iRect;\n    };\n    let makeLtrb = (left, top, right, bottom) => {\n      let iRect = allocate_n(SkiaTypes.IRect.t, ~count=1);\n      setf(!@iRect, SkiaTypes.IRect.left, left);\n      setf(!@iRect, SkiaTypes.IRect.top, top);\n      setf(!@iRect, SkiaTypes.IRect.right, right);\n      setf(!@iRect, SkiaTypes.IRect.bottom, bottom);\n      iRect;\n    };\n  };\n\n  module Matrix44 = {\n    type t = ptr(structure(SkiaTypes.Matrix44.t));\n    let t = ptr(SkiaTypes.Matrix44.t);\n\n    let allocate = foreign(\"sk_matrix44_new\", void @-> returning(t));\n    let allocate_identity =\n      foreign(\"sk_matrix44_new_identity\", void @-> returning(t));\n    let destroy = foreign(\"sk_matrix44_destroy\", t @-> returning(void));\n\n    let setRotateAboutDegrees =\n      foreign(\n        \"sk_matrix44_set_rotate_about_degrees\",\n        t @-> float @-> float @-> float @-> float @-> returning(void),\n      );\n    let setRotateAboutRadians =\n      foreign(\n        \"sk_matrix44_set_rotate_about_radians\",\n        t @-> float @-> float @-> float @-> float @-> returning(void),\n      );\n\n    let setTranslate =\n      foreign(\n        \"sk_matrix44_set_translate\",\n        t @-> float @-> float @-> float @-> returning(void),\n      );\n    let preTranslate =\n      foreign(\n        \"sk_matrix44_pre_translate\",\n        t @-> float @-> float @-> float @-> returning(void),\n      );\n    let postTranslate =\n      foreign(\n        \"sk_matrix44_post_translate\",\n        t @-> float @-> float @-> float @-> returning(void),\n      );\n\n    let setScale =\n      foreign(\n        \"sk_matrix44_set_scale\",\n        t @-> float @-> float @-> float @-> returning(void),\n      );\n    let preScale =\n      foreign(\n        \"sk_matrix44_pre_scale\",\n        t @-> float @-> float @-> float @-> returning(void),\n      );\n    let postScale =\n      foreign(\n        \"sk_matrix44_post_scale\",\n        t @-> float @-> float @-> float @-> returning(void),\n      );\n\n    let setConcat =\n      foreign(\"sk_matrix44_set_concat\", t @-> t @-> t @-> returning(void));\n    let preConcat =\n      foreign(\"sk_matrix44_pre_concat\", t @-> t @-> returning(void));\n    let postConcat =\n      foreign(\"sk_matrix44_post_concat\", t @-> t @-> returning(void));\n\n    let get =\n      foreign(\"sk_matrix44_get\", t @-> int @-> int @-> returning(float));\n    let set =\n      foreign(\n        \"sk_matrix44_set\",\n        t @-> int @-> int @-> float @-> returning(void),\n      );\n    let toMatrix =\n      foreign(\"sk_matrix44_to_matrix\", t @-> Matrix.t @-> returning(void));\n  };\n\n  module RRect = {\n    type t = ptr(structure(SkiaTypes.RRect.t));\n    let t = ptr(SkiaTypes.RRect.t);\n\n    type rRectType = SkiaTypes.RRect.rRectType;\n    let rRectType = SkiaTypes.RRect.rRectType;\n\n    type corner = SkiaTypes.RRect.corner;\n    let corner = SkiaTypes.RRect.corner;\n\n    let allocate = foreign(\"sk_rrect_new\", void @-> returning(t));\n    let allocateCopy = foreign(\"sk_rrect_new_copy\", t @-> returning(t));\n    let delete = foreign(\"sk_rrect_delete\", t @-> returning(void));\n\n    let getType = foreign(\"sk_rrect_get_type\", t @-> returning(rRectType));\n    let getRect =\n      foreign(\"sk_rrect_get_rect\", t @-> Rect.t @-> returning(void));\n    let getRadii =\n      foreign(\n        \"sk_rrect_get_radii\",\n        t @-> corner @-> Vector.t @-> returning(void),\n      );\n    let getWidth = foreign(\"sk_rrect_get_width\", t @-> returning(float));\n    let getHeight = foreign(\"sk_rrect_get_height\", t @-> returning(float));\n    let setEmpty = foreign(\"sk_rrect_set_empty\", t @-> returning(void));\n    let setRect =\n      foreign(\"sk_rrect_set_rect\", t @-> Rect.t @-> returning(void));\n    let setOval =\n      foreign(\"sk_rrect_set_oval\", t @-> Rect.t @-> returning(void));\n    let setRectXy =\n      foreign(\n        \"sk_rrect_set_rect_xy\",\n        t @-> Rect.t @-> float @-> float @-> returning(void),\n      );\n    let setNinePatch =\n      foreign(\n        \"sk_rrect_set_nine_patch\",\n        t\n        @-> Rect.t\n        @-> float\n        @-> float\n        @-> float\n        @-> float\n        @-> returning(void),\n      );\n    let setRectRadii =\n      foreign(\n        \"sk_rrect_set_rect_radii\",\n        t @-> Rect.t @-> Vector.t @-> returning(void),\n      );\n    let inset =\n      foreign(\"sk_rrect_inset\", t @-> float @-> float @-> returning(void));\n    let outset =\n      foreign(\"sk_rrect_outset\", t @-> float @-> float @-> returning(void));\n    let offset =\n      foreign(\"sk_rrect_offset\", t @-> float @-> float @-> returning(void));\n    let contains =\n      foreign(\"sk_rrect_contains\", t @-> Rect.t @-> returning(bool));\n    let isValid = foreign(\"sk_rrect_is_valid\", t @-> returning(bool));\n    let transform =\n      foreign(\n        \"sk_rrect_transform\",\n        t @-> Matrix.t @-> t @-> returning(bool),\n      );\n  };\n\n  module ColorSpace = {\n    type t = ptr(structure(SkiaTypes.ColorSpace.t));\n    let t = ptr(SkiaTypes.ColorSpace.t);\n  };\n\n  module ImageInfo = {\n    type t = ptr(structure(SkiaTypes.ImageInfo.t));\n    let t = ptr(SkiaTypes.ImageInfo.t);\n\n    let make = (width, height, colorType, alphaType, colorSpace) => {\n      let imageInfo = allocate_n(SkiaTypes.ImageInfo.t, ~count=1);\n      setf(!@imageInfo, SkiaTypes.ImageInfo.width, width);\n      setf(!@imageInfo, SkiaTypes.ImageInfo.height, height);\n      setf(!@imageInfo, SkiaTypes.ImageInfo.colorType, colorType);\n      setf(!@imageInfo, SkiaTypes.ImageInfo.alphaType, alphaType);\n      setf(!@imageInfo, SkiaTypes.ImageInfo.colorSpace, colorSpace);\n      imageInfo;\n    };\n  };\n\n  module Image = {\n    type t = ptr(structure(SkiaTypes.Image.t));\n    let t = ptr(SkiaTypes.Image.t);\n\n    let allocateFromEncoded =\n      foreign(\n        \"sk_image_new_from_encoded\",\n        Data.t\n        @-> ptr_opt(SkiaTypes.IRect.t)\n        @-> returning(ptr_opt(SkiaTypes.Image.t)),\n      );\n    let delete = foreign(\"sk_image_unref\", t @-> returning(void));\n\n    let encode = foreign(\"sk_image_encode\", t @-> returning(Data.t));\n\n    let width = foreign(\"sk_image_get_width\", t @-> returning(int));\n    let height = foreign(\"sk_image_get_height\", t @-> returning(int));\n  };\n\n  type pixelGeometry = SkiaTypes.pixelGeometry;\n  let pixelGeometry = SkiaTypes.pixelGeometry;\n\n  module Gr = {\n    type surfaceOrigin = SkiaTypes.Gr.surfaceOrigin;\n    let surfaceOrigin = SkiaTypes.Gr.surfaceOrigin;\n\n    module Gl = {\n      module Interface = {\n        type t = ptr(structure(SkiaTypes.Gr.Gl.Interface.t));\n        let t = ptr(SkiaTypes.Gr.Gl.Interface.t);\n\n        let makeNative =\n          foreign(\n            \"gr_glinterface_create_native_interface\",\n            void @-> returning(ptr_opt(SkiaTypes.Gr.Gl.Interface.t)),\n          );\n\n        let makeSdl2 =\n          foreign(\n            \"reason_skia_make_sdl2_gl_interface\",\n            void @-> returning(ptr_opt(SkiaTypes.Gr.Gl.Interface.t)),\n          );\n\n        let makeSdl2ES =\n          foreign(\n            \"reason_skia_make_sdl2_gles_interface\",\n            void @-> returning(ptr_opt(SkiaTypes.Gr.Gl.Interface.t)),\n          );\n      };\n\n      module FramebufferInfo = {\n        type t = ptr(structure(SkiaTypes.Gr.Gl.FramebufferInfo.t));\n        let t = ptr(SkiaTypes.Gr.Gl.FramebufferInfo.t);\n\n        let make = (framebufferObjectId, format) => {\n          let framebufferInfo =\n            allocate_n(SkiaTypes.Gr.Gl.FramebufferInfo.t, ~count=1);\n          setf(\n            !@framebufferInfo,\n            SkiaTypes.Gr.Gl.FramebufferInfo.framebufferObjectId,\n            framebufferObjectId,\n          );\n          setf(\n            !@framebufferInfo,\n            SkiaTypes.Gr.Gl.FramebufferInfo.format,\n            format,\n          );\n          framebufferInfo;\n        };\n      };\n    };\n\n    module Context = {\n      type t = ptr(structure(SkiaTypes.Gr.Context.t));\n      let t = ptr(SkiaTypes.Gr.Context.t);\n\n      let makeGl =\n        foreign(\n          \"gr_context_make_gl\",\n          ptr_opt(SkiaTypes.Gr.Gl.Interface.t)\n          @-> returning(ptr_opt(SkiaTypes.Gr.Context.t)),\n        );\n    };\n\n    module BackendRenderTarget = {\n      type t = ptr(structure(SkiaTypes.Gr.BackendRenderTarget.t));\n      let t = ptr(SkiaTypes.Gr.BackendRenderTarget.t);\n\n      let makeGl =\n        foreign(\n          \"gr_backendrendertarget_new_gl\",\n          int\n          @-> int\n          @-> int\n          @-> int\n          @-> Gl.FramebufferInfo.t\n          @-> returning(t),\n        );\n    };\n  };\n\n  type clipOp = SkiaTypes.clipOp;\n  let clipOp = SkiaTypes.clipOp;\n\n  module Canvas = {\n    type t = ptr(structure(SkiaTypes.Canvas.t));\n    let t = ptr(SkiaTypes.Canvas.t);\n\n    let clear =\n      foreign(\"sk_canvas_clear\", t @-> Color.t @-> returning(void));\n\n    let drawPaint =\n      foreign(\"sk_canvas_draw_paint\", t @-> Paint.t @-> returning(void));\n    let drawRect =\n      foreign(\n        \"sk_canvas_draw_rect\",\n        t @-> Rect.t @-> Paint.t @-> returning(void),\n      );\n    let drawRectLtwh =\n      foreign(\n        \"reason_skia_stub_sk_canvas_draw_rect_ltwh\",\n        t\n        @-> float\n        @-> float\n        @-> float\n        @-> float\n        @-> Paint.t\n        @-> returning(void),\n      );\n    let drawRoundRect =\n      foreign(\n        \"sk_canvas_draw_round_rect\",\n        t @-> Rect.t @-> float @-> float @-> Paint.t @-> returning(void),\n      );\n    let drawOval =\n      foreign(\n        \"sk_canvas_draw_oval\",\n        t @-> Rect.t @-> Paint.t @-> returning(void),\n      );\n    let drawCircle =\n      foreign(\n        \"sk_canvas_draw_circle\",\n        t @-> float @-> float @-> float @-> Paint.t @-> returning(void),\n      );\n    let drawRRect =\n      foreign(\n        \"sk_canvas_draw_rrect\",\n        t @-> RRect.t @-> Paint.t @-> returning(void),\n      );\n    let drawPath =\n      foreign(\n        \"sk_canvas_draw_path\",\n        t @-> Path.t @-> Paint.t @-> returning(void),\n      );\n\n    let drawText =\n      foreign(\n        \"sk_canvas_draw_text\",\n        t\n        @-> string\n        @-> int\n        @-> float\n        @-> float\n        @-> Paint.t\n        @-> returning(void),\n      );\n\n    let drawImage =\n      foreign(\n        \"sk_canvas_draw_image\",\n        t\n        @-> Image.t\n        @-> float\n        @-> float\n        @-> ptr_opt(SkiaTypes.Paint.t)\n        @-> returning(void),\n      );\n\n    let drawImageRect =\n      foreign(\n        \"sk_canvas_draw_image_rect\",\n        t\n        @-> Image.t\n        @-> ptr_opt(SkiaTypes.Rect.t)\n        @-> ptr(SkiaTypes.Rect.t)\n        @-> ptr_opt(SkiaTypes.Paint.t)\n        @-> returning(void),\n      );\n\n    let concat =\n      foreign(\"sk_canvas_concat\", t @-> Matrix.t @-> returning(void));\n    let setMatrix =\n      foreign(\"sk_canvas_set_matrix\", t @-> Matrix.t @-> returning(void));\n    let translate =\n      foreign(\n        \"sk_canvas_translate\",\n        t @-> float @-> float @-> returning(void),\n      );\n    let scale =\n      foreign(\"sk_canvas_scale\", t @-> float @-> float @-> returning(void));\n    let rotate =\n      foreign(\"sk_canvas_rotate_degrees\", t @-> float @-> returning(void));\n    let skew =\n      foreign(\"sk_canvas_skew\", t @-> float @-> float @-> returning(void));\n    let resetMatrix =\n      foreign(\"sk_canvas_reset_matrix\", t @-> returning(void));\n\n    let clipRect =\n      foreign(\n        \"sk_canvas_clip_rect_with_operation\",\n        t @-> Rect.t @-> clipOp @-> bool @-> returning(void),\n      );\n    let clipPath =\n      foreign(\n        \"sk_canvas_clip_path_with_operation\",\n        t @-> Path.t @-> clipOp @-> bool @-> returning(void),\n      );\n    let clipRRect =\n      foreign(\n        \"sk_canvas_clip_rrect_with_operation\",\n        t @-> RRect.t @-> clipOp @-> bool @-> returning(void),\n      );\n\n    let save = foreign(\"sk_canvas_save\", t @-> returning(int));\n    let saveLayer =\n      foreign(\n        \"sk_canvas_save_layer\",\n        t\n        @-> ptr_opt(SkiaTypes.Rect.t)\n        @-> ptr_opt(SkiaTypes.Paint.t)\n        @-> returning(int),\n      );\n    let restore = foreign(\"sk_canvas_restore\", t @-> returning(void));\n\n    let flush = foreign(\"sk_canvas_flush\", t @-> returning(void));\n  };\n\n  module SurfaceProps = {\n    type t = ptr(structure(SkiaTypes.SurfaceProps.t));\n    let t = ptr(SkiaTypes.SurfaceProps.t);\n\n    let make =\n      foreign(\n        \"sk_surfaceprops_new\",\n        uint32_t @-> pixelGeometry @-> returning(t),\n      );\n  };\n\n  module Surface = {\n    type t = ptr(structure(SkiaTypes.Surface.t));\n    let t = ptr(SkiaTypes.Surface.t);\n\n    let allocateRaster =\n      foreign(\n        \"sk_surface_new_raster\",\n        ImageInfo.t\n        @-> size_t\n        @-> ptr_opt(SkiaTypes.SurfaceProps.t)\n        @-> returning(ptr_opt(SkiaTypes.Surface.t)),\n      );\n    let allocateRenderTarget =\n      foreign(\n        \"sk_surface_new_render_target\",\n        Gr.Context.t\n        @-> bool\n        @-> ImageInfo.t\n        @-> int\n        @-> Gr.surfaceOrigin\n        @-> ptr_opt(SkiaTypes.SurfaceProps.t)\n        @-> bool\n        @-> returning(ptr_opt(SkiaTypes.Surface.t)),\n      );\n    let allocateFromBackendRenderTarget =\n      foreign(\n        \"sk_surface_new_backend_render_target\",\n        Gr.Context.t\n        @-> Gr.BackendRenderTarget.t\n        @-> Gr.surfaceOrigin\n        @-> colorType\n        @-> ptr_opt(SkiaTypes.ColorSpace.t)\n        @-> ptr_opt(SkiaTypes.SurfaceProps.t)\n        @-> returning(ptr_opt(SkiaTypes.Surface.t)),\n      );\n    let delete = foreign(\"sk_surface_unref\", t @-> returning(void));\n\n    let draw =\n      foreign(\n        \"sk_surface_draw\",\n        t\n        @-> Canvas.t\n        @-> float\n        @-> float\n        @-> ptr_opt(SkiaTypes.Paint.t)\n        @-> returning(void),\n      );\n\n    let getCanvas =\n      foreign(\"sk_surface_get_canvas\", t @-> returning(Canvas.t));\n    let allocateImageSnapshot =\n      foreign(\"sk_surface_new_image_snapshot\", t @-> returning(Image.t));\n\n    let getWidth = foreign(\"sk_surface_get_width\", t @-> returning(int));\n    let getHeight = foreign(\"sk_surface_get_height\", t @-> returning(int));\n    let getProps =\n      foreign(\"sk_surface_get_props\", t @-> returning(SurfaceProps.t));\n  };\n\n  module SVG = {\n    type t = ptr(structure(SkiaTypes.SVG.t));\n    let t = ptr(SkiaTypes.SVG.t);\n    let maybeT = ptr_opt(SkiaTypes.SVG.t);\n\n    let makeFromStream =\n      foreign(\n        \"sk_svgdom_create_from_stream\",\n        Stream.t @-> returning(maybeT),\n      );\n\n    let render =\n      foreign(\"sk_svgdom_render\", t @-> Canvas.t @-> returning(void));\n\n    let setContainerSize =\n      foreign(\n        \"sk_svgdom_set_container_size\",\n        t @-> float @-> float @-> returning(void),\n      );\n\n    let getContainerWidth =\n      foreign(\"sk_svgdom_get_container_width\", t @-> returning(float));\n\n    let getContainerHeight =\n      foreign(\"sk_svgdom_get_container_height\", t @-> returning(float));\n\n    let delete = foreign(\"sk_svgdom_unref\", t @-> returning(void));\n  };\n};\n"
  },
  {
    "path": "packages/reason-skia/src/wrapped/bindings/dune",
    "content": "(rule\n (targets skia_generated_type_stubs.ml)\n (deps ../stubgen/ml_types_stubgen.exe)\n (action\n  (with-stdout-to\n   %{targets}\n   (run %{deps}))))\n\n(library\n (name SkiaWrappedBindings)\n (flags -w -9)\n (public_name reason-skia.wrapped.bindings)\n (libraries reason-skia.wrapped.types reason-skia.wrapped.c ctypes.stubs\n   ctypes integers))\n"
  },
  {
    "path": "packages/reason-skia/src/wrapped/c/c_stubs.c",
    "content": "/*\n * Use this file for building any C-layer functionality that we want to keep out\n * of Reason\n */\n\n#include \"c_stubs.h\"\n\n#include <stdio.h>\n#include <stdlib.h>\n\nvoid reason_skia_stub_sk_canvas_draw_rect_ltwh(sk_canvas_t *canvas, float left,\n        float top, float width,\n        float height,\n        sk_paint_t *paint) {\n    sk_rect_t rect;\n    rect.left = left;\n    rect.top = top;\n    rect.right = left + width;\n    rect.bottom = top + height;\n\n    sk_canvas_draw_rect(canvas, &rect, paint);\n}\n\nvoid *reason_skia_sdl2_get(void *ctx, const char name[]) {\n    return SDL_GL_GetProcAddress(name);\n};\n\ngr_glinterface_t *reason_skia_make_sdl2_gl_interface() {\n    gr_glinterface_t *interface =\n            gr_glinterface_assemble_gl_interface(0, reason_skia_sdl2_get);\n    return interface;\n}\n\ngr_glinterface_t *reason_skia_make_sdl2_gles_interface() {\n    gr_glinterface_t *interface =\n            gr_glinterface_assemble_gles_interface(0, reason_skia_sdl2_get);\n    return interface;\n}\n\nsk_shader_t* reason_skia_stub_linear_gradient2(\n    sk_point_t* startPosition,\n    sk_point_t* stopPosition,\n    sk_color_t startColor,\n    sk_color_t stopColor,\n    sk_shader_tilemode_t tileMode) {\n\n    sk_point_t pts[2];\n    pts[0] = *startPosition;\n    pts[1] = *stopPosition;\n\n    sk_color_t colors[2];\n    colors[0] = startColor;\n    colors[1] = stopColor;\n\n    float stops[2];\n    stops[0] = 0.0f;\n    stops[1] = 1.0f;\n\n    return sk_shader_new_linear_gradient(\n               pts,\n               colors,\n               stops,\n               2,\n               tileMode,\n               NULL\n           );\n}\n\nsk_shader_t* reason_skia_stub_linear_gradient(\n    sk_point_t* startPosition,\n    sk_point_t* stopPosition,\n    sk_color_t* colors,\n    float* positions,\n    int count,\n    sk_shader_tilemode_t tileMode) {\n\n    sk_point_t pts[2];\n    pts[0] = *startPosition;\n    pts[1] = *stopPosition;\n\n    return sk_shader_new_linear_gradient(\n               pts,\n               colors,\n               positions,\n               count,\n               tileMode,\n               NULL\n           );\n}\n"
  },
  {
    "path": "packages/reason-skia/src/wrapped/c/c_stubs.h",
    "content": "#include \"gr_context.h\"\n#include \"sk_canvas.h\"\n#include \"sk_paint.h\"\n#include \"sk_patheffect.h\"\n#include \"sk_shader.h\"\n#include \"sk_types.h\"\n\n#include <SDL2/SDL.h>\n\nvoid reason_skia_stub_sk_canvas_draw_rect_ltwh(sk_canvas_t *canvas, float left,\n        float top, float width,\n        float height, sk_paint_t *paint);\n\ngr_glinterface_t *reason_skia_make_sdl2_gl_interface();\ngr_glinterface_t *reason_skia_make_sdl2_gles_interface();\n\nsk_shader_t* reason_skia_stub_linear_gradient2(\n    sk_point_t* startPosition,\n    sk_point_t* stopPosition,\n    sk_color_t startColor,\n    sk_color_t stopColor,\n    sk_shader_tilemode_t tileMode);\n\nsk_shader_t* reason_skia_stub_linear_gradient(\n    sk_point_t* startPosition,\n    sk_point_t* stopPosition,\n    sk_color_t* colors,\n    float* positions,\n    int count,\n    sk_shader_tilemode_t tileMode);\n"
  },
  {
    "path": "packages/reason-skia/src/wrapped/c/dune",
    "content": "(rule\n (targets c_flags.txt c_library_flags.txt)\n (deps\n  (:discover ../../config/discover.exe))\n (action\n  (run %{discover})))\n\n(library\n (name skia_wrapped_c)\n (public_name reason-skia.wrapped.c)\n (preprocess no_preprocessing)\n (libraries sdl2)\n (foreign_archives skia_wrapped_c_stubs))\n\n(rule\n (targets libskia_wrapped_c_stubs.a)\n (deps c_stubs.o)\n (action\n  (run ar rcs %{targets} %{deps})))\n\n(rule\n (targets dllskia_wrapped_c_stubs.dll)\n (deps c_stubs.o)\n (action\n  (run %{cc} -shared -o %{targets} %{deps} %{read-lines:c_library_flags.txt})))\n\n(rule\n (targets dllskia_wrapped_c_stubs.so)\n (deps c_stubs.o)\n (action\n  (run %{cc} %{read-lines:c_library_flags.txt} -shared -o %{targets} %{deps})))\n\n(rule\n (targets c_stubs.o)\n (deps\n  (:c c_stubs.c c_stubs.h)\n  c_flags.txt)\n (action\n  (run %{cc} %{read-lines:c_flags.txt} -c %{c})))\n"
  },
  {
    "path": "packages/reason-skia/src/wrapped/lib/SkiaWrapped.re",
    "content": "include SkiaWrappedBindings.M(Skia_generated_stubs);\n"
  },
  {
    "path": "packages/reason-skia/src/wrapped/lib/dune",
    "content": "(rule\n (targets skia_generated_stubs.ml)\n (deps ../stubgen/stubgen.exe)\n (action\n  (with-stdout-to\n   %{targets}\n   (run %{deps} -ml))))\n\n(rule\n (targets skia_generated_stubs.c)\n (deps\n  (:stubgen ../stubgen/stubgen.exe))\n (action\n  (with-stdout-to\n   %{targets}\n   (run %{stubgen} -c))))\n\n(library\n (name SkiaWrapped)\n (flags\n  (-w -40 -w +26))\n (public_name reason-skia.wrapped)\n (libraries reason-skia.wrapped.types reason-skia.wrapped.bindings\n   reason-skia.wrapped.c ctypes integers)\n (foreign_stubs\n  (language c)\n  (names skia_generated_stubs raw_bindings)\n  (flags\n   (:include ../../c_flags.sexp)))\n (c_library_flags\n  (:include ../../c_library_flags.sexp)))\n"
  },
  {
    "path": "packages/reason-skia/src/wrapped/lib/raw_bindings.c",
    "content": "\n/*\n * Use this file for any manual, raw bindings - ie,\n * ones that use [@noalloc], [@unboxed].\n */\n\n#include \"c_stubs.h\"\n#include \"sk_canvas.h\"\n#include \"sk_matrix.h\"\n#include \"sk_paint.h\"\n#include \"sk_types.h\"\n#include \"sk_typeface.h\"\n#include \"sk_data.h\"\n#include \"sk_stream.h\"\n\n#include <stdio.h>\n#include <stdlib.h>\n\n#include \"ctypes_cstubs_internals.h\"\n\nCAMLprim value reason_skia_paint_set_color(value vPaint, int32_t color) {\n    sk_paint_t *pPaint = CTYPES_ADDR_OF_FATPTR(vPaint);\n    sk_paint_set_color(pPaint, color);\n    return Val_unit;\n}\n\nCAMLprim value reason_skia_paint_set_alphaf(value vPaint, double alpha) {\n    sk_paint_t *pPaint = CTYPES_ADDR_OF_FATPTR(vPaint);\n\n    int a = (int)(255.0 * alpha);\n    sk_color_t c = sk_paint_get_color(pPaint);\n\n    sk_paint_set_color(pPaint,\n                       sk_color_set_argb(a, sk_color_get_r(c), sk_color_get_g(c),\n                                         sk_color_get_b(c)));\n\n    sk_color_t newColor = sk_paint_get_color(pPaint);\n\n    return Val_unit;\n}\n\nCAMLprim value reason_skia_paint_set_alphaf_byte(value vPaint, value vAlpha) {\n    reason_skia_paint_set_alphaf(vPaint, Double_val(vAlpha));\n    return Val_unit;\n}\n\nCAMLprim value reason_skia_paint_set_color_byte(value vPaint, value vColor) {\n    reason_skia_paint_set_color(vPaint, Int32_val(vColor));\n\n    return Val_unit;\n}\n\ndouble reason_skia_stub_sk_color_float_get_b(int32_t color) {\n    return (double)(sk_color_get_b(color) / 255.0);\n}\n\nCAMLprim value reason_skia_stub_sk_color_float_get_b_byte(value vColor) {\n    return caml_copy_double(\n               reason_skia_stub_sk_color_float_get_b(Int32_val(vColor)));\n}\n\ndouble reason_skia_stub_sk_color_float_get_g(int32_t color) {\n    return (double)(sk_color_get_g(color) / 255.0);\n}\n\nCAMLprim value reason_skia_stub_sk_color_float_get_g_byte(value vColor) {\n    return caml_copy_double(\n               reason_skia_stub_sk_color_float_get_g(Int32_val(vColor)));\n}\n\ndouble reason_skia_stub_sk_color_float_get_r(int32_t color) {\n    return (double)(sk_color_get_r(color) / 255.0);\n}\n\nCAMLprim value reason_skia_stub_sk_color_float_get_r_byte(value vColor) {\n    return caml_copy_double(\n               reason_skia_stub_sk_color_float_get_r(Int32_val(vColor)));\n}\n\ndouble reason_skia_stub_sk_color_float_get_a(int32_t color) {\n    return (double)(sk_color_get_a(color) / 255.0);\n}\n\nCAMLprim value reason_skia_stub_sk_color_float_get_a_byte(value vColor) {\n    return caml_copy_double(\n               reason_skia_stub_sk_color_float_get_a(Int32_val(vColor)));\n}\n\nuint32_t reason_skia_color_float_make_argb(double a, double r, double g,\n        double b) {\n    int iA = (int)(255.0 * a);\n    int iR = (int)(255.0 * r);\n    int iG = (int)(255.0 * g);\n    int iB = (int)(255.0 * b);\n    return (uint32_t)sk_color_set_argb(iA, iR, iG, iB);\n}\n\nCAMLprim value reason_skia_color_float_make_argb_byte(value vA, value vR,\n        value vG, value vB) {\n    return caml_copy_int32(reason_skia_color_float_make_argb(\n                               Double_val(vA), Double_val(vR), Double_val(vG), Double_val(vB)));\n}\n\nuint32_t reason_skia_stub_sk_color_get_a(int32_t color) {\n    return (uint32_t)sk_color_get_a(color);\n}\n\nCAMLprim value reason_skia_stub_sk_color_get_a_byte(value vColor) {\n    return caml_copy_int32(reason_skia_stub_sk_color_get_a(Int32_val(vColor)));\n}\n\nuint32_t reason_skia_stub_sk_color_get_r(int32_t color) {\n    return (uint32_t)sk_color_get_r(color);\n}\n\nCAMLprim value reason_skia_stub_sk_color_get_r_byte(value vColor) {\n    return caml_copy_int32(reason_skia_stub_sk_color_get_r(Int32_val(vColor)));\n}\n\nuint32_t reason_skia_stub_sk_color_get_g(int32_t color) {\n    return (uint32_t)sk_color_get_g(color);\n}\n\nCAMLprim value reason_skia_stub_sk_color_get_g_byte(value vColor) {\n    return caml_copy_int32(reason_skia_stub_sk_color_get_g(Int32_val(vColor)));\n}\n\nuint32_t reason_skia_stub_sk_color_get_b(int32_t color) {\n    return (uint32_t)sk_color_get_b(color);\n}\n\nCAMLprim value reason_skia_stub_sk_color_get_b_byte(value vColor) {\n    return caml_copy_int32(reason_skia_stub_sk_color_get_b(Int32_val(vColor)));\n}\n\nuint32_t reason_skia_stub_sk_color_set_argb(int32_t alpha, int32_t red,\n        int32_t green, int32_t blue) {\n    return (uint32_t)sk_color_set_argb(alpha, red, green, blue);\n}\n\nCAMLprim value reason_skia_stub_sk_color_set_argb_byte(value vAlpha, value vRed,\n        value vGreen,\n        value vBlue) {\n    return caml_copy_int32(reason_skia_stub_sk_color_set_argb(\n                               Int32_val(vAlpha), Int32_val(vRed), Int32_val(vGreen), Int32_val(vBlue)));\n}\n\ndouble reason_skia_rect_get_bottom(value vRect) {\n    sk_rect_t *pRect = CTYPES_ADDR_OF_FATPTR(vRect);\n    return (double)pRect->bottom;\n}\n\nCAMLprim value reason_skia_rect_get_bottom_byte(value vRect) {\n    return caml_copy_double(reason_skia_rect_get_bottom(vRect));\n}\n\ndouble reason_skia_rect_get_right(value vRect) {\n    sk_rect_t *pRect = CTYPES_ADDR_OF_FATPTR(vRect);\n    return (double)pRect->right;\n}\n\nCAMLprim value reason_skia_rect_get_right_byte(value vRect) {\n    return caml_copy_double(reason_skia_rect_get_right(vRect));\n}\n\ndouble reason_skia_rect_get_top(value vRect) {\n    sk_rect_t *pRect = CTYPES_ADDR_OF_FATPTR(vRect);\n    return (double)pRect->top;\n}\n\nCAMLprim value reason_skia_rect_get_top_byte(value vRect) {\n    return caml_copy_double(reason_skia_rect_get_top(vRect));\n}\n\ndouble reason_skia_rect_get_left(value vRect) {\n    sk_rect_t *pRect = CTYPES_ADDR_OF_FATPTR(vRect);\n    return (double)pRect->left;\n}\n\nCAMLprim value reason_skia_rect_get_left_byte(value vRect) {\n    return caml_copy_double(reason_skia_rect_get_left(vRect));\n}\n\nCAMLprim value reason_skia_rect_set(value vRect, double left, double top,\n                                    double right, double bottom) {\n    sk_rect_t *pRect = CTYPES_ADDR_OF_FATPTR(vRect);\n    pRect->left = left;\n    pRect->top = top;\n    pRect->right = right;\n    pRect->bottom = bottom;\n\n    return Val_unit;\n}\n\nCAMLprim value reason_skia_rect_set_byte(value vRect, value vLeft, value vTop,\n        value vRight, value vBottom) {\n    return reason_skia_rect_set(vRect, Double_val(vLeft), Double_val(vTop),\n                                Double_val(vRight), Double_val(vBottom));\n}\n\nCAMLprim value reason_skia_matrix_set_scale(value vMatrix, double scaleX,\n        double scaleY, double pivotX,\n        double pivotY) {\n    float *pMatrix = CTYPES_ADDR_OF_FATPTR(vMatrix);\n    pMatrix[0] = scaleX;\n    pMatrix[1] = 0.0;\n    pMatrix[2] = pivotX - (scaleX * pivotX);\n    pMatrix[3] = 0.0;\n    pMatrix[4] = scaleY;\n    pMatrix[5] = pivotY - (scaleY * pivotY);\n    pMatrix[6] = 0.0;\n    pMatrix[7] = 0.0;\n    pMatrix[8] = 1.0;\n    return Val_unit;\n}\n\nCAMLprim value reason_skia_matrix_set_scale_byte(value vMatrix, value vScaleX,\n        value vScaleY, value vPivotX,\n        value vPivotY) {\n    return reason_skia_matrix_set_scale(vMatrix, Double_val(vScaleX),\n                                        Double_val(vScaleY), Double_val(vPivotX),\n                                        Double_val(vPivotY));\n}\n\nCAMLprim value reason_skia_matrix_set_translate(value vMatrix,\n        double translateX,\n        double translateY) {\n    float *pMatrix = CTYPES_ADDR_OF_FATPTR(vMatrix);\n    pMatrix[0] = 1.0;\n    pMatrix[1] = 0.0;\n    pMatrix[2] = translateX;\n    pMatrix[3] = 0.0;\n    pMatrix[4] = 1.0;\n    pMatrix[5] = translateY;\n    pMatrix[6] = 0.0;\n    pMatrix[7] = 0.0;\n    pMatrix[8] = 1.0;\n    return Val_unit;\n}\n\nCAMLprim value reason_skia_matrix_set_translate_byte(value vMatrix,\n        value vTranslateX,\n        value vTranslateY) {\n    return reason_skia_matrix_set_translate(vMatrix, Double_val(vTranslateX),\n                                            Double_val(vTranslateY));\n}\n\n"
  },
  {
    "path": "packages/reason-skia/src/wrapped/stubgen/dune",
    "content": "(executable\n (name stubgen)\n (package reason-skia)\n (modules stubgen)\n (public_name stubgen.exe)\n (libraries reason-skia.wrapped.bindings ctypes))\n\n(executable\n (name types_stubgen)\n (package reason-skia)\n (modules types_stubgen)\n (public_name types_stubgen.exe)\n (libraries reason-skia.wrapped.types ctypes.stubs ctypes))\n\n(rule\n (targets ml_types_stubgen.c)\n (deps ./types_stubgen.exe)\n (action\n  (with-stdout-to\n   %{targets}\n   (run %{deps}))))\n\n(rule\n (targets ctypes_path.txt)\n (action\n  (with-stdout-to\n   %{targets}\n   (run ocamlfind query ctypes))))\n\n(rule\n (targets ml_types_stubgen.exe)\n (deps\n  (:c ./ml_types_stubgen.c)\n  (:ctypes_path ctypes_path.txt)\n  ../c/libskia_wrapped_c_stubs.a)\n (action\n  (run %{cc} %{c} %{read-lines:../c/c_flags.txt}\n    -I%{read-lines:ctypes_path.txt} -I%{ocaml_where} -o %{targets})))\n"
  },
  {
    "path": "packages/reason-skia/src/wrapped/stubgen/stubgen.ml",
    "content": "let prefix = \"skia_wrapped_stub\"\n\nlet prologue = {|\n#include \"c_stubs.h\"\n#include \"gr_context.h\"\n#include \"sk_canvas.h\"\n#include \"sk_data.h\"\n#include \"sk_image.h\"\n#include \"sk_imagefilter.h\"\n#include \"sk_paint.h\"\n#include \"sk_path.h\"\n#include \"sk_surface.h\"\n#include \"sk_rrect.h\"\n#include \"sk_matrix.h\"\n#include \"sk_typeface.h\"\n#include \"sk_stream.h\"\n#include \"sk_string.h\"\n\n// Enable the SVG functions\n#define ESY_SKIA_SVG\n#include \"sk_svg.h\"\n|}\n\nlet () =\n  let generate_ml, generate_c = ref false, ref false in\n  let () =\n    Arg.(parse [ (\"-ml\", Set generate_ml, \"Generate ML\");\n                 (\"-c\", Set generate_c, \"Generate C\") ])\n      (fun _ -> failwith \"unexpected anonymous argument\")\n      \"stubgen [-ml|-c]\"\n  in\n  match !generate_ml, !generate_c with\n  | false, false\n  | true, true ->\n    failwith \"Exactly one of -ml and -c must be specified\"\n  | true, false ->\n    Cstubs.write_ml Format.std_formatter ~prefix (module SkiaWrappedBindings.M)\n  | false, true ->\n    print_endline prologue;\n    Cstubs.write_c Format.std_formatter ~prefix (module SkiaWrappedBindings.M)\n"
  },
  {
    "path": "packages/reason-skia/src/wrapped/stubgen/types_stubgen.ml",
    "content": "let prefix = \"skia_wrapped_stub\"\n\nlet prologue = \"\n#include \\\"sk_types.h\\\"\n\"\n\nlet () =\n  print_endline prologue;\n  Cstubs.Types.write_c Format.std_formatter (module SkiaWrappedTypes.M)\n"
  },
  {
    "path": "packages/reason-skia/src/wrapped/types/SkiaWrappedTypes.re",
    "content": "open Ctypes;\n\nmodule M = (T: TYPE) => {\n  open T;\n\n  let skiaCEnum = (name, mapping) =>\n    enum(\n      name,\n      ~typedef=true,\n      List.map(\n        ((constructor, constantName)) =>\n          (constructor, constant(constantName, int64_t)),\n        mapping,\n      ),\n      ~unexpected=i =>\n      invalid_arg(Printf.sprintf(\"Unsupported %s enum: %Ld\", name, i))\n    );\n\n  module FilterQuality = {\n    type t = [ | `none | `low | `medium | `high];\n\n    let t: T.typ(t) =\n      skiaCEnum(\n        \"sk_filter_quality_t\",\n        [\n          (`none, \"NONE_SK_FILTER_QUALITY\"),\n          (`low, \"LOW_SK_FILTER_QUALITY\"),\n          (`medium, \"MEDIUM_SK_FILTER_QUALITY\"),\n          (`high, \"HIGH_SK_FILTER_QUALITY\"),\n        ],\n      );\n  };\n  module Hinting = {\n    type t =\n      | NoHinting\n      | SlightHinting\n      | NormalHinting\n      | FullHinting;\n\n    let t =\n      skiaCEnum(\n        \"sk_paint_hinting_t\",\n        [\n          (NoHinting, \"NO_HINTING_SK_PAINT_HINTING\"),\n          (SlightHinting, \"SLIGHT_HINTING_SK_PAINT_HINTING\"),\n          (NormalHinting, \"NORMAL_HINTING_SK_PAINT_HINTING\"),\n          (FullHinting, \"FULL_HINTING_SK_PAINT_HINTING\"),\n        ],\n      );\n  };\n\n  module TextEncoding = {\n    type t =\n      | Utf8\n      | Utf16\n      | Utf32\n      | GlyphId;\n\n    let t =\n      skiaCEnum(\n        \"sk_text_encoding_t\",\n        [\n          (Utf8, \"UTF8_SK_TEXT_ENCODING\"),\n          (Utf16, \"UTF16_SK_TEXT_ENCODING\"),\n          (Utf32, \"UTF32_SK_TEXT_ENCODING\"),\n          (GlyphId, \"GLYPH_ID_SK_TEXT_ENCODING\"),\n        ],\n      );\n  };\n\n  module FontStyle = {\n    type t;\n    let t: typ(structure(t)) = structure(\"sk_fontstyle_t\");\n    let t = typedef(t, \"sk_fontstyle_t\");\n\n    type slant =\n      | Upright\n      | Italic\n      | Oblique;\n\n    let slant =\n      skiaCEnum(\n        \"sk_font_style_slant_t\",\n        [\n          (Upright, \"UPRIGHT_SK_FONT_STYLE_SLANT\"),\n          (Italic, \"ITALIC_SK_FONT_STYLE_SLANT\"),\n          (Oblique, \"OBLIQUE_SK_FONT_STYLE_SLANT\"),\n        ],\n      );\n  };\n\n  module Data = {\n    type t;\n    let t: typ(structure(t)) = structure(\"sk_data_t\");\n    let t = typedef(t, \"sk_data_t\");\n  };\n\n  module Stream = {\n    type t;\n    let t: typ(structure(t)) = structure(\"sk_stream_t\");\n    let t = typedef(t, \"sk_stream_t\");\n  };\n\n  module String = {\n    type t;\n    let t: typ(structure(t)) = structure(\"sk_string_t\");\n    let t = typedef(t, \"sk_string_t\");\n  };\n\n  module Typeface = {\n    type t;\n    let t: typ(structure(t)) = structure(\"sk_typeface_t\");\n    let t = typedef(t, \"sk_typeface_t\");\n  };\n\n  module FontManager = {\n    type t;\n\n    let t: typ(structure(t)) = structure(\"sk_fontmgr_t\");\n    let t = typedef(t, \"sk_fontmgr_t\");\n  };\n\n  module FontMetrics = {\n    type t;\n    let t: typ(structure(t)) = structure(\"sk_fontmetrics_t\");\n    let flags = field(t, \"fFlags\", uint32_t);\n    let top = field(t, \"fTop\", float);\n    let ascent = field(t, \"fAscent\", float);\n    let descent = field(t, \"fDescent\", float);\n    let bottom = field(t, \"fBottom\", float);\n    let leading = field(t, \"fLeading\", float);\n    let avgCharWidth = field(t, \"fAvgCharWidth\", float);\n    let maxCharWidth = field(t, \"fMaxCharWidth\", float);\n    let xMin = field(t, \"fXMin\", float);\n    let xMax = field(t, \"fXMax\", float);\n    let xHeight = field(t, \"fXHeight\", float);\n    let capHeight = field(t, \"fCapHeight\", float);\n    let underlineThickness = field(t, \"fUnderlineThickness\", float);\n    let underlinePosition = field(t, \"fUnderlinePosition\", float);\n    let strikeoutThickness = field(t, \"fStrikeoutThickness\", float);\n    let strikeoutPosition = field(t, \"fStrikeoutPosition\", float);\n    let maxCharacterWidth = field(t, \"fMaxCharWidth\", float);\n    let avgCharacterWidth = field(t, \"fAvgCharWidth\", float);\n    seal(t);\n    let t = typedef(t, \"sk_fontmetrics_t\");\n  };\n\n  module ImageFilter = {\n    type t;\n    let t: typ(structure(t)) = structure(\"sk_imagefilter_t\");\n    let t = typedef(t, \"sk_imagefilter_t\");\n\n    module CropRect = {\n      type t;\n      let t: typ(structure(t)) = structure(\"sk_imagefilter_croprect_t\");\n      let t = typedef(t, \"sk_imagefilter_croprect_t\");\n    };\n\n    module DropShadow = {\n      type shadowMode =\n        | DrawShadowAndForeground\n        | DrawShadowOnly;\n      let shadowMode =\n        skiaCEnum(\n          \"sk_drop_shadow_image_filter_shadow_mode_t\",\n          [\n            (\n              DrawShadowAndForeground,\n              \"DRAW_SHADOW_AND_FOREGROUND_SK_DROP_SHADOW_IMAGE_FILTER_SHADOW_MODE\",\n            ),\n            (\n              DrawShadowOnly,\n              \"DRAW_SHADOW_ONLY_SK_DROP_SHADOW_IMAGE_FILTER_SHADOW_MODE\",\n            ),\n          ],\n        );\n    };\n  };\n\n  module PathEffect = {\n    module Style = {\n      type t = [ | `translate | `rotate | `morph];\n\n      let t: T.typ(t) =\n        skiaCEnum(\n          \"sk_path_effect_1d_style_t\",\n          [\n            (`translate, \"TRANSLATE_SK_PATH_EFFECT_1D_STYLE\"),\n            (`rotate, \"ROTATE_SK_PATH_EFFECT_1D_STYLE\"),\n            (`morph, \"MORPH_SK_PATH_EFFECT_1D_STYLE\"),\n          ],\n        );\n    };\n\n    type t;\n    let t: typ(structure(t)) = structure(\"sk_path_effect_t\");\n    let t = typedef(t, \"sk_path_effect_t\");\n  };\n\n  module Paint = {\n    type t;\n    let t: typ(structure(t)) = structure(\"sk_paint_t\");\n    let t = typedef(t, \"sk_paint_t\");\n\n    type style =\n      | Fill\n      | Stroke\n      | StrokeAndFill;\n    let style =\n      skiaCEnum(\n        \"sk_paint_style_t\",\n        [\n          (Fill, \"FILL_SK_PAINT_STYLE\"),\n          (Stroke, \"STROKE_SK_PAINT_STYLE\"),\n          (StrokeAndFill, \"STROKE_AND_FILL_SK_PAINT_STYLE\"),\n        ],\n      );\n  };\n\n  module Point = {\n    type t;\n    let t: typ(structure(t)) = structure(\"sk_point_t\");\n    let x = field(t, \"x\", float);\n    let y = field(t, \"y\", float);\n    seal(t);\n    let t = typedef(t, \"sk_point_t\");\n  };\n\n  module Shader = {\n    type tileMode = [ | `clamp | `repeat | `mirror];\n\n    let tileMode: T.typ(tileMode) =\n      skiaCEnum(\n        \"sk_shader_tilemode_t\",\n        [\n          (`clamp, \"CLAMP_SK_SHADER_TILEMODE\"),\n          (`repeat, \"REPEAT_SK_SHADER_TILEMODE\"),\n          (`mirror, \"MIRROR_SK_SHADER_TILEMODE\"),\n        ],\n      );\n\n    type t;\n    let t: typ(structure(t)) = structure(\"sk_shader_t\");\n    let t = typedef(t, \"sk_shader_t\");\n  };\n\n  module Vector = Point;\n\n  module Matrix = {\n    type t;\n    let t: typ(structure(t)) = structure(\"sk_matrix_t\");\n    let mat = field(t, \"mat\", array(9, float));\n    seal(t);\n    let t = typedef(t, \"sk_matrix_t\");\n  };\n\n  module Matrix44 = {\n    type t;\n    let t: typ(structure(t)) = structure(\"sk_matrix44_t\");\n    let t = typedef(t, \"sk_matrix44_t\");\n  };\n\n  module IRect = {\n    type t;\n    let t: typ(structure(t)) = structure(\"sk_irect_t\");\n    let left = field(t, \"left\", int32_t);\n    let top = field(t, \"top\", int32_t);\n    let right = field(t, \"right\", int32_t);\n    let bottom = field(t, \"bottom\", int32_t);\n    seal(t);\n    let t = typedef(t, \"sk_irect_t\");\n  };\n\n  module Rect = {\n    type t;\n    let t: typ(structure(t)) = structure(\"sk_rect_t\");\n    let left = field(t, \"left\", float);\n    let top = field(t, \"top\", float);\n    let right = field(t, \"right\", float);\n    let bottom = field(t, \"bottom\", float);\n    seal(t);\n    let t = typedef(t, \"sk_rect_t\");\n  };\n\n  module RRect = {\n    type t;\n    let t: typ(structure(t)) = structure(\"sk_rrect_t\");\n    let t = typedef(t, \"sk_rrect_t\");\n\n    // this should be called \"type\" only but that's a reserved keyword\n    type rRectType =\n      | Empty\n      | Rect\n      | Oval\n      | Simple\n      | NinePatch\n      | Complex;\n    let rRectType =\n      skiaCEnum(\n        \"sk_rrect_type_t\",\n        [\n          (Empty, \"EMPTY_SK_RRECT_TYPE\"),\n          (Rect, \"RECT_SK_RRECT_TYPE\"),\n          (Oval, \"OVAL_SK_RRECT_TYPE\"),\n          (Simple, \"SIMPLE_SK_RRECT_TYPE\"),\n          (NinePatch, \"NINE_PATCH_SK_RRECT_TYPE\"),\n          (Complex, \"COMPLEX_SK_RRECT_TYPE\"),\n        ],\n      );\n\n    type corner =\n      | UpperLeft\n      | UpperRight\n      | LowerLeft\n      | LowerRight;\n    let corner =\n      skiaCEnum(\n        \"sk_rrect_corner_t\",\n        [\n          (UpperLeft, \"UPPER_LEFT_SK_RRECT_CORNER\"),\n          (UpperRight, \"UPPER_RIGHT_SK_RRECT_CORNER\"),\n          (LowerRight, \"LOWER_RIGHT_SK_RRECT_CORNER\"),\n          (LowerLeft, \"LOWER_LEFT_SK_RRECT_CORNER\"),\n        ],\n      );\n  };\n\n  module Path = {\n    type arcSize = [ | `small | `large];\n\n    let arcSize: T.typ(arcSize) =\n      skiaCEnum(\n        \"sk_path_arc_size_t\",\n        [\n          (`small, \"SMALL_SK_PATH_ARC_SIZE\"),\n          (`large, \"LARGE_SK_PATH_ARC_SIZE\"),\n        ],\n      );\n\n    type pathDirection = [ | `cw | `ccw];\n\n    let pathDirection: T.typ(pathDirection) =\n      skiaCEnum(\n        \"sk_path_direction_t\",\n        [(`cw, \"CW_SK_PATH_DIRECTION\"), (`ccw, \"CCW_SK_PATH_DIRECTION\")],\n      );\n\n    type t;\n    let t: typ(structure(t)) = structure(\"sk_path_t\");\n    let t = typedef(t, \"sk_path_t\");\n  };\n\n  module ColorSpace = {\n    type t;\n    let t: typ(structure(t)) = structure(\"sk_colorspace_t\");\n    let t = typedef(t, \"sk_colorspace_t\");\n  };\n\n  type colorType =\n    | Unknown\n    | Alpha8\n    | Rgb565\n    | Argb4444\n    | Rgba8888\n    | Rgb888x\n    | Bgra8888\n    | Rgba1010102\n    | Rgb101010x\n    | Gray8\n    | RgbaF16;\n  let colorType =\n    skiaCEnum(\n      \"sk_colortype_t\",\n      [\n        (Unknown, \"UNKNOWN_SK_COLORTYPE\"),\n        (Alpha8, \"ALPHA_8_SK_COLORTYPE\"),\n        (Rgb565, \"RGB_565_SK_COLORTYPE\"),\n        (Argb4444, \"ARGB_4444_SK_COLORTYPE\"),\n        (Rgba8888, \"RGBA_8888_SK_COLORTYPE\"),\n        (Rgb888x, \"RGB_888X_SK_COLORTYPE\"),\n        (Bgra8888, \"BGRA_8888_SK_COLORTYPE\"),\n        (Rgba1010102, \"RGBA_1010102_SK_COLORTYPE\"),\n        (Rgb101010x, \"RGB_101010X_SK_COLORTYPE\"),\n        (Gray8, \"GRAY_8_SK_COLORTYPE\"),\n        (RgbaF16, \"RGBA_F16_SK_COLORTYPE\"),\n      ],\n    );\n\n  type alphaType =\n    | Unknown\n    | Opaque\n    | Premul\n    | Unpremul;\n  let alphaType =\n    skiaCEnum(\n      \"sk_alphatype_t\",\n      [\n        (Unknown, \"UNKNOWN_SK_ALPHATYPE\"),\n        (Opaque, \"OPAQUE_SK_ALPHATYPE\"),\n        (Premul, \"PREMUL_SK_ALPHATYPE\"),\n        (Unpremul, \"UNPREMUL_SK_ALPHATYPE\"),\n      ],\n    );\n\n  module ImageInfo = {\n    type t;\n    let t: typ(structure(t)) = structure(\"sk_imageinfo_t\");\n    let colorSpace = field(t, \"colorspace\", ptr_opt(ColorSpace.t));\n    let width = field(t, \"width\", int32_t);\n    let height = field(t, \"height\", int32_t);\n    let colorType = field(t, \"colorType\", colorType);\n    let alphaType = field(t, \"alphaType\", alphaType);\n    seal(t);\n    let t = typedef(t, \"sk_imageinfo_t\");\n  };\n\n  module Image = {\n    type t;\n    let t: typ(structure(t)) = structure(\"sk_image_t\");\n    let t = typedef(t, \"sk_image_t\");\n  };\n\n  module SVG = {\n    type t;\n    let t: typ(structure(t)) = structure(\"sk_svgdom_t\");\n    let t = typedef(t, \"sk_svgdom_t\");\n  };\n\n  type pixelGeometry =\n    | Unknown\n    | RgbH\n    | BgrH\n    | RgbV\n    | BgrV;\n  let pixelGeometry =\n    skiaCEnum(\n      \"sk_pixelgeometry_t\",\n      [\n        (Unknown, \"UNKNOWN_SK_PIXELGEOMETRY\"),\n        (RgbH, \"RGB_H_SK_PIXELGEOMETRY\"),\n        (BgrH, \"BGR_H_SK_PIXELGEOMETRY\"),\n        (RgbV, \"RGB_V_SK_PIXELGEOMETRY\"),\n        (BgrV, \"RGB_V_SK_PIXELGEOMETRY\"),\n      ],\n    );\n\n  module Gr = {\n    type surfaceOrigin =\n      | TopLeft\n      | BottomLeft;\n    let surfaceOrigin =\n      skiaCEnum(\n        \"gr_surfaceorigin_t\",\n        [\n          (TopLeft, \"TOP_LEFT_GR_SURFACE_ORIGIN\"),\n          (BottomLeft, \"BOTTOM_LEFT_GR_SURFACE_ORIGIN\"),\n        ],\n      );\n\n    module Gl = {\n      module Interface = {\n        type t;\n        let t: typ(structure(t)) = structure(\"gr_glinterface_t\");\n        let t = typedef(t, \"gr_glinterface_t\");\n      };\n\n      module FramebufferInfo = {\n        type t;\n        let t: typ(structure(t)) = structure(\"gr_gl_framebufferinfo_t\");\n        let framebufferObjectId = field(t, \"fFBOID\", uint);\n        let format = field(t, \"fFormat\", uint); // TODO this could also be an enum?\n        seal(t);\n        let t = typedef(t, \"gr_gl_framebufferinfo_t\");\n      };\n    };\n\n    module Context = {\n      type t;\n      let t: typ(structure(t)) = structure(\"gr_context_t\");\n      let t = typedef(t, \"gr_context_t\");\n    };\n\n    module BackendRenderTarget = {\n      type t;\n      let t: typ(structure(t)) = structure(\"gr_backendrendertarget_t\");\n      let t = typedef(t, \"gr_backendrendertarget_t\");\n    };\n  };\n\n  module TextBlob = {\n    type t;\n    let t: typ(structure(t)) = structure(\"sk_textblob_t\");\n    let t = typedef(t, \"sk_textblob_t\");\n\n    module Builder = {\n      type t;\n      let t: typ(structure(t)) = structure(\"sk_textblob_builder_t\");\n      let t = typedef(t, \"sk_textblob_builder_t\");\n    };\n  };\n\n  type clipOp =\n    | Difference\n    | Intersect;\n  let clipOp =\n    skiaCEnum(\n      \"sk_clipop_t\",\n      [\n        (Difference, \"DIFFERENCE_SK_CLIPOP\"),\n        (Intersect, \"INTERSECT_SK_CLIPOP\"),\n      ],\n    );\n\n  module Canvas = {\n    type t;\n    let t: typ(structure(t)) = structure(\"sk_canvas_t\");\n    let t = typedef(t, \"sk_canvas_t\");\n  };\n\n  module View3d = {\n    type t;\n    let t: typ(structure(t)) = structure(\"sk_3dview_t\");\n    let t = typedef(t, \"sk_3dview_t\");\n  };\n\n  module SurfaceProps = {\n    type t;\n    let t: typ(structure(t)) = structure(\"sk_surfaceprops_t\");\n    let t = typedef(t, \"sk_surfaceprops_t\");\n  };\n\n  module Surface = {\n    type t;\n    let t: typ(structure(t)) = structure(\"sk_surface_t\");\n    let t = typedef(t, \"sk_surface_t\");\n  };\n};\n"
  },
  {
    "path": "packages/reason-skia/src/wrapped/types/dune",
    "content": "(library\n (name SkiaWrappedTypes)\n (public_name reason-skia.wrapped.types)\n (libraries ctypes))\n"
  },
  {
    "path": "packages/reason-skia/test/ColorTest.re",
    "content": "open Skia;\nopen TestFramework;\n\ndescribe(\"Color\", ({describe, test, _}) => {\n  describe(\"float\", ({test, _}) => {\n    test(\"makeArgb\", ({expect, _}) => {\n      let color = Color.Float.makeArgb(0., 0.25099, 0.501961, 1.0);\n\n      let a = Color.getA(color) |> Int32.to_int;\n      let r = Color.getR(color) |> Int32.to_int;\n      let g = Color.getG(color) |> Int32.to_int;\n      let b = Color.getB(color) |> Int32.to_int;\n\n      expect.int(a).toBe(0x00);\n      expect.int(r).toBe(0x40);\n      expect.int(g).toBe(0x80);\n      expect.int(b).toBe(0xFF);\n    });\n\n    test(\"getA/R/G/B\", ({expect, _}) => {\n      let color = Color.Float.makeArgb(1.0, 0.501961, 0.25099, 0.);\n\n      let a = Color.Float.getA(color);\n      let r = Color.Float.getR(color);\n      let g = Color.Float.getG(color);\n      let b = Color.Float.getB(color);\n\n      expect.float(a).toBeCloseTo(1.0);\n      expect.float(r).toBeCloseTo(0.501961);\n      expect.float(g).toBeCloseTo(0.25099);\n      expect.float(b).toBeCloseTo(0.0);\n    });\n  });\n  test(\"makeArgb\", ({expect, _}) => {\n    let color = Color.makeArgb(0x1Al, 0x1Bl, 0x1Cl, 0x1Dl);\n\n    let a = Color.getA(color) |> Int32.to_int;\n    let r = Color.getR(color) |> Int32.to_int;\n    let g = Color.getG(color) |> Int32.to_int;\n    let b = Color.getB(color) |> Int32.to_int;\n\n    expect.int(a).toBe(0x1A);\n    expect.int(r).toBe(0x1B);\n    expect.int(g).toBe(0x1C);\n    expect.int(b).toBe(0x1D);\n  });\n});\n"
  },
  {
    "path": "packages/reason-skia/test/MatrixTest.re",
    "content": "open Skia;\nopen TestFramework;\n\ndescribe(\"Matrix\", ({test, _}) => {\n  test(\"setTranslate\", ({expect, _}) => {\n    let matrix = Matrix.make();\n    let () = Matrix.setTranslate(matrix, 8.0, 16.0);\n\n    let vector = Vector.make(0., 0.);\n    Matrix.mapXy(matrix, 1., 2., vector);\n\n    expect.float(Vector.getX(vector)).toBeCloseTo(9.0);\n    expect.float(Vector.getY(vector)).toBeCloseTo(18.0);\n  });\n  test(\"setScale\", ({expect, _}) => {\n    let matrix = Matrix.make();\n    let () = Matrix.setScale(matrix, 3.0, 4.0, 0., 0.);\n\n    let vector = Vector.make(0., 0.);\n    Matrix.mapXy(matrix, 1., 2., vector);\n\n    expect.float(Vector.getX(vector)).toBeCloseTo(3.0);\n    expect.float(Vector.getY(vector)).toBeCloseTo(8.0);\n  });\n});\n"
  },
  {
    "path": "packages/reason-skia/test/PaintTest.re",
    "content": "open Skia;\nopen TestFramework;\n\ndescribe(\"Paint\", ({describe, _}) => {\n  describe(\"hinting\", ({test, _}) => {\n    test(\"get / set isAutohinted\", ({expect, _}) => {\n      let paint = Paint.make();\n\n      Paint.setAutohinted(paint, true);\n      expect.equal(paint |> Paint.isAutohinted, true);\n\n      Paint.setAutohinted(paint, false);\n      expect.equal(paint |> Paint.isAutohinted, false);\n    });\n    test(\"get / set hinting\", ({expect, _}) => {\n      let paint = Paint.make();\n\n      Paint.setTextEncoding(paint, Utf8);\n\n      Paint.setHinting(paint, FullHinting);\n      expect.equal(paint |> Paint.getHinting, FullHinting);\n\n      Paint.setHinting(paint, NoHinting);\n      expect.equal(paint |> Paint.getHinting, NoHinting);\n    });\n  })\n});\n"
  },
  {
    "path": "packages/reason-skia/test/RectTest.re",
    "content": "open Skia;\nopen TestFramework;\n\ndescribe(\"Rect\", ({test, _}) => {\n  test(\"makeLtrb\", ({expect, _}) => {\n    let rect = Rect.makeLtrb(1., 2., 3., 4.);\n    let left = Rect.getLeft(rect);\n    let right = Rect.getRight(rect);\n    let top = Rect.getTop(rect);\n    let bottom = Rect.getBottom(rect);\n\n    expect.float(left).toBeCloseTo(1.);\n    expect.float(top).toBeCloseTo(2.);\n    expect.float(right).toBeCloseTo(3.);\n    expect.float(bottom).toBeCloseTo(4.);\n  });\n  test(\"setLtrb\", ({expect, _}) => {\n    let rect = Rect.makeLtrb(1., 2., 3., 4.);\n    Rect.Mutable.setLtrb(~out=rect, 3.0, 9.0, 27.0, 81.0);\n    let left = Rect.getLeft(rect);\n    let right = Rect.getRight(rect);\n    let top = Rect.getTop(rect);\n    let bottom = Rect.getBottom(rect);\n\n    expect.float(left).toBeCloseTo(3.);\n    expect.float(top).toBeCloseTo(9.);\n    expect.float(right).toBeCloseTo(27.);\n    expect.float(bottom).toBeCloseTo(81.);\n  });\n});\n"
  },
  {
    "path": "packages/reason-skia/test/TestFramework.re",
    "content": "include Rely.Make({\n  let config =\n    Rely.TestFrameworkConfig.initialize({\n      snapshotDir: \"./__snapshots__\",\n      projectDir: \"\",\n    });\n});\n"
  },
  {
    "path": "packages/reason-skia/test/dune",
    "content": "(library\n (name Skia_Test)\n (flags\n  (:standard\n   (-w -39)))\n (ocamlopt_flags -linkall -g)\n (libraries rely.lib rely.internal reason-skia reason-skia.wrapped.bindings\n   reason-skia.wrapped))\n"
  },
  {
    "path": "packages/revery-text-wrap/examples/text-wrap-cli/ReveryTextWrapCli.re",
    "content": "open Revery_TextWrap;\n\n/* Get the width of a character */\nlet width_of_token = str => {\n  String.length(str) |> float;\n};\n\nTimber.App.enable(Timber.Reporter.console());\nTimber.App.setLevel(Timber.Level.trace);\n\nPrintexc.record_backtrace(true);\n\nlet () = {\n  wrap(~width_of_token, ~max_width=100.0, \"0\") |> List.iter(print_endline);\n  let wrapInput =\n    \"Here's another example of text where wrapping might be more difficult. \"\n    ++ \"This string is very, very long and consists of words of varying lengths. \"\n    ++ \"By utilizing some extremely long words, we can hopefully trigger some of \"\n    ++ \"the more obscure edge-cases that word-wrapping can result in, such as \"\n    ++ \"placing a hyphen in the middle of a word on top of another hyphen.\"\n    ++ \"Let's also throw some UTF8 in here, who doesn't like a good emoji or 10?\"\n    ++ \"😀 🤔 🤭 🤫 🤥 😶 😐 😑 😬\"\n    ++ \"Also let's try some CJK!\"\n    ++ \"日本語の場合はランダムに生成された文章以外に、 著作権が切れた小説などが利用されることもある。\";\n  print_endline(\"==================\");\n  wrap(~width_of_token, ~max_width=5.0, ~hyphenate=true, wrapInput)\n  |> List.iter(print_endline);\n  print_endline(\"==================\");\n  wrap(~width_of_token, ~max_width=10.0, ~hyphenate=true, wrapInput)\n  |> List.iter(print_endline);\n  print_endline(\"==================\");\n  wrap(~width_of_token, ~max_width=15.0, wrapInput)\n  |> List.iter(print_endline);\n  print_endline(\"==================\");\n  wrap(~width_of_token, ~max_width=40.0, wrapInput)\n  |> List.iter(print_endline);\n};\n"
  },
  {
    "path": "packages/revery-text-wrap/examples/text-wrap-cli/dune",
    "content": "(executables\n (names ReveryTextWrapCli)\n (package ReveryExamples)\n (public_names ReveryTextWrapCli)\n (modes native byte)\n (libraries Revery.text-wrap Revery.zed))\n\n(install\n (section bin)\n (package ReveryExamples)\n (files ReveryTextWrapCli.bc))\n"
  },
  {
    "path": "packages/revery-text-wrap/src/Revery_TextWrap.re",
    "content": "open Tokenize;\n\nmodule Log = (val Timber.Log.withNamespace(\"Revery.TextWrap\"));\n\nlet rec list_of_queue = q =>\n  if (Queue.is_empty(q)) {\n    [];\n  } else {\n    /* We need to split into two lines because the RHS of ::\n       gets evaluated first, leading to recursion before the\n       item is removed from the queue. */\n    let first = Queue.take(q);\n    [first, ...list_of_queue(q)];\n  };\n\n[@inline always]\nlet last_uchar_of_string = str =>\n  switch (str) {\n  | \"\" => Uchar.of_int(0)\n  | _ =>\n    let offset = Zed_utf8.prev(str, String.length(str));\n    let offset = offset == String.length(str) ? 0 : offset;\n    Zed_utf8.extract(str, offset);\n  };\n\nlet log10_of_2 = log10(2.);\n\n[@inline always]\nlet uchar_num_bytes = uchar => {\n  let code = Uchar.to_int(uchar);\n  let num_bits = log10(float(code)) /. log10_of_2;\n  num_bits /. 8. |> int_of_float;\n};\n\nlet wrap_queue =\n    (\n      ~max_width,\n      ~width_of_token,\n      ~hyphenate=false,\n      ~ignore_preceding_whitespace=true,\n      text,\n    ) => {\n  /* Create a buffer for the outputted lines */\n  let output_lines = Queue.create();\n  /* Split the input text into lines and for each line: */\n  split_on_newline(text)\n  |> Queue.iter(line => {\n       /* Create a buffer for the wrapped portion of the line */\n       let buffer = Buffer.create(String.length(line));\n       let current_offset = ref(0);\n       let last_added = ref(None);\n\n       let buffer_add_uchar = uchar => {\n         let num_bytes = uchar_num_bytes(uchar);\n         Buffer.add_utf_8_uchar(buffer, uchar);\n         current_offset := current_offset^ + num_bytes;\n         last_added := Some(`Uchar(uchar));\n       };\n\n       let buffer_add_string = str => {\n         let num_bytes = String.length(str);\n         Buffer.add_string(buffer, str);\n         current_offset := current_offset^ + num_bytes;\n         last_added := Some(`String(str));\n       };\n\n       let buffer_reset = () => {\n         Buffer.reset(buffer);\n         current_offset := 0;\n         last_added := None;\n       };\n\n       let buffer_last_uchar = () =>\n         switch (last_added^) {\n         | Some(`Uchar(uchar)) => uchar\n         | Some(`String(str)) => last_uchar_of_string(str)\n         | None => Uchar.of_int(0)\n         };\n\n       /* Store the width of this portion */\n       let width = ref(0.0);\n       /* Tokenize the line by whitespace and for each token: */\n       split_tokens(line)\n       |> Queue.iter(token => {\n            /* Calculate the width of the token */\n            let token_width = width_of_token(token);\n\n            Log.tracef(m =>\n              m(\n                \"Token: %s, Width: %f, Token_width: %f, Max_width: %f\\n\",\n                token,\n                width^,\n                token_width,\n                max_width,\n              )\n            );\n\n            /* If the buffer is already too long, push the buffer and reset it */\n            if (width^ >= max_width) {\n              Log.trace(\"Clear\");\n              Queue.add(Buffer.contents(buffer), output_lines);\n              buffer_reset();\n              width := 0.0;\n            };\n\n            /* Check to see if the line starts w/ whitespace and if we should ignore it */\n            if (ignore_preceding_whitespace\n                && is_whitespace(token)\n                && width^ == 0.0) {\n              Log.trace(\n                \"Decision: ignore\",\n                /* If it's gonna be too long with the whitespace, don't add it & just prepare to wrap,\n                   so that we don't get the whitespace at the start of the next line. */\n              );\n            } else if (ignore_preceding_whitespace\n                       && is_whitespace(token)\n                       && width^\n                       +. token_width > max_width) {\n              Log.trace(\"Decision: append-whitespace\");\n              width := width^ +. token_width;\n              /* If we can add the token without exceeding the limit, add it to the current line */\n            } else if (width^ +. token_width <= max_width) {\n              Log.tracef(m => m(\"Decision: append (%s)\", __LOC__));\n              width := width^ +. token_width;\n              buffer_add_string(token);\n              /* If it would exceed the limit and the user wants hyphenation: */\n            } else if (hyphenate && width^ +. token_width > max_width) {\n              Log.trace(\"Decision: hyphenate\");\n\n              let rec loop = (~str, ~offset, ~iteration as i) =>\n                if (offset != String.length(str)) {\n                  let (uchar, nextOffset) =\n                    Zed_utf8.extract_next(str, offset);\n\n                  let char_width = width_of_token(Zed_utf8.singleton(uchar));\n\n                  /* If it won't overflow this line OR if there's no way to fit it into a line without overflowing */\n                  if (width^\n                      +. char_width <= max_width\n                      || width^ == 0.0\n                      && char_width >= max_width) {\n                    Log.tracef(m => m(\"--Decision: append (%s)\", __LOC__));\n                    /* Append it to the buffer */\n                    buffer_add_uchar(uchar);\n                    width := width^ +. char_width;\n                    /* If it will overflow... */\n                  } else {\n                    Log.trace(\"--Decision: break\");\n                    /* Finalize the current line and reset the buffer (if we need to) */\n                    if (width^ > 0.0) {\n                      /* If we're part-way through the string already */\n                      if (i > 0) {\n                        Log.trace(\"--Clear+hyphenate\");\n                        /* We need to swap the last char of the buffer with a -, because the\n                           hyphen will be the last character that fits into this width. */\n                        let last_uchar = buffer_last_uchar();\n                        /* If we've only put one character of the string before hyphenating, we\n                           should just swap with a space, so that we don't have a lonely hyphen\n                           on the previous line */\n                        let hyphen =\n                          if (i == 1) {\n                            \" \";\n                          } else {\n                            \"-\";\n                          };\n                        /* Flush the buffer with the hyphen and reset the buffer to just the last\n                           character that was in the buffer (where the hyphen now is) */\n                        Queue.add(\n                          Zed_utf8.rchop(Buffer.contents(buffer)) ++ hyphen,\n                          output_lines,\n                        );\n                        buffer_reset();\n                        buffer_add_uchar(last_uchar);\n                        width := width_of_token(Zed_utf8.singleton(uchar));\n                        /* Otherwise, this is the start of the token */\n                      } else {\n                        Log.trace(\"--Clear\");\n                        /* So just flush & reset the buffer */\n                        Queue.add(Buffer.contents(buffer), output_lines);\n                        buffer_reset();\n                        width := 0.0;\n                      };\n                    };\n                    /* Then push the next character from this token onto the buffer */\n                    width := width^ +. char_width;\n                    buffer_add_uchar(uchar);\n                  };\n                  loop(~str, ~offset=nextOffset, ~iteration=i + 1);\n                };\n\n              loop(~str=token, ~offset=0, ~iteration=0);\n              /* If it would exceed the limit and the user doesn't want hyphenation: */\n            } else {\n              Log.trace(\"Decision: wrap\");\n              /* Finalize the current line and reset the buffer (if we need to) */\n              if (width^ > 0.0) {\n                Queue.add(Buffer.contents(buffer), output_lines);\n                buffer_reset();\n              };\n              /* Then push the new token onto the buffer */\n              width := token_width;\n              buffer_add_string(token);\n            };\n          });\n       /* Finalize any remaining text in the buffer */\n       if (width^ > 0.0) {\n         Queue.add(Buffer.contents(buffer), output_lines);\n       };\n     });\n  output_lines;\n};\n\nlet wrap =\n    (\n      ~max_width,\n      ~width_of_token,\n      ~hyphenate=false,\n      ~ignore_preceding_whitespace=true,\n      text,\n    ) =>\n  list_of_queue(\n    wrap_queue(\n      ~max_width,\n      ~width_of_token,\n      ~hyphenate,\n      ~ignore_preceding_whitespace,\n      text,\n    ),\n  );\n"
  },
  {
    "path": "packages/revery-text-wrap/src/Revery_TextWrap.rei",
    "content": "let wrap:\n  (\n    ~max_width: float,\n    ~width_of_token: string => float,\n    ~hyphenate: bool=?,\n    ~ignore_preceding_whitespace: bool=?,\n    string\n  ) =>\n  list(string);\n"
  },
  {
    "path": "packages/revery-text-wrap/src/Tokenize.re",
    "content": "let slice = (~first, ~last, string) => {\n  let length = last - first;\n  String.sub(string, first, length);\n};\n\n/* Split a string into an AppendList of strings, chopping at every newline and\n   removing the newline characters. */\nlet split_on_newline = string => {\n  let lines = Queue.create();\n  let first = ref(0);\n  for (i in 0 to String.length(string) - 1) {\n    if (string.[i] == '\\n') {\n      Queue.add(slice(~first=first^, ~last=i, string), lines);\n      first := i + 1;\n    };\n  };\n  /* Append the remaining characters after the last split */\n  if (first^ <= String.length(string) - 1) {\n    Queue.add(\n      slice(~first=first^, ~last=String.length(string), string),\n      lines,\n    );\n  };\n  lines;\n};\n\n/* Tokenize a string into an AppendList of strings, chopping on every instance of whitespace\n   and every hyphen. Isolate whitespace into its own token and preserve it in the final list. */\nlet split_tokens = string => {\n  let tokens = Queue.create();\n  let first = ref(0);\n  for (i in 0 to String.length(string) - 1) {\n    switch (string.[i]) {\n    | '\\n'\n    | ' '\n    | '\\t' =>\n      /* Push everything before this character (if there is anything) */\n      if (first^ != i) {\n        Queue.add(slice(~first=first^, ~last=i, string), tokens);\n      };\n      /* Then push this character */\n      Queue.add(slice(~first=i, ~last=i + 1, string), tokens);\n      /* And skip it */\n      first := i + 1;\n    | '-' =>\n      /* When a word is already hyphenated, split it into tokens so we don't needlessly\n         rehyphenate it and end up with a double hyphen */\n      Queue.add(slice(~first=first^, ~last=i + 1, string), tokens);\n      first := i + 1;\n    | _ => ()\n    };\n  };\n  /* Append the remaining characters after the last split */\n  if (first^ <= String.length(string) - 1) {\n    Queue.add(\n      slice(~first=first^, ~last=String.length(string), string),\n      tokens,\n    );\n  };\n  tokens;\n};\n\n/* Check if a token is blank/whitespace */\nlet is_whitespace = str =>\n  /* We only have to check the start b/c the tokenizer won't return anything after the whitespace */\n  String.length(str) == 0 || str.[0] === ' ' || str.[0] === '\\t';\n"
  },
  {
    "path": "packages/revery-text-wrap/src/dune",
    "content": "(library\n (name Revery_TextWrap)\n (public_name Revery.text-wrap)\n (libraries Revery.zed timber))\n"
  },
  {
    "path": "packages/zed/src/dune",
    "content": "(library\n (name Revery_Zed)\n (public_name Revery.zed)\n (wrapped false)\n (flags\n  (:standard -safe-string))\n (libraries bytes uucp uutf result charInfo_width))\n"
  },
  {
    "path": "packages/zed/src/zed_utf8.ml",
    "content": "(*\n * zed_utf8.ml\n * -----------\n * Copyright : (c) 2011, Jeremie Dimino <jeremie@dimino.org>\n * Licence   : BSD3\n *\n * This file is a part of Zed, an editor engine.\n *)\n\ntype t = string\nexception Invalid of string * string\nexception Out_of_bounds\n\nlet fail str pos msg = raise (Invalid(Printf.sprintf \"at position %d: %s\" pos msg, str))\n\nlet byte str i = Char.code (String.unsafe_get str i)\nlet set_byte str i n = Bytes.unsafe_set str i (Char.unsafe_chr n)\n\n(* +-----------------------------------------------------------------+\n   | Validation                                                      |\n   +-----------------------------------------------------------------+ *)\n\ntype check_result =\n  | Correct of int\n  | Message of string\n\nlet next_error s i =\n  let len = String.length s in\n  let rec main i ulen =\n    if i = len then\n      (i, ulen, \"\")\n    else\n      let ch = String.unsafe_get s i in\n      match ch with\n        | '\\x00' .. '\\x7f' ->\n            main (i + 1) (ulen + 1)\n        | '\\xc0' .. '\\xdf' ->\n            if i + 1 >= len then\n              (i, ulen, \"premature end of UTF8 sequence\")\n            else begin\n              let byte1 = Char.code (String.unsafe_get s (i + 1)) in\n              if byte1 land 0xc0 != 0x80 then\n                (i, ulen, \"malformed UTF8 sequence\")\n              else if ((Char.code ch land 0x1f) lsl 6) lor (byte1 land 0x3f) < 0x80 then\n                (i, ulen, \"overlong UTF8 sequence\")\n              else\n                main (i + 2) (ulen + 1)\n            end\n        | '\\xe0' .. '\\xef' ->\n            if i + 2 >= len then\n              (i, ulen, \"premature end of UTF8 sequence\")\n            else begin\n              let byte1 = Char.code (String.unsafe_get s (i + 1))\n              and byte2 = Char.code (String.unsafe_get s (i + 2)) in\n              if byte1 land 0xc0 != 0x80 then\n                (i, ulen, \"malformed UTF8 sequence\")\n              else if byte2 land 0xc0 != 0x80 then\n                (i, ulen, \"malformed UTF8 sequence\")\n              else if ((Char.code ch land 0x0f) lsl 12) lor ((byte1 land 0x3f) lsl 6) lor (byte2 land 0x3f) < 0x800 then\n                (i, ulen, \"overlong UTF8 sequence\")\n              else\n                main (i + 3) (ulen + 1)\n            end\n        | '\\xf0' .. '\\xf7' ->\n            if i + 3 >= len then\n              (i, ulen, \"premature end of UTF8 sequence\")\n            else begin\n              let byte1 = Char.code (String.unsafe_get s (i + 1))\n              and byte2 = Char.code (String.unsafe_get s (i + 2))\n              and byte3 = Char.code (String.unsafe_get s (i + 3)) in\n              if byte1 land 0xc0 != 0x80 then\n                (i, ulen, \"malformed UTF8 sequence\")\n              else if byte2 land 0xc0 != 0x80 then\n                (i, ulen, \"malformed UTF8 sequence\")\n              else if byte3 land 0xc0 != 0x80 then\n                (i, ulen, \"malformed UTF8 sequence\")\n              else if ((Char.code ch land 0x07) lsl 18) lor ((byte1 land 0x3f) lsl 12) lor ((byte2 land 0x3f) lsl 6) lor (byte3 land 0x3f) < 0x10000 then\n                (i, ulen, \"overlong UTF8 sequence\")\n              else\n                main (i + 4) (ulen + 1)\n            end\n        | _ ->\n            (i, ulen, \"invalid start of UTF8 sequence\")\n  in\n  main i 0\n\nlet check str =\n  let ofs, len, msg = next_error str 0 in\n  if ofs = String.length str then\n    Correct len\n  else\n    Message (Printf.sprintf \"at position %d: %s\" ofs msg)\n\nlet validate str =\n  let ofs, len, msg = next_error str 0 in\n  if ofs = String.length str then\n    len\n  else\n    fail str ofs msg\n\n(* +-----------------------------------------------------------------+\n   | Unsafe UTF-8 manipulation                                       |\n   +-----------------------------------------------------------------+ *)\n\nlet unsafe_next str ofs =\n  match String.unsafe_get str ofs with\n    | '\\x00' .. '\\x7f' ->\n        ofs + 1\n    | '\\xc0' .. '\\xdf' ->\n        if ofs + 2 > String.length str then\n          fail str ofs \"unterminated UTF-8 sequence\"\n        else\n          ofs + 2\n    | '\\xe0' .. '\\xef' ->\n        if ofs + 3 > String.length str then\n          fail str ofs \"unterminated UTF-8 sequence\"\n        else\n          ofs + 3\n    | '\\xf0' .. '\\xf7' ->\n        if ofs + 4 > String.length str then\n          fail str ofs \"unterminated UTF-8 sequence\"\n        else\n          ofs + 4\n    | _ ->\n        fail str ofs \"invalid start of UTF-8 sequence\"\n\nlet unsafe_prev str ofs =\n  match String.unsafe_get str (ofs - 1) with\n    | '\\x00' .. '\\x7f' ->\n        ofs - 1\n    | '\\x80' .. '\\xbf' ->\n        if ofs >= 2 then\n          match String.unsafe_get str (ofs - 2) with\n            | '\\xc0' .. '\\xdf' ->\n                ofs - 2\n            | '\\x80' .. '\\xbf' ->\n                if ofs >= 3 then\n                  match String.unsafe_get str (ofs - 3) with\n                    | '\\xe0' .. '\\xef' ->\n                        ofs - 3\n                    | '\\x80' .. '\\xbf' ->\n                        if ofs >= 4 then\n                          match String.unsafe_get str (ofs - 4) with\n                            | '\\xf0' .. '\\xf7' ->\n                                ofs - 4\n                            | _ ->\n                                fail str (ofs - 4) \"invalid start of UTF-8 sequence\"\n                        else\n                          fail str (ofs - 3) \"invalid start of UTF-8 string\"\n                    | _ ->\n                        fail str (ofs - 3) \"invalid middle of UTF-8 sequence\"\n                else\n                  fail str (ofs - 2) \"invaild start of UTF-8 string\"\n            | _ ->\n                fail str (ofs - 2) \"invalid middle of UTF-8 sequence\"\n        else\n          fail str (ofs - 1) \"invalid start of UTF-8 string\"\n    | _ ->\n        fail str (ofs - 1) \"invalid end of UTF-8 sequence\"\n\nlet unsafe_extract str ofs =\n  let ch = String.unsafe_get str ofs in\n  match ch with\n    | '\\x00' .. '\\x7f' ->\n        Uchar.of_char ch\n    | '\\xc0' .. '\\xdf' ->\n        if ofs + 2 > String.length str then\n          fail str ofs \"unterminated UTF-8 sequence\"\n        else\n          Uchar.of_int (((Char.code ch land 0x1f) lsl 6) lor (byte str (ofs + 1) land 0x3f))\n    | '\\xe0' .. '\\xef' ->\n        if ofs + 3 > String.length str then\n          fail str ofs \"unterminated UTF-8 sequence\"\n        else\n          Uchar.of_int (((Char.code ch land 0x0f) lsl 12) lor ((byte str (ofs + 1) land 0x3f) lsl 6) lor (byte str (ofs + 2) land 0x3f))\n    | '\\xf0' .. '\\xf7' ->\n        if ofs + 4 > String.length str then\n          fail str ofs \"unterminated UTF-8 sequence\"\n        else\n          Uchar.of_int (((Char.code ch land 0x07) lsl 18) lor ((byte str (ofs + 1) land 0x3f) lsl 12) lor ((byte str (ofs + 2) land 0x3f) lsl 6) lor (byte str (ofs + 3) land 0x3f))\n    | _ ->\n        fail str ofs \"invalid start of UTF-8 sequence\"\n\nlet unsafe_extract_next str ofs =\n  let ch = String.unsafe_get str ofs in\n  match ch with\n    | '\\x00' .. '\\x7f' ->\n        (Uchar.of_char ch, ofs + 1)\n    | '\\xc0' .. '\\xdf' ->\n        if ofs + 2 > String.length str then\n          fail str ofs \"unterminated UTF-8 sequence\"\n        else\n          (Uchar.of_int (((Char.code ch land 0x1f) lsl 6) lor (byte str (ofs + 1) land 0x3f)), ofs + 2)\n    | '\\xe0' .. '\\xef' ->\n        if ofs + 3 > String.length str then\n          fail str ofs \"unterminated UTF-8 sequence\"\n        else\n          (Uchar.of_int (((Char.code ch land 0x0f) lsl 12) lor ((byte str (ofs + 1) land 0x3f) lsl 6) lor (byte str (ofs + 2) land 0x3f)), ofs + 3)\n    | '\\xf0' .. '\\xf7' ->\n        if ofs + 4 > String.length str then\n          fail str ofs \"unterminated UTF-8 sequence\"\n        else\n          (Uchar.of_int (((Char.code ch land 0x07) lsl 18) lor ((byte str (ofs + 1) land 0x3f) lsl 12) lor ((byte str (ofs + 2) land 0x3f) lsl 6) lor (byte str (ofs + 3) land 0x3f)), ofs + 4)\n    | _ ->\n        fail str ofs \"invalid start of UTF-8 sequence\"\n\nlet unsafe_extract_prev str ofs =\n  let ch1 = String.unsafe_get str (ofs - 1) in\n  match ch1 with\n    | '\\x00' .. '\\x7f' ->\n        (Uchar.of_char ch1, ofs - 1)\n    | '\\x80' .. '\\xbf' ->\n        if ofs >= 2 then\n          let ch2 = String.unsafe_get str (ofs - 2) in\n          match ch2 with\n            | '\\xc0' .. '\\xdf' ->\n                (Uchar.of_int (((Char.code ch2 land 0x1f) lsl 6) lor (Char.code ch1 land 0x3f)), ofs - 2)\n            | '\\x80' .. '\\xbf' ->\n                if ofs >= 3 then\n                  let ch3 = String.unsafe_get str (ofs - 3) in\n                  match ch3 with\n                    | '\\xe0' .. '\\xef' ->\n                        (Uchar.of_int (((Char.code ch3 land 0x0f) lsl 12) lor ((Char.code ch2 land 0x3f) lsl 6) lor (Char.code ch1 land 0x3f)), ofs - 3)\n                    | '\\x80' .. '\\xbf' ->\n                        if ofs >= 4 then\n                          let ch4 = String.unsafe_get str (ofs - 4) in\n                          match ch4 with\n                            | '\\xf0' .. '\\xf7' ->\n                                (Uchar.of_int (((Char.code ch4 land 0x07) lsl 18) lor ((Char.code ch3 land 0x3f) lsl 12) lor ((Char.code ch2 land 0x3f) lsl 6) lor (Char.code ch1 land 0x3f)), ofs - 4)\n                            | _ ->\n                                fail str (ofs - 4) \"invalid start of UTF-8 sequence\"\n                        else\n                          fail str (ofs - 3) \"invalid start of UTF-8 string\"\n                    | _ ->\n                        fail str (ofs - 3) \"invalid middle of UTF-8 sequence\"\n                else\n                  fail str (ofs - 2) \"invaild start of UTF-8 string\"\n            | _ ->\n                fail str (ofs - 2) \"invalid middle of UTF-8 sequence\"\n        else\n          fail str (ofs - 1) \"invalid start of UTF-8 string\"\n    | _ ->\n        fail str (ofs - 1) \"invalid end of UTF-8 sequence\"\n\nlet rec move_l str ofs len =\n  if len = 0 then\n    ofs\n  else if ofs = String.length str then\n    raise Out_of_bounds\n  else\n    move_l str (unsafe_next str ofs) (len - 1)\n\nlet unsafe_sub str ofs len =\n  let res = Bytes.create len in\n  String.unsafe_blit str ofs res 0 len;\n  Bytes.unsafe_to_string res\n\n(* +-----------------------------------------------------------------+\n   | Construction                                                    |\n   +-----------------------------------------------------------------+ *)\n\nlet singleton char =\n  let code = Uchar.to_int char in\n  Bytes.unsafe_to_string @@\n  if code < 0x80 then begin\n    let s = Bytes.create 1 in\n    set_byte s 0 code;\n    s\n  end else if code <= 0x800 then begin\n    let s = Bytes.create 2 in\n    set_byte s 0 ((code lsr 6) lor 0xc0);\n    set_byte s 1 ((code land 0x3f) lor 0x80);\n    s\n  end else if code <= 0x10000 then begin\n    let s = Bytes.create 3 in\n    set_byte s 0 ((code lsr 12) lor 0xe0);\n    set_byte s 1 (((code lsr 6) land 0x3f) lor 0x80);\n    set_byte s 2 ((code land 0x3f) lor 0x80);\n    s\n  end else if code <= 0x10ffff then begin\n    let s = Bytes.create 4 in\n    set_byte s 0 ((code lsr 18) lor 0xf0);\n    set_byte s 1 (((code lsr 12) land 0x3f) lor 0x80);\n    set_byte s 2 (((code lsr 6) land 0x3f) lor 0x80);\n    set_byte s 3 ((code land 0x3f) lor 0x80);\n    s\n  end else\n    (* Camomile allow characters with code-point greater than\n       0x10ffff *)\n    invalid_arg \"Zed_utf8.singleton\"\n\nlet make n code =\n  let str = singleton code in\n  let len = String.length str in\n  let res = Bytes.create (n * len) in\n  let ofs = ref 0 in\n  for _ = 1 to n do\n    String.unsafe_blit str 0 res !ofs len;\n    ofs := !ofs + len\n  done;\n  Bytes.unsafe_to_string res\n\nlet init n f =\n  let buf = Buffer.create n in\n  for i = 0 to n - 1 do\n    Buffer.add_string buf (singleton (f i))\n  done;\n  Buffer.contents buf\n\nlet rev_init n f =\n  let buf = Buffer.create n in\n  for i = n - 1 downto 0 do\n    Buffer.add_string buf (singleton (f i))\n  done;\n  Buffer.contents buf\n\n(* +-----------------------------------------------------------------+\n   | Informations                                                    |\n   +-----------------------------------------------------------------+ *)\n\nlet rec length_rec str ofs len =\n  if ofs = String.length str then\n    len\n  else\n    length_rec str (unsafe_next str ofs) (len + 1)\n\nlet length str =\n  length_rec str 0 0\n\n(* +-----------------------------------------------------------------+\n   | Comparison                                                      |\n   +-----------------------------------------------------------------+ *)\n\nlet rec compare_rec str1 ofs1 str2 ofs2 =\n  if ofs1 = String.length str1 then\n    if ofs2 = String.length str2 then\n      0\n    else\n      -1\n  else if ofs2 = String.length str2 then\n    1\n  else\n    let code1, ofs1 = unsafe_extract_next str1 ofs1\n    and code2, ofs2 = unsafe_extract_next str2 ofs2 in\n    let d = Uchar.to_int code1 - Uchar.to_int code2 in\n    if d <> 0 then\n      d\n    else\n      compare_rec str1 ofs1 str2 ofs2\n\nlet compare str1 str2 =\n  compare_rec str1 0 str2 0\n\n(* +-----------------------------------------------------------------+\n   | Random access                                                   |\n   +-----------------------------------------------------------------+ *)\n\nlet get str idx =\n  if idx < 0 then\n    raise Out_of_bounds\n  else\n    unsafe_extract str (move_l str 0 idx)\n\n(* +-----------------------------------------------------------------+\n   | Manipulation                                                    |\n   +-----------------------------------------------------------------+ *)\n\nlet sub str idx len =\n  if idx < 0 || len < 0 then\n    raise Out_of_bounds\n  else\n    let ofs1 = move_l str 0 idx in\n    let ofs2 = move_l str ofs1 len in\n    unsafe_sub str ofs1 (ofs2 - ofs1)\n\nlet break str idx =\n  if idx < 0 then\n    raise Out_of_bounds\n  else\n    let ofs = move_l str 0 idx in\n    (unsafe_sub str 0 ofs, unsafe_sub str ofs (String.length str - ofs))\n\nlet before str idx =\n  if idx < 0 then\n    raise Out_of_bounds\n  else\n    let ofs = move_l str 0 idx in\n    unsafe_sub str 0 ofs\n\nlet after str idx =\n  if idx < 0 then\n    raise Out_of_bounds\n  else\n    let ofs = move_l str 0 idx in\n    unsafe_sub str ofs (String.length str - ofs)\n\nlet concat3 a b c =\n  let lena = String.length a\n  and lenb = String.length b\n  and lenc = String.length c in\n  let res = Bytes.create (lena + lenb + lenc) in\n  String.unsafe_blit a 0 res 0 lena;\n  String.unsafe_blit b 0 res lena lenb;\n  String.unsafe_blit c 0 res (lena + lenb) lenc;\n  Bytes.unsafe_to_string res\n\nlet insert str idx sub =\n  let a, b = break str idx in\n  concat3 a sub b\n\nlet remove str idx len =\n  if idx < 0 || len < 0 then\n    raise Out_of_bounds\n  else\n    let ofs1 = move_l str 0 idx in\n    let ofs2 = move_l str ofs1 len in\n    unsafe_sub str 0 ofs1 ^ unsafe_sub str ofs2 (String.length str - ofs2)\n\nlet replace str idx len repl =\n  if idx < 0 || len < 0 then\n    raise Out_of_bounds\n  else\n    let ofs1 = move_l str 0 idx in\n    let ofs2 = move_l str ofs1 len in\n    concat3 (unsafe_sub str 0 ofs1) repl (unsafe_sub str ofs2 (String.length str - ofs2))\n\n(* +-----------------------------------------------------------------+\n   | Exploding and imploding                                         |\n   +-----------------------------------------------------------------+ *)\n\nlet rec rev_rec (res : Bytes.t) str ofs_src ofs_dst =\n  if ofs_src = String.length str then\n    Bytes.unsafe_to_string res\n  else begin\n    let ofs_src' = unsafe_next str ofs_src in\n    let len = ofs_src' - ofs_src in\n    let ofs_dst = ofs_dst - len in\n    String.unsafe_blit str ofs_src res ofs_dst len;\n    rev_rec res str ofs_src' ofs_dst\n  end\n\nlet rev str =\n  let len = String.length str in\n  rev_rec (Bytes.create len) str 0 len\n\nlet concat sep l =\n  match l with\n    | [] ->\n        \"\"\n    | x :: l ->\n        let sep_len = String.length sep in\n        let len = List.fold_left (fun len str -> len + sep_len + String.length str) (String.length x) l in\n        let res = Bytes.create len in\n        String.unsafe_blit x 0 res 0 (String.length x);\n        ignore\n          (List.fold_left\n             (fun ofs str ->\n                String.unsafe_blit sep 0 res ofs sep_len;\n                let ofs = ofs + sep_len in\n                let len = String.length str in\n                String.unsafe_blit str 0 res ofs len;\n                ofs + len)\n             (String.length x) l);\n        Bytes.unsafe_to_string res\n\nlet rev_concat sep l =\n  match l with\n    | [] ->\n        \"\"\n    | x :: l ->\n        let sep_len = String.length sep in\n        let len = List.fold_left (fun len str -> len + sep_len + String.length str) (String.length x) l in\n        let res = Bytes.create len in\n        let ofs = len - String.length x in\n        String.unsafe_blit x 0 res ofs (String.length x);\n        ignore\n          (List.fold_left\n             (fun ofs str ->\n                let ofs = ofs - sep_len in\n                String.unsafe_blit sep 0 res ofs sep_len;\n                let len = String.length str in\n                let ofs = ofs - len in\n                String.unsafe_blit str 0 res ofs len;\n                ofs)\n             ofs l);\n        Bytes.unsafe_to_string res\n\nlet rec explode_rec str ofs acc =\n  if ofs = 0 then\n    acc\n  else\n    let x, ofs = unsafe_extract_prev str ofs in\n    explode_rec str ofs (x :: acc)\n\nlet explode str =\n  explode_rec str (String.length str) []\n\nlet rec rev_explode_rec str ofs acc =\n  if ofs = String.length str then\n    acc\n  else\n    let x, ofs = unsafe_extract_next str ofs in\n    rev_explode_rec str ofs (x :: acc)\n\nlet rev_explode str =\n  rev_explode_rec str 0 []\n\nlet implode l =\n  let l = List.map singleton l in\n  let len = List.fold_left (fun len str -> len + String.length str) 0 l in\n  let res = Bytes.create len in\n  ignore\n    (List.fold_left\n       (fun ofs str ->\n          let len = String.length str in\n          String.unsafe_blit str 0 res ofs len;\n          ofs + len)\n       0 l);\n  Bytes.unsafe_to_string res\n\nlet rev_implode l =\n  let l = List.map singleton l in\n  let len = List.fold_left (fun len str -> len + String.length str) 0 l in\n  let res = Bytes.create len in\n  ignore\n    (List.fold_left\n       (fun ofs str ->\n          let len = String.length str in\n          let ofs = ofs - len in\n          String.unsafe_blit str 0 res ofs len;\n          ofs)\n       len l);\n  Bytes.unsafe_to_string res\n\n(* +-----------------------------------------------------------------+\n   | Text transversal                                                |\n   +-----------------------------------------------------------------+ *)\n\nlet rec iter_rec f str ofs =\n  if ofs = String.length str then\n    ()\n  else begin\n    let chr, ofs = unsafe_extract_next str ofs in\n    f chr;\n    iter_rec f str ofs\n  end\n\nlet iter f str =\n  iter_rec f str 0\n\nlet rec rev_iter_rec f str ofs =\n  if ofs = 0 then\n    ()\n  else begin\n    let chr, ofs = unsafe_extract_prev str ofs in\n    f chr;\n    rev_iter_rec f str ofs\n  end\n\nlet rev_iter f str =\n  rev_iter_rec f str (String.length str)\n\nlet rec fold_rec f str ofs acc =\n  if ofs = String.length str then\n    acc\n  else begin\n    let chr, ofs = unsafe_extract_next str ofs in\n    fold_rec f str ofs (f chr acc)\n  end\n\nlet fold f str acc =\n  fold_rec f str 0 acc\n\nlet rec rev_fold_rec f str ofs acc =\n  if ofs = 0 then\n    acc\n  else begin\n    let chr, ofs = unsafe_extract_prev str ofs in\n    rev_fold_rec f str ofs (f chr acc)\n  end\n\nlet rev_fold f str acc =\n  rev_fold_rec f str (String.length str) acc\n\nlet rec map_rec buf f str ofs =\n  if ofs = String.length str then\n    Buffer.contents buf\n  else begin\n    let chr, ofs = unsafe_extract_next str ofs in\n    Buffer.add_string buf (singleton (f chr));\n    map_rec buf f str ofs\n  end\n\nlet map f str =\n  map_rec (Buffer.create (String.length str)) f str 0\n\nlet rec map_concat_rec buf f str ofs =\n  if ofs = String.length str then\n    Buffer.contents buf\n  else begin\n    let chr, ofs = unsafe_extract_next str ofs in\n    Buffer.add_string buf (f chr);\n    map_concat_rec buf f str ofs\n  end\n\nlet map_concat f str =\n  map_concat_rec (Buffer.create (String.length str)) f str 0\n\nlet rec rev_map_rec buf f str ofs =\n  if ofs = 0 then\n    Buffer.contents buf\n  else begin\n    let chr, ofs = unsafe_extract_prev str ofs in\n    Buffer.add_string buf (singleton (f chr));\n    rev_map_rec buf f str ofs\n  end\n\nlet rev_map f str =\n  rev_map_rec (Buffer.create (String.length str)) f str (String.length str)\n\nlet rec rev_map_concat_rec buf f str ofs =\n  if ofs = 0 then\n    Buffer.contents buf\n  else begin\n    let chr, ofs = unsafe_extract_prev str ofs in\n    Buffer.add_string buf (f chr);\n    rev_map_concat_rec buf f str ofs\n  end\n\nlet rev_map_concat f str =\n  rev_map_concat_rec (Buffer.create (String.length str)) f str (String.length str)\n\nlet rec filter_rec buf f str ofs =\n  if ofs = String.length str then\n    Buffer.contents buf\n  else begin\n    let chr, ofs = unsafe_extract_next str ofs in\n    if f chr then\n      Buffer.add_string buf (singleton chr);\n    filter_rec buf f str ofs\n  end\n\nlet filter f str =\n  filter_rec (Buffer.create (String.length str)) f str 0\n\nlet rec rev_filter_rec buf f str ofs =\n  if ofs = 0 then\n    Buffer.contents buf\n  else begin\n    let chr, ofs = unsafe_extract_prev str ofs in\n    if f chr then\n      Buffer.add_string buf (singleton chr);\n    rev_filter_rec buf f str ofs\n  end\n\nlet rev_filter f str =\n  rev_filter_rec (Buffer.create (String.length str)) f str (String.length str)\n\nlet rec filter_map_rec buf f str ofs =\n  if ofs = String.length str then\n    Buffer.contents buf\n  else begin\n    let chr, ofs = unsafe_extract_next str ofs in\n    (match f chr with\n       | Some chr ->\n           Buffer.add_string buf (singleton chr)\n       | None ->\n           ());\n    filter_map_rec buf f str ofs\n  end\n\nlet filter_map f str =\n  filter_map_rec (Buffer.create (String.length str)) f str 0\n\nlet rec filter_map_concat_rec buf f str ofs =\n  if ofs = String.length str then\n    Buffer.contents buf\n  else begin\n    let chr, ofs = unsafe_extract_next str ofs in\n    (match f chr with\n       | Some txt ->\n           Buffer.add_string buf txt\n       | None ->\n           ());\n    filter_map_concat_rec buf f str ofs\n  end\n\nlet filter_map_concat f str =\n  filter_map_concat_rec (Buffer.create (String.length str)) f str 0\n\nlet rec rev_filter_map_rec buf f str ofs =\n  if ofs = 0 then\n    Buffer.contents buf\n  else begin\n    let chr, ofs = unsafe_extract_prev str ofs in\n    (match f chr with\n       | Some chr ->\n           Buffer.add_string buf (singleton chr)\n       | None ->\n           ());\n    rev_filter_map_rec buf f str ofs\n  end\n\nlet rev_filter_map f str =\n  rev_filter_map_rec (Buffer.create (String.length str)) f str (String.length str)\n\nlet rec rev_filter_map_concat_rec buf f str ofs =\n  if ofs = 0 then\n    Buffer.contents buf\n  else begin\n    let chr, ofs = unsafe_extract_prev str ofs in\n    (match f chr with\n       | Some txt ->\n           Buffer.add_string buf txt\n       | None ->\n           ());\n    rev_filter_map_concat_rec buf f str ofs\n  end\n\nlet rev_filter_map_concat f str =\n  rev_filter_map_concat_rec (Buffer.create (String.length str)) f str (String.length str)\n\n(* +-----------------------------------------------------------------+\n   | Scanning                                                        |\n   +-----------------------------------------------------------------+ *)\n\nlet rec for_all_rec f str ofs =\n  if ofs = String.length str then\n    true\n  else\n    let chr, ofs = unsafe_extract_next str ofs in\n    f chr && for_all_rec f str ofs\n\nlet for_all f str =\n  for_all_rec f str 0\n\nlet rec exists_rec f str ofs =\n  if ofs = String.length str then\n    false\n  else\n    let chr, ofs = unsafe_extract_next str ofs in\n    f chr || exists_rec f str ofs\n\nlet exists f str =\n  exists_rec f str 0\n\nlet rec count_rec f str ofs n =\n  if ofs = String.length str then\n    n\n  else\n    let chr, ofs = unsafe_extract_next str ofs in\n    count_rec f str ofs (if f chr then n + 1 else n)\n\nlet count f str =\n  count_rec f str 0 0\n\n(* +-----------------------------------------------------------------+\n   | Tests                                                           |\n   +-----------------------------------------------------------------+ *)\n\nlet rec unsafe_sub_equal str ofs sub ofs_sub =\n  if ofs_sub = String.length sub then\n    true\n  else\n    (String.unsafe_get str ofs = String.unsafe_get sub ofs_sub)\n    && unsafe_sub_equal str (ofs + 1) sub (ofs_sub + 1)\n\nlet rec contains_rec str sub ofs =\n  if ofs + String.length sub > String.length str then\n    false\n  else\n    unsafe_sub_equal str ofs sub 0 || contains_rec str sub (unsafe_next str ofs)\n\nlet contains str sub =\n  contains_rec str sub 0\n\nlet starts_with str prefix =\n  if String.length prefix > String.length str then\n    false\n  else\n    unsafe_sub_equal str 0 prefix 0\n\nlet ends_with str suffix =\n  let ofs = String.length str - String.length suffix in\n  if ofs < 0 then\n    false\n  else\n    unsafe_sub_equal str ofs suffix 0\n\n(* +-----------------------------------------------------------------+\n   | Stripping                                                       |\n   +-----------------------------------------------------------------+ *)\n\nlet rec lfind predicate str ofs =\n  if ofs = String.length str then\n    ofs\n  else\n    let chr, ofs' = unsafe_extract_next str ofs in\n    if predicate chr then\n      lfind predicate str ofs'\n    else\n      ofs\n\nlet rec rfind predicate str ofs =\n  if ofs = 0 then\n    0\n  else\n    let chr, ofs' = unsafe_extract_prev str ofs in\n    if predicate chr then\n      rfind predicate str ofs'\n    else\n      ofs\n\nlet strip ?(predicate=Uucp.White.is_white_space) str =\n  let lofs = lfind predicate str 0 and rofs = rfind predicate str (String.length str) in\n  if lofs < rofs then\n    unsafe_sub str lofs (rofs - lofs)\n  else\n    \"\"\n\nlet lstrip ?(predicate=Uucp.White.is_white_space) str =\n  let lofs = lfind predicate str 0 in\n  unsafe_sub str lofs (String.length str - lofs)\n\nlet rstrip ?(predicate=Uucp.White.is_white_space) str =\n  let rofs = rfind predicate str (String.length str) in\n  unsafe_sub str 0 rofs\n\nlet lchop = function\n  | \"\" ->\n      \"\"\n  | str ->\n      let ofs = unsafe_next str 0 in\n      unsafe_sub str ofs (String.length str - ofs)\n\nlet rchop = function\n  | \"\" ->\n      \"\"\n  | str ->\n      let ofs = unsafe_prev str (String.length str) in\n      unsafe_sub str 0 ofs\n\n(* +-----------------------------------------------------------------+\n   | Buffers                                                         |\n   +-----------------------------------------------------------------+ *)\n\nlet add buf char =\n  let code = Uchar.to_int char in\n  if code < 0x80 then\n    Buffer.add_char buf (Char.unsafe_chr code)\n  else if code <= 0x800 then begin\n    Buffer.add_char buf (Char.unsafe_chr ((code lsr 6) lor 0xc0));\n    Buffer.add_char buf (Char.unsafe_chr ((code land 0x3f) lor 0x80))\n  end else if code <= 0x10000 then begin\n    Buffer.add_char buf (Char.unsafe_chr ((code lsr 12) lor 0xe0));\n    Buffer.add_char buf (Char.unsafe_chr (((code lsr 6) land 0x3f) lor 0x80));\n    Buffer.add_char buf (Char.unsafe_chr ((code land 0x3f) lor 0x80))\n  end else if code <= 0x10ffff then begin\n    Buffer.add_char buf (Char.unsafe_chr ((code lsr 18) lor 0xf0));\n    Buffer.add_char buf (Char.unsafe_chr (((code lsr 12) land 0x3f) lor 0x80));\n    Buffer.add_char buf (Char.unsafe_chr (((code lsr 6) land 0x3f) lor 0x80));\n    Buffer.add_char buf (Char.unsafe_chr ((code land 0x3f) lor 0x80))\n  end else\n    invalid_arg \"Zed_utf8.add\"\n\n(* +-----------------------------------------------------------------+\n   | Offset API                                                      |\n   +-----------------------------------------------------------------+ *)\n\nlet extract str ofs =\n  if ofs < 0 || ofs >= String.length str then\n    raise Out_of_bounds\n  else\n    unsafe_extract str ofs\n\nlet next str ofs =\n  if ofs < 0 || ofs >= String.length str then\n    raise Out_of_bounds\n  else\n    unsafe_next str ofs\n\nlet extract_next str ofs =\n  if ofs < 0 || ofs >= String.length str then\n    raise Out_of_bounds\n  else\n    unsafe_extract_next str ofs\n\nlet prev str ofs =\n  if ofs <= 0 || ofs > String.length str then\n    raise Out_of_bounds\n  else\n    unsafe_prev str ofs\n\nlet extract_prev str ofs =\n  if ofs <= 0 || ofs > String.length str then\n    raise Out_of_bounds\n  else\n    unsafe_extract_prev str ofs\n\n(* +-----------------------------------------------------------------+\n   | Escaping                                                        |\n   +-----------------------------------------------------------------+ *)\n\nlet escaped_char ch =\n  match Uchar.to_int ch with\n    | 7 ->\n        \"\\\\a\"\n    | 8 ->\n        \"\\\\b\"\n    | 9 ->\n        \"\\\\t\"\n    | 10 ->\n        \"\\\\n\"\n    | 11 ->\n        \"\\\\v\"\n    | 12 ->\n        \"\\\\f\"\n    | 13 ->\n        \"\\\\r\"\n    | 27 ->\n        \"\\\\e\"\n    | 92 ->\n        \"\\\\\\\\\"\n    | code when code >= 32 && code <= 126 ->\n        String.make 1 (Char.chr code)\n    | _ when Uucp.Alpha.is_alphabetic ch ->\n        singleton ch\n    | code when code <= 127 ->\n        Printf.sprintf \"\\\\x%02x\" code\n    | code when code <= 0xffff ->\n        Printf.sprintf \"\\\\u%04x\" code\n    | code ->\n        Printf.sprintf \"\\\\U%06x\" code\n\nlet add_escaped_char buf ch =\n  match Uchar.to_int ch with\n    | 7 ->\n        Buffer.add_string buf \"\\\\a\"\n    | 8 ->\n        Buffer.add_string buf \"\\\\b\"\n    | 9 ->\n        Buffer.add_string buf \"\\\\t\"\n    | 10 ->\n        Buffer.add_string buf \"\\\\n\"\n    | 11 ->\n        Buffer.add_string buf \"\\\\v\"\n    | 12 ->\n        Buffer.add_string buf \"\\\\f\"\n    | 13 ->\n        Buffer.add_string buf \"\\\\r\"\n    | 27 ->\n        Buffer.add_string buf \"\\\\e\"\n    | 92 ->\n        Buffer.add_string buf \"\\\\\\\\\"\n    | code when code >= 32 && code <= 126 ->\n        Buffer.add_char buf (Char.chr code)\n    | _ when Uucp.Alpha.is_alphabetic ch ->\n        add buf ch\n    | code when code <= 127 ->\n        Printf.bprintf buf \"\\\\x%02x\" code\n    | code when code <= 0xffff ->\n        Printf.bprintf buf \"\\\\u%04x\" code\n    | code ->\n        Printf.bprintf buf \"\\\\U%06x\" code\n\nlet escaped str =\n  let buf = Buffer.create (String.length str) in\n  iter (add_escaped_char buf) str;\n  Buffer.contents buf\n\nlet add_escaped buf str =\n  iter (add_escaped_char buf) str\n\nlet add_escaped_string buf encoding str =\n  let b = Buffer.create (String.length str) in\n  let d = Uutf.decoder ~encoding (`String str) in\n  let rec loop () =\n    match Uutf.decode d with\n    | `Uchar u -> ignore (Uutf.Buffer.add_utf_8 b u); loop ()\n    | `End -> add_escaped buf (Buffer.contents b)\n    | `Malformed _ ->\n        String.iter\n          (function\n             | '\\x20' .. '\\x7e' as ch ->\n                 Buffer.add_char buf ch\n             | ch ->\n                 Printf.bprintf buf \"\\\\y%02x\" (Char.code ch))\n          str\n    | `Await -> assert false\n  in\n  loop ()\n\nlet escaped_string enc str =\n  let buf = Buffer.create (String.length str) in\n  add_escaped_string buf enc str;\n  Buffer.contents buf\n"
  },
  {
    "path": "packages/zed/src/zed_utf8.mli",
    "content": "(*\n * zed_utf8.mli\n * ------------\n * Copyright : (c) 2011, Jeremie Dimino <jeremie@dimino.org>\n * Licence   : BSD3\n *\n * This file is a part of Zed, an editor engine.\n *)\n\n(** UTF-8 enoded strings *)\n\ntype t = string\n    (** Type of UTF-8 encoded strings. *)\n\nexception Invalid of string * string\n  (** [Invalid(error, text)] Exception raised when an invalid UTF-8\n      encoded string is encountered. [text] is the faulty text and\n      [error] is a description of the first error in [text]. *)\n\nexception Out_of_bounds\n  (** Exception raised when trying to access a character which is\n      outside the bounds of a string. *)\n\n(** {5 Validation} *)\n\n(** Result of cheking a string for correct UTF-8. *)\ntype check_result =\n  | Correct of int\n      (** The string is correctly UTF-8 encoded, and the paramter is\n          the length of the string. *)\n  | Message of string\n      (** The string is invalid and the parameter is an error\n          message. *)\n\nval check : t -> check_result\n  (** [check str] checks that [str] is a valid UTF-8 encoded\n      string. *)\n\nval validate : t -> int\n  (** Same as check but raises an exception in case the argument is\n      not a valid text, otherwise returns the length of the string. *)\n\nval next_error : t -> int -> int * int * string\n  (** [next_error str ofs] returns [(ofs', count, msg)] where [ofs']\n      is the offset of the start of the first invalid sequence after\n      [ofs] (inclusive) in [str], [count] is the number of unicode\n      character between [ofs] and [ofs'] (exclusive) and [msg] is an\n      error message. If there is no error until the end of string then\n      [ofs] is [String.length str] and [msg] is the empty string. *)\n\n(** {5 Construction} *)\n\nval singleton : Uchar.t -> t\n  (** [singleton ch] creates a string of length 1 containing only the\n      given character. *)\n\nval make : int -> Uchar.t -> t\n  (** [make n ch] creates a string of length [n] filled with [ch]. *)\n\nval init : int -> (int -> Uchar.t) -> t\n  (** [init n f] returns the contenation of [singleton (f 0)],\n      [singleton (f 1)], ..., [singleton (f (n - 1))]. *)\n\nval rev_init : int -> (int -> Uchar.t) -> t\n  (** [rev_init n f] returns the contenation of [singleton (f (n -\n      1))], ..., [singleton (f 1)], [singleton (f 0)]. *)\n\n(** {5 Informations} *)\n\nval length : t -> int\n  (** Returns the length of the given string. *)\n\n(** {5 Comparison} *)\n\nval compare : t -> t -> int\n  (** Compares two strings (in code point order). *)\n\n(** {5 Random access} *)\n\nval get : t -> int -> Uchar.t\n  (** [get str idx] returns the character at index [idx] in\n      [str]. *)\n\n(** {5 String manipulation} *)\n\nval sub : t -> int -> int -> t\n  (** [sub str ofs len] Returns the sub-string of [str] starting at\n      [ofs] and of length [len]. *)\n\nval break : t -> int -> t * t\n  (** [break str pos] returns the sub-strings before and after [pos]\n      in [str]. It is more efficient than creating two sub-strings\n      with {!sub}. *)\n\nval before : t -> int -> t\n  (** [before str pos] returns the sub-string before [pos] in [str] *)\n\nval after : t -> int -> t\n  (** [after str pos] returns the sub-string after [pos] in [str] *)\n\nval insert : t -> int -> t -> t\n  (** [insert str pos sub] inserts [sub] in [str] at position\n      [pos]. *)\n\nval remove : t -> int -> int -> t\n  (** [remove str pos len] removes the [len] characters at position\n      [pos] in [str] *)\n\nval replace : t -> int -> int -> t -> t\n  (** [replace str pos len repl] replaces the [len] characters at\n      position [pos] in [str] by [repl]. *)\n\n(** {5 Tranformation} *)\n\nval rev : t -> t\n  (** [rev str] reverses all characters of [str]. *)\n\nval concat : t -> t list -> t\n  (** [concat sep l] returns the concatenation of all strings of [l]\n      separated by [sep]. *)\n\nval rev_concat : t -> t list -> t\n  (** [concat sep l] returns the concatenation of all strings of [l]\n      in reverse order separated by [sep]. *)\n\nval explode : t -> Uchar.t list\n  (** [explode str] returns the list of all characters of [str]. *)\n\nval rev_explode : t -> Uchar.t list\n  (** [rev_explode str] returns the list of all characters of [str] in\n      reverse order. *)\n\nval implode : Uchar.t list -> t\n  (** [implode l] returns the concatenation of all characters of [l]. *)\n\nval rev_implode : Uchar.t list -> t\n  (** [rev_implode l] is the same as [implode (List.rev l)] but more\n      efficient. *)\n\n(** {5 Text traversals} *)\n\nval iter : (Uchar.t -> unit) -> t -> unit\n  (** [iter f str] applies [f] an all characters of [str] starting\n      from the left. *)\n\nval rev_iter : (Uchar.t -> unit) -> t -> unit\n  (** [rev_iter f str] applies [f] an all characters of [str] starting\n      from the right. *)\n\nval fold : (Uchar.t -> 'a -> 'a) -> t -> 'a -> 'a\n  (** [fold f str acc] applies [f] on all characters of [str]\n      starting from the left, accumulating a value. *)\n\nval rev_fold : (Uchar.t -> 'a -> 'a) -> t -> 'a -> 'a\n  (** [rev_fold f str acc] applies [f] on all characters of [str]\n      starting from the right, accumulating a value. *)\n\nval map : (Uchar.t -> Uchar.t) -> t -> t\n  (** [map f str] maps all characters of [str] with [f]. *)\n\nval rev_map : (Uchar.t -> Uchar.t) -> t -> t\n  (** [rev_map f str] maps all characters of [str] with [f] in reverse\n      order. *)\n\nval map_concat : (Uchar.t -> t) -> t -> t\n  (** [map f str] maps all characters of [str] with [f] and\n      concatenate the result. *)\n\nval rev_map_concat : (Uchar.t -> t) -> t -> t\n  (** [rev_map f str] maps all characters of [str] with [f] in reverse\n      order and concatenate the result. *)\n\nval filter : (Uchar.t -> bool) -> t -> t\n  (** [filter f str] filters characters of [str] with [f]. *)\n\nval rev_filter : (Uchar.t -> bool) -> t -> t\n  (** [rev_filter f str] filters characters of [str] with [f] in\n      reverse order. *)\n\nval filter_map : (Uchar.t -> Uchar.t option) -> t -> t\n  (** [filter_map f str] filters and maps characters of [str] with\n      [f]. *)\n\nval rev_filter_map : (Uchar.t -> Uchar.t option) -> t -> t\n  (** [rev_filter_map f str] filters and maps characters of [str] with\n      [f] in reverse order. *)\n\nval filter_map_concat : (Uchar.t -> t option) -> t -> t\n  (** [filter_map f str] filters and maps characters of [str] with [f]\n      and concatenate the result. *)\n\nval rev_filter_map_concat : (Uchar.t -> t option) -> t -> t\n  (** [rev_filter_map f str] filters and maps characters of [str] with\n      [f] in reverse order and concatenate the result. *)\n\n(** {5 Scanning} *)\n\nval for_all : (Uchar.t -> bool) -> t -> bool\n  (** [for_all f text] returns whether all characters of [text] verify\n      the predicate [f]. *)\n\nval exists : (Uchar.t -> bool) -> t -> bool\n  (** [exists f text] returns whether at least one character of [text]\n      verify [f]. *)\n\nval count : (Uchar.t -> bool) -> t -> int\n  (** [count f text] returhs the number of characters of [text]\n      verifying [f]. *)\n\n(** {5 Tests} *)\n\nval contains : t -> t -> bool\n  (** [contains text sub] returns whether [sub] appears in [text] *)\n\nval starts_with : t -> t -> bool\n  (** [starts_with text prefix] returns [true] iff [s] starts with\n      [prefix]. *)\n\nval ends_with : t -> t -> bool\n  (** [ends_with text suffix] returns [true] iff [s] ends with\n      [suffix]. *)\n\n(** {5 Stripping} *)\n\nval strip : ?predicate : (Uchar.t -> bool) -> t -> t\n  (** [strip ?predicate text] returns [text] without its firsts and\n      lasts characters that match [predicate]. [predicate] default to\n      testing whether the given character has the [`White_Space]\n      unicode property. For example:\n      {[\n        strip \"\\n  foo\\n  \" = \"foo\"\n      ]}\n  *)\n\nval lstrip : ?predicate : (Uchar.t -> bool) -> t -> t\n  (** [lstrip ?predicate text] is the same as {!strip} but it only\n      removes characters at the left of [text]. *)\n\nval rstrip : ?predicate : (Uchar.t -> bool) -> t -> t\n  (** [lstrip ?predicate text] is the same as {!strip} but it only\n      removes characters at the right of [text]. *)\n\nval lchop : t -> t\n  (** [lchop t] returns [t] without is first character. Returns [\"\"]\n      if [t = \"\"] *)\n\nval rchop : t -> t\n  (** [rchop t] returns [t] without is last character. Returns [\"\"] if\n      [t = \"\"]. *)\n\n(** {5 Buffers} *)\n\nval add : Buffer.t -> Uchar.t -> unit\n  (** [add buf ch] is the same as [Buffer.add_string buf (singleton\n      ch)] but is more efficient. *)\n\n(** {5 Escaping} *)\n\nval escaped_char : Uchar.t -> t\n  (** [escaped_char ch] returns a string containg [ch] or an escaped\n      version of [ch] if:\n      - [ch] is a control character (code < 32)\n      - [ch] is the character with code 127\n      - [ch] is a non-ascii, non-alphabetic character\n      It uses the syntax [\\xXX], [\\uXXXX], [\\UXXXXXX] or a specific\n      escape sequence [\\n, \\r, ...]. *)\n\nval add_escaped_char : Buffer.t -> Uchar.t -> unit\n  (** [add_escaped_char buf ch] is the same as [Buffer.add_string buf\n      (escaped_char ch)] but a bit more efficient. *)\n\nval escaped : t -> t\n  (** [escaped text] escape all characters of [text] as with\n      [escape_char]. *)\n\nval add_escaped : Buffer.t -> t -> unit\n  (** [add_escaped_char buf text] is the same as [Buffer.add_string\n      buf (escaped text)] but a bit more efficient. *)\n\nval escaped_string : Uutf.decoder_encoding -> string -> t\n  (** [escaped_string enc str] escape the string [str] which is\n      encoded with encoding [enc]. If decoding [str] with [enc] fails,\n      it escape all non-printable bytes of [str] with the syntax\n      [\\yAB]. *)\n\nval add_escaped_string : Buffer.t -> Uutf.decoder_encoding -> string -> unit\n  (** [add_escaped_char buf enc text] is the same as\n      [Buffer.add_string buf (escaped_string enc text)] but a bit more\n      efficient. *)\n\n(** {5 Safe offset API} *)\n\nval next : t -> int -> int\n  (** [next str ofs] returns the offset of the next character in\n      [str]. *)\n\nval prev : t -> int -> int\n  (** [prev str ofs] returns the offset of the previous character in\n      [str]. *)\n\nval extract : t -> int -> Uchar.t\n  (** [extract str ofs] returns the code-point at offset [ofs] in\n      [str]. *)\n\nval extract_next : t -> int -> Uchar.t * int\n  (** [extract_next str ofs] returns the code-point at offset [ofs] in\n      [str] and the offset of the next character. *)\n\nval extract_prev : t -> int -> Uchar.t * int\n  (** [extract_prev str ofs] returns the code-point at the previous\n      offset in [str] and this offset. *)\n\n(** {5 Unsafe offset API} *)\n\n(** These functions does not check that the given offset is inside the\n    bounds of the given string. *)\n\nval unsafe_next : t -> int -> int\n  (** [unsafe_next str ofs] returns the offset of the next character\n      in [str]. *)\n\nval unsafe_prev : t -> int -> int\n  (** [unsafe_prev str ofs] returns the offset of the previous\n      character in [str]. *)\n\nval unsafe_extract : t -> int -> Uchar.t\n  (** [unsafe_extract str ofs] returns the code-point at offset [ofs]\n      in [str]. *)\n\nval unsafe_extract_next : t -> int -> Uchar.t * int\n  (** [unsafe_extract_next str ofs] returns the code-point at offset\n      [ofs] in [str] and the offset the next character. *)\n\nval unsafe_extract_prev : t -> int -> Uchar.t * int\n  (** [unsafe_extract_prev str ofs] returns the code-point at the\n      previous offset in [str] and this offset. *)"
  },
  {
    "path": "reason-harfbuzz.opam",
    "content": "opam-version: \"1.2\"\nversion: \"dev\"\nmaintainer: \"bryphe@outrunlabs.com\"\nauthor: [\"Bryan Phelps\"]\nbuild: [\n\n]\n"
  },
  {
    "path": "reason-sdl2.opam",
    "content": "opam-version: \"1.2\"\nversion: \"dev\"\nmaintainer: \"bryphe@outlook.com\"\nauthor: [\"Bryan Phelps\"]\nbuild: [\n\n]\n"
  },
  {
    "path": "reason-skia.opam",
    "content": "opam-version: \"1.2\"\nversion: \"dev\"\nmaintainer: \"bryphe@outlook.com\"\nauthor: [\"Bryan Phelps\"]\nbuild: [\n\n]\n"
  },
  {
    "path": "scripts/docker/archlinux/Dockerfile",
    "content": "FROM archlinux:latest\n\nRUN pacman -Syu --noconfirm\nRUN pacman -S --noconfirm base base-devel git npm perl\n#perl for shasum binary\n\n# Revery special deps\nRUN pacman -S --noconfirm libpng libxcursor libxi libxinerama libxrandr harfbuzz glu gtk3 fontconfig nasm python2 clang\nENV PATH=$PATH:/usr/bin/core_perl\n\n# workaround for esy-skia that need `python` binary\nRUN cp /usr/bin/python2 /usr/bin/python\n\n# workaround hack while ocaml-secondary-compiler@4.08.1 work well with gcc 10\nRUN mv /usr/bin/gcc /usr/bin/gcc-orig\nRUN echo -en '#!/usr/bin/sh\\n/usr/bin/gcc-orig -fcommon \"$@\"' > /usr/bin/gcc\nRUN chmod u+x /usr/bin/gcc\n\nRUN npm install --verbose --global --unsafe-perm=true esy@0.6.6\n"
  },
  {
    "path": "scripts/docker/centos/Dockerfile",
    "content": "FROM centos:7\n\nRUN yum -y update\n\nRUN yum -y install centos-release-scl\nRUN yum-config-manager --enable rhel-server-rhscl-7-rpms\nRUN yum -y install llvm-toolset-7.0\nRUN scl enable llvm-toolset-7.0 'clang -v'\n\nRUN yum -y install gcc-c++ make sudo\nRUN curl -sL https://rpm.nodesource.com/setup_10.x | sudo -E bash -\nRUN yum -y install nodejs npm coreutils grep tar sed gawk diffutils autoconf unzip\n\nRUN yum -y install file fuse fuse-devel wget bzip2-devel libXt-devel libSM-devel libICE-devel ncurses-devel libacl-devel libxrandr-devel libXinerama-devel libXcursor-devel libXi-devel mesa-libGL-devel mesa-libGLU-devel gtk3-devel perl-Digest-SHA bzip2 m4 patch which cmake3 git nasm\n\nRUN rpm -i https://download-ib01.fedoraproject.org/pub/epel/7/x86_64/Packages/c/colm-0.13.0.4-2.el7.x86_64.rpm\nRUN rpm -i https://download-ib01.fedoraproject.org/pub/epel/7/x86_64/Packages/r/ragel-7.0.0.9-2.el7.x86_64.rpm\n\nRUN yum -y install /usr/lib64/libasan.so.0.0.0\n\nRUN node -v\nRUN npm -v\n\nRUN npm install --global --unsafe-perm=true esy@0.6.6\n\nRUN yum -y install nasm\nRUN yum -y install https://repo.ius.io/ius-release-el7.rpm\nRUN yum -y remove git\nRUN yum -y install epel-release\nRUN yum -y install git222\n\nRUN node -v\nRUN npm -v\nRUN git --version\n"
  },
  {
    "path": "scripts/docker-build.sh",
    "content": "source /opt/rh/llvm-toolset-7.0/enable\nclang -v\n\n# Workaround for: https://github.com/esy/esy/issues/1227\n# Concurrent fetch seems to cause hang on Docker in Azure Pipelines..\nexport ESY__BUILD_CONCURRENCY=1\nexport ESY__FETCH_CONCURRENCY=1\n\nesy install\nesy build\n"
  },
  {
    "path": "scripts/make-binary-file.js",
    "content": "const fs = require(\"fs\");\n\nconst testData = new Uint8Array(5);\ntestData[0] = 255;\ntestData[1] = 0;\ntestData[2] = 1;\ntestData[3] = 2;\ntestData[4] = 128;\n\nfs.writeFileSync(\"binary.dat\", new Buffer(testData));\n"
  },
  {
    "path": "scripts/release.sh",
    "content": "# Set path\nRELEASE_PATH=\"$(pwd)/_release\"\nmkdir -p $RELEASE_PATH\n\ncp $cur__bin/* $RELEASE_PATH\n"
  },
  {
    "path": "src/Core/App.re",
    "content": "%import\n\"../Native/config.h\";\n\nmodule AppLog = (val Log.withNamespace(\"Revery.App\"));\nmodule SdlLog = (val Log.withNamespace(\"Revery.SDL2\"));\n\nmodule Log = AppLog;\n\ntype delegatedFunc = unit => unit;\ntype unsubscribe = unit => unit;\n\ntype quitResponse =\n  | AllowQuit\n  | PreventQuit;\n\nlet mergeQuitResponse = (a: quitResponse, b: quitResponse) => {\n  switch (a, b) {\n  | (AllowQuit, AllowQuit) => AllowQuit\n  | _ => PreventQuit\n  };\n};\n\ntype t = {\n  mutable idleCount: int,\n  mutable isFirstRender: bool,\n  mutable isQuitting: bool,\n  windows: Hashtbl.t(int, Window.t),\n  onIdle: Event.t(unit),\n  onBeforeQuit: Event.Fanout.t(int, quitResponse),\n  onFileOpen: Event.t(string),\n  mutable canIdle: unit => bool,\n};\n\nlet framesToIdle = 10;\n\nlet getWindows = (app: t) => {\n  Hashtbl.to_seq_values(app.windows) |> List.of_seq;\n};\n\nlet getWindowById = (app: t, id: int) => {\n  Hashtbl.find_opt(app.windows, id);\n};\n\nlet _tryToClose = (app: t, window: Window.t) => {\n  let uniqueId = Window.getUniqueId(window);\n  if (Window.canQuit(window)) {\n    Log.debugf(m => m(\"canQuit is true for window %i\", uniqueId));\n    Hashtbl.remove(app.windows, uniqueId);\n  } else {\n    Log.debugf(m => m(\"canQuit is false for window %i\", uniqueId));\n  };\n};\n\nlet _tryToCloseAll = (app: t) => {\n  let windows = Hashtbl.to_seq_values(app.windows);\n  Seq.iter(w => _tryToClose(app, w), windows);\n};\n\nlet quit = (~askNicely=false, ~code=0, app: t) => {\n  if (askNicely) {\n    _tryToCloseAll(app);\n  };\n\n  if (Hashtbl.length(app.windows) == 0 || !askNicely) {\n    Revery_Native.uninitApp();\n\n    // Verify [quit] wasn't called recursively from a beforeQuit handler\n    if (!app.isQuitting) {\n      Log.info(\"onBeforeQuit\");\n      app.isQuitting = true;\n      let quitResponse =\n        Event.Fanout.dispatch(\n          app.onBeforeQuit,\n          mergeQuitResponse,\n          AllowQuit,\n          code,\n        );\n      app.isQuitting = false;\n\n      switch (quitResponse) {\n      | AllowQuit =>\n        Log.info(\"Quitting\");\n        exit(code);\n      | PreventQuit => Log.info(\"Quit prevented by event handler\")\n      };\n    };\n  };\n};\n\nlet isIdle = (app: t) => app.idleCount >= framesToIdle;\n\nlet _mainThreadMutex = Mutex.create();\n/* A list of pending functions the main thread will need to run */\nlet _mainThreadPendingFunctions: ref(list(delegatedFunc)) = ref([]);\nlet _anyPendingWork: ref(bool) = ref(false);\nlet runOnMainThread = f => {\n  Mutex.lock(_mainThreadMutex);\n  _mainThreadPendingFunctions := [f, ..._mainThreadPendingFunctions^];\n  _anyPendingWork := true;\n  Mutex.unlock(_mainThreadMutex);\n\n  // If we're 'idle' - in a [waitTimeout], dispatch an event to wake up the main thread\n  Sdl2.Event.push();\n};\n\nlet _anyPendingMainThreadJobs = () => {\n  _anyPendingWork^;\n};\n\nlet setCanIdle = (f, app: t) => {\n  app.canIdle = f;\n};\n\nlet onBeforeQuit = app => Event.Fanout.subscribe(app.onBeforeQuit);\nlet onIdle = app => Event.subscribe(app.onIdle);\nlet onFileOpen = app => Event.subscribe(app.onFileOpen);\n\n/* Execute any pending main thread jobs */\nlet _doPendingMainThreadJobs = () => {\n  let jobs = {\n    Mutex.lock(_mainThreadMutex);\n    let ret = _mainThreadPendingFunctions^;\n    _anyPendingWork := false;\n    _mainThreadPendingFunctions := [];\n    Mutex.unlock(_mainThreadMutex);\n    ret;\n  };\n\n  jobs |> List.rev |> List.iter(f => f());\n};\n\nlet flushPendingCallbacks = () => _doPendingMainThreadJobs();\n\nlet createWindow =\n    (~createOptions=WindowCreateOptions.default, app: t, windowName) => {\n  let w = Window.create(windowName, createOptions);\n  /* Window.render(w) */\n  let uniqueId = Window.getUniqueId(w);\n  Hashtbl.add(app.windows, uniqueId, w);\n  w;\n};\n\nlet _anyWindowsDirty = (app: t) =>\n  List.fold_left(\n    (prev, w) => prev || Window.isDirty(w),\n    false,\n    getWindows(app),\n  );\n\nlet initConsole = () =>\n  if (Sys.win32) {\n    // First, try attaching to an existing console.\n    let attachResult = Sdl2.Platform.win32AttachConsole();\n\n    // If that wasn't available - try to allocate a new one.\n    let _code =\n      if (attachResult == 0) {\n        Sdl2.Platform.win32AllocConsole();\n      } else {\n        attachResult;\n      };\n    ();\n  };\n\nlet handleKeymapChanged = () => {\n  AppLog.info(\"Keymap changed\");\n\n  if (Sys.win32) {\n    // Workaround for https://github.com/onivim/oni2/issues/1657\n    // When changing the keyboard layout, on Windows 10, the\n    // modifier keys like the Windows/GUI key 'stick'...\n    // ...this resets the state of the modifiers.\n    Sdl2.Keymod.setState(\n      Sdl2.Keymod.none,\n    );\n  };\n};\n\n%ifdef\nUSE_GTK;\n\nlet runGtkIteration = () =>\n  if (Revery_Native.Gtk.eventsPending()) {\n    AppLog.debug(\"Running Gtk iteration\");\n    let _quit: bool = Revery_Native.Gtk.mainIteration();\n    ();\n  };\n\n[%%else];\n\nlet runGtkIteration = () => ();\n\n[%%endif];\n\nlet start = init => {\n  let appInstance: t = {\n    windows: Hashtbl.create(1),\n    idleCount: 0,\n    isFirstRender: true,\n    isQuitting: false,\n    onBeforeQuit: Event.create(),\n    onIdle: Event.create(),\n    onFileOpen: Event.create(),\n    canIdle: () => true,\n  };\n\n  Sdl2.Log.setOutputFunction((_category, priority, message) =>\n    switch (priority) {\n    | Verbose\n    | Debug => SdlLog.trace(message)\n    | Info => SdlLog.info(message)\n    | Warn => SdlLog.warn(message)\n    | Error\n    | Critical => SdlLog.error(message)\n    }\n  );\n\n  let _ = Sdl2.init();\n\n  let _dispose = init(appInstance);\n\n  let _ = Sdl2.ScreenSaver.enable();\n\n  let _handleEvent = evt => {\n    let handleEvent = windowID => {\n      let window = getWindowById(appInstance, windowID);\n      switch (window) {\n      | Some(win) => Window.handleEvent(evt, win)\n      | None =>\n        Log.errorf(m =>\n          m(\n            \"Unable to find window with ID: %i - event: %s\",\n            windowID,\n            Sdl2.Event.show(evt),\n          )\n        )\n      };\n    };\n    switch (evt) {\n    | Sdl2.Event.MouseButtonUp({windowID, _}) => handleEvent(windowID)\n    | Sdl2.Event.MouseButtonDown({windowID, _}) => handleEvent(windowID)\n    | Sdl2.Event.MouseMotion({windowID, _}) => handleEvent(windowID)\n    | Sdl2.Event.MouseWheel({windowID, _}) => handleEvent(windowID)\n    | Sdl2.Event.KeyDown({windowID, _}) => handleEvent(windowID)\n    | Sdl2.Event.KeyUp({windowID, _}) => handleEvent(windowID)\n    | Sdl2.Event.TextInput({windowID, _}) => handleEvent(windowID)\n    | Sdl2.Event.TextEditing({windowID, _}) => handleEvent(windowID)\n    | Sdl2.Event.KeymapChanged => handleKeymapChanged()\n    | Sdl2.Event.WindowResized({windowID, _}) => handleEvent(windowID)\n    | Sdl2.Event.WindowSizeChanged({windowID, _}) => handleEvent(windowID)\n    | Sdl2.Event.WindowExposed({windowID, _}) => handleEvent(windowID)\n    | Sdl2.Event.WindowFocusGained({windowID, _}) => handleEvent(windowID)\n    | Sdl2.Event.WindowFocusLost({windowID, _}) => handleEvent(windowID)\n    | Sdl2.Event.WindowMaximized({windowID, _}) => handleEvent(windowID)\n    | Sdl2.Event.WindowFullscreen({windowID, _}) => handleEvent(windowID)\n    | Sdl2.Event.WindowMinimized({windowID, _}) => handleEvent(windowID)\n    | Sdl2.Event.WindowRestored({windowID, _}) => handleEvent(windowID)\n    | Sdl2.Event.WindowMoved({windowID, _}) => handleEvent(windowID)\n    | Sdl2.Event.WindowEnter({windowID}) => handleEvent(windowID)\n    | Sdl2.Event.WindowLeave({windowID}) => handleEvent(windowID)\n    | Sdl2.Event.WindowClosed({windowID, _}) =>\n      Log.debugf(m => m(\"Got WindowClosed event for %i\", windowID));\n      handleEvent(windowID);\n      switch (getWindowById(appInstance, windowID)) {\n      | None => ()\n      | Some(win) => _tryToClose(appInstance, win)\n      };\n    | Sdl2.Event.DropBegin({windowID, _}) => handleEvent(windowID)\n    | Sdl2.Event.DropFile({windowID, _}) => handleEvent(windowID)\n    | Sdl2.Event.DropComplete({windowID, _}) => handleEvent(windowID)\n    | Sdl2.Event.Quit =>\n      // Sometimes, on Mac, we could get a 'quit' without a\n      // corresponding WindowClosed event - this can happen\n      // if Command+Q is pressed. In that case, we'll try\n      // closing all the windows - and if they all close,\n      // we'll exit the app.\n      quit(~askNicely=true, ~code=0, appInstance)\n    | _ => ()\n    };\n  };\n\n  let _flushEvents = () => {\n    let processingEvents = ref(true);\n\n    while (processingEvents^) {\n      let evt = Sdl2.Event.poll();\n      switch (evt) {\n      | None => processingEvents := false\n      | Some(v) => _handleEvent(v)\n      };\n    };\n  };\n\n  let dispatchFileOpen = Event.dispatch(appInstance.onFileOpen);\n  Callback.register(\"revery_dispatchFileOpen\", dispatchFileOpen);\n\n  Revery_Native.initApp();\n\n  AppLog.infof(m => m(\"Operating in locale : %s\", Environment.userLocale));\n\n  let appLoop = () => {\n    _flushEvents();\n\n    runGtkIteration();\n\n    Tick.Default.pump();\n\n    if (appInstance.isFirstRender\n        || _anyWindowsDirty(appInstance)\n        || _anyPendingMainThreadJobs()\n        || !appInstance.canIdle()) {\n      if (appInstance.idleCount > 0) {\n        Log.debug(\"Upshifting into active state.\");\n      };\n\n      Performance.bench(\"_doPendingMainThreadJobs\", () =>\n        _doPendingMainThreadJobs()\n      );\n      Performance.bench(\"renderWindows\", () => {\n        List.iter(w => Window.render(w), getWindows(appInstance))\n      });\n\n      appInstance.idleCount = 0;\n      appInstance.isFirstRender = false;\n    } else {\n      appInstance.idleCount = appInstance.idleCount + 1;\n\n      if (appInstance.idleCount === framesToIdle) {\n        Log.debug(\"Downshifting into idle state...\");\n        let _: unit = Event.dispatch(appInstance.onIdle, ());\n        ();\n      };\n\n      let evt = Sdl2.Event.waitTimeout(250);\n      switch (evt) {\n      | None => ()\n      | Some(evt) => _handleEvent(evt)\n      };\n    };\n\n    Environment.yield();\n\n    false;\n  };\n\n  Sdl2.renderLoop(appLoop);\n};\nlet start = init => Sdl2.main(() => start(init));\n"
  },
  {
    "path": "src/Core/App.rei",
    "content": "/** [t] is the type representing the running app instance\n\n  There is only ever a single app instance, but an app instance\n  may have multiple windows.\n*/\ntype t;\n\n/** [getWindows(app)] returns the list of all open [Window.t] instances */\nlet getWindows: t => list(Window.t);\n\n/**\n[quit(~askNicely, ~code, c)] causes the App to quit with exit code [c]\n\n[askNicely] specifies whether quit should be forced (default [false]). If [true],\nthe canQuit handlers will be run for each window. If [false], the canQuit handlers\nwill be ignored.\n\n[code] specifies the exit code. Defaults to [0].\n*/\nlet quit: (~askNicely: bool=?, ~code: int=?, t) => unit;\n\n/** [isIdle(app)] returns true if the app is idling, false othwrise */\nlet isIdle: t => bool;\n\ntype delegatedFunc = unit => unit;\n\n/** [runOnMainThread(f)] schedules the function [f] to run during the next\n    render frame on the main thread.\n\n    If the application is idle, this will cause it to become active, and trigger a re-render.\n*/\nlet runOnMainThread: delegatedFunc => unit;\n\n/** [flushPendingCallbacks(f)] will explicitly run all the callbacks\n    queued up via [runOnMainThread].\n\n    In general, this should not need to be called by user code,\n    as it happens as part of the application lifecycle.\n\n    However, it may be necessary to call this for tests that queue\n    up callbacks via [runOnMainThread].\n*/\nlet flushPendingCallbacks: unit => unit;\n\n/** [setCanIdle(f, app)] registers a callback [f]. Each frame, [f] will be called\n to check if the application can idle. If [f()] returns [false], the app will not\n idle and will render. This is useful if you have animations active, but in general,\n you want to allow the app to idle to minimize CPU and battery usage.\n*/\nlet setCanIdle: (unit => bool, t) => unit;\n\ntype unsubscribe = unit => unit;\n\ntype quitResponse =\n  | AllowQuit\n  | PreventQuit;\n\n/** [onBeforeQuit(app, f) registers a callback [f] that is called prior to quitting] */\nlet onBeforeQuit: (t, int => quitResponse) => unsubscribe;\n\n/** [onIdle(app, f) registers a callback [f] that is called when the application is idle.\n\n  This allows you to defer work when the app is not under load - (for example,\n  this is may be a good time to garbage collect). This will be called\n  when multiple frames have passed without requiring a render.\n*/\nlet onIdle: (t, unit => unit) => unsubscribe;\n\n/** [onFileOpen(app, f) registers a callback [f] that is called when the host OS is trying to open a file]\n\n  Note that as of now, this function only is called on macOS, as other OS's have other\n  mechanisms for opening files (usually just through CLI args)\n*/\nlet onFileOpen: (t, string => unit) => unsubscribe;\n\n/** [createWindow ~createOptions, app, name] creates a new window */\nlet createWindow:\n  (~createOptions: WindowCreateOptions.t=?, t, string) => Window.t;\n\n/** [start] is the entry point for a Revery application. This initiates\n  the Revery application lifecycle, and an app instance ([t]) is passed\n  to an initialization function.\n*/\n\nlet start: (t => unit) => unit;\n\n/** [initConsole] (Windows-only) attaches or allocates a console,\n  to show logging output. No-op on other platforms.\n*/\nlet initConsole: unit => unit;\n"
  },
  {
    "path": "src/Core/Color.re",
    "content": "type t = Skia.Color.t;\n\nlet rgba = (r, g, b, a) => Skia.Color.Float.makeArgb(a, r, g, b);\n\nlet rgb = (r, g, b) => Skia.Color.Float.makeArgb(1.0, r, g, b);\n\nlet rgba_int = (r, g, b, a) =>\n  Skia.Color.makeArgb(\n    a |> Int32.of_int,\n    r |> Int32.of_int,\n    g |> Int32.of_int,\n    b |> Int32.of_int,\n  );\n\nlet rgb_int = (r, g, b) =>\n  Skia.Color.makeArgb(\n    255l,\n    r |> Int32.of_int,\n    g |> Int32.of_int,\n    b |> Int32.of_int,\n  );\n\nlet getAlpha = Skia.Color.Float.getA;\n\n// Matches:\n// #FFF\n// #FFFA\n// #FFF00\n//let singleHex = Str.regexp(\"#\\\\([a-f\\\\|A-F\\\\|0-9]\\\\)\\\\([a-f\\\\|A-F\\\\|0-9]\\\\)\\\\([a-f\\\\|A-F\\\\|0-9]\\\\)\\\\([a-f\\\\|A-F\\\\|0-9]\\\\)\");\nlet singleHex =\n  Re.Perl.re(\n    \"#([a-f|A-F|0-9])([a-f|A-F|0-9])([a-f|A-F|0-9])([a-f|A-F|0-9]?[a-f|A-F|0-9]?)\",\n  )\n  |> Re.Perl.compile;\n\n// Matches:\n// #FFFFFF\n// #FFFFFF0\n// #FFFFFF00\nlet doubleHex =\n  Re.Perl.re(\n    \"#([a-f|A-F|0-9][a-f|A-F|0-9])([a-f|A-F|0-9][a-f|A-F|0-9])([a-f|A-F|0-9][a-f|A-F|0-9])([a-f|A-F|0-9]?[a-f|A-F|0-9]?)\",\n  )\n  |> Re.Perl.compile;\n\nexception ColorHexParseException(string);\n\nlet parseColor = c => {\n  let len = String.length(c);\n  let result =\n    switch (len) {\n    // Zero-length case only happens in the alpha channel, if no alpha has been specified\n    | 0 => Some(255)\n    | 1 =>\n      switch (int_of_string_opt(\"0x\" ++ c)) {\n      | Some(v) => Some(v * 16 + v)\n      | None => None\n      }\n    | 2 => int_of_string_opt(\"0x\" ++ c)\n    | _ => None\n    };\n\n  switch (result) {\n  | None =>\n    raise(ColorHexParseException(\"Unable to parse color component: \" ++ c))\n  | Some(v) => float_of_int(v) /. 255.\n  };\n};\n\nlet hex = str =>\n  // First, try and parse with the 'double hex' option\n  switch (Re.exec_opt(doubleHex, str)) {\n  | Some(matches) =>\n    let r = Re.Group.get(matches, 1) |> parseColor;\n    let g = Re.Group.get(matches, 2) |> parseColor;\n    let b = Re.Group.get(matches, 3) |> parseColor;\n    let a = Re.Group.get(matches, 4) |> parseColor;\n    rgba(r, g, b, a);\n  | None =>\n    // Now, try and parse with the 'single hex' option\n    switch (Re.exec_opt(singleHex, str)) {\n    | Some(matches) =>\n      let r = Re.Group.get(matches, 1) |> parseColor;\n      let g = Re.Group.get(matches, 2) |> parseColor;\n      let b = Re.Group.get(matches, 3) |> parseColor;\n      let a = Re.Group.get(matches, 4) |> parseColor;\n      rgba(r, g, b, a);\n    | None => raise(ColorHexParseException(\"Unable to parse color: \" ++ str))\n    }\n  };\n\nlet multiplyAlpha = (opacity: float, color: t) => {\n  let a = Skia.Color.Float.getA(color);\n  let r = Skia.Color.Float.getR(color);\n  let g = Skia.Color.Float.getG(color);\n  let b = Skia.Color.Float.getB(color);\n  Skia.Color.Float.makeArgb(a *. opacity, r, g, b);\n};\n\nlet mix = (~start, ~stop, ~amount) => {\n  let startA = Skia.Color.Float.getA(start);\n  let startR = Skia.Color.Float.getR(start);\n  let startG = Skia.Color.Float.getG(start);\n  let startB = Skia.Color.Float.getB(start);\n\n  let stopA = Skia.Color.Float.getA(stop);\n  let stopR = Skia.Color.Float.getR(stop);\n  let stopG = Skia.Color.Float.getG(stop);\n  let stopB = Skia.Color.Float.getB(stop);\n  let r = (stopR -. startR) *. amount +. startR;\n  let g = (stopG -. startG) *. amount +. startG;\n  let b = (stopB -. startB) *. amount +. startB;\n  let a = (stopA -. startA) *. amount +. startA;\n  Skia.Color.Float.makeArgb(a, r, g, b);\n};\n\nlet opposite = color => {\n  let a = Skia.Color.Float.getA(color);\n  let r = Skia.Color.Float.getR(color);\n  let g = Skia.Color.Float.getG(color);\n  let b = Skia.Color.Float.getB(color);\n  Skia.Color.Float.makeArgb(a, 1. -. r, 1. -. g, 1. -. b);\n};\n\nlet toRgba = (color: t) => {\n  let a = Skia.Color.Float.getA(color);\n  let r = Skia.Color.Float.getR(color);\n  let g = Skia.Color.Float.getG(color);\n  let b = Skia.Color.Float.getB(color);\n  (r, g, b, a);\n};\n\nlet equals = (a: t, b: t) => {\n  a == b;\n};\n\nlet toString = (color: t) => {\n  let a = Skia.Color.Float.getA(color);\n  let r = Skia.Color.Float.getR(color);\n  let g = Skia.Color.Float.getG(color);\n  let b = Skia.Color.Float.getB(color);\n  Printf.sprintf(\"(r: %f g: %f b: %f a: %f)\", r, g, b, a);\n};\n\nlet toSkia = (color: t) => {\n  color;\n};\n"
  },
  {
    "path": "src/Core/Color.rei",
    "content": "exception ColorHexParseException(string);\n\ntype t;\n\nlet rgba: (float, float, float, float) => t;\nlet rgb: (float, float, float) => t;\n\nlet rgba_int: (int, int, int, int) => t;\nlet rgb_int: (int, int, int) => t;\n\nlet hex: string => t;\n\nlet multiplyAlpha: (float, t) => t;\nlet mix: (~start: t, ~stop: t, ~amount: float) => t;\nlet opposite: t => t;\n\nlet toRgba: t => (float, float, float, float);\nlet getAlpha: t => float;\n\nlet equals: (t, t) => bool;\n\nlet toString: t => string;\n\nlet toSkia: t => Skia.Color.t;\n"
  },
  {
    "path": "src/Core/Colors.re",
    "content": "open Color;\n\nlet transparentWhite = rgba(1.0, 1.0, 1.0, 0.0);\nlet transparentBlack = rgba(0., 0., 0., 0.);\n\nlet aliceBlue = rgb(0.94, 0.97, 1.0);\nlet antiqueWhite = rgb(0.98, 0.92, 0.84);\nlet aqua = rgb(0.0, 1.0, 1.0);\nlet aquamarine = rgb(0.5, 1.0, 0.83);\nlet azure = rgb(0.94, 1.0, 1.0);\nlet beige = rgb(0.96, 0.96, 0.86);\nlet bisque = rgb(1.0, 0.89, 0.77);\nlet black = rgb(0.0, 0.0, 0.0);\nlet blanchedAlmond = rgb(1.0, 0.92, 0.8);\nlet blue = rgb(0.0, 0.0, 1.0);\nlet blueViolet = rgb(0.54, 0.17, 0.89);\nlet brown = rgb(0.65, 0.16, 0.16);\nlet burlyWood = rgb(0.87, 0.72, 0.53);\nlet cadetBlue = rgb(0.37, 0.62, 0.63);\nlet chartreuse = rgb(0.5, 1.0, 0.0);\nlet chocolate = rgb(0.82, 0.41, 0.12);\nlet coral = rgb(1.0, 0.5, 0.31);\nlet cornflowerBlue = rgb(0.39, 0.58, 0.93);\nlet cornsilk = rgb(1.0, 0.97, 0.86);\nlet crimson = rgb(0.86, 0.08, 0.24);\nlet cyan = rgb(0.0, 1.0, 1.0);\nlet darkBlue = rgb(0.0, 0.0, 0.55);\nlet darkCyan = rgb(0.0, 0.55, 0.55);\nlet darkGoldenRod = rgb(0.72, 0.53, 0.04);\nlet darkGray = rgb(0.66, 0.66, 0.66);\nlet darkGrey = rgb(0.66, 0.66, 0.66);\nlet darkGreen = rgb(0.0, 0.39, 0.0);\nlet darkKhaki = rgb(0.74, 0.72, 0.42);\nlet darkMagenta = rgb(0.55, 0.0, 0.55);\nlet darkOliveGreen = rgb(0.33, 0.42, 0.18);\nlet darkOrange = rgb(1.0, 0.55, 0.0);\nlet darkOrchid = rgb(0.6, 0.2, 0.8);\nlet darkRed = rgb(0.55, 0.0, 0.0);\nlet darkSalmon = rgb(0.91, 0.59, 0.48);\nlet darkSeaGreen = rgb(0.56, 0.74, 0.56);\nlet darkSlateBlue = rgb(0.28, 0.24, 0.55);\nlet darkSlateGray = rgb(0.18, 0.31, 0.31);\nlet darkSlateGrey = rgb(0.18, 0.31, 0.31);\nlet darkTurquoise = rgb(0.0, 0.81, 0.82);\nlet darkViolet = rgb(0.58, 0.0, 0.83);\nlet deepPink = rgb(1.0, 0.08, 0.58);\nlet deepSkyBlue = rgb(0.0, 0.75, 1.0);\nlet dimGray = rgb(0.41, 0.41, 0.41);\nlet dimGrey = rgb(0.41, 0.41, 0.41);\nlet dodgerBlue = rgb(0.12, 0.56, 1.0);\nlet fireBrick = rgb(0.7, 0.13, 0.13);\nlet floralWhite = rgb(1.0, 0.98, 0.94);\nlet forestGreen = rgb(0.13, 0.55, 0.13);\nlet fuchsia = rgb(1.0, 0.0, 1.0);\nlet gainsboro = rgb(0.86, 0.86, 0.86);\nlet ghostWhite = rgb(0.97, 0.97, 1.0);\nlet gold = rgb(1.0, 0.84, 0.0);\nlet goldenRod = rgb(0.85, 0.65, 0.13);\nlet gray = rgb(0.5, 0.5, 0.5);\nlet grey = rgb(0.5, 0.5, 0.5);\nlet green = rgb(0.0, 0.5, 0.0);\nlet greenYellow = rgb(0.68, 1.0, 0.18);\nlet honeyDew = rgb(0.94, 1.0, 0.94);\nlet hotPink = rgb(1.0, 0.41, 0.71);\nlet indianRed = rgb(0.8, 0.36, 0.36);\nlet indigo = rgb(0.29, 0.0, 0.51);\nlet ivory = rgb(1.0, 1.0, 0.94);\nlet khaki = rgb(0.94, 0.9, 0.55);\nlet lavender = rgb(0.9, 0.9, 0.98);\nlet lavenderBlush = rgb(1.0, 0.94, 0.96);\nlet lawnGreen = rgb(0.49, 0.99, 0.0);\nlet lemonChiffon = rgb(1.0, 0.98, 0.8);\nlet lightBlue = rgb(0.68, 0.85, 0.9);\nlet lightCoral = rgb(0.94, 0.5, 0.5);\nlet lightCyan = rgb(0.88, 1.0, 1.0);\nlet lightGoldenRodYellow = rgb(0.98, 0.98, 0.82);\nlet lightGray = rgb(0.83, 0.83, 0.83);\nlet lightGrey = rgb(0.83, 0.83, 0.83);\nlet lightGreen = rgb(0.56, 0.93, 0.56);\nlet lightPink = rgb(1.0, 0.71, 0.76);\nlet lightSalmon = rgb(1.0, 0.63, 0.48);\nlet lightSeaGreen = rgb(0.13, 0.7, 0.67);\nlet lightSkyBlue = rgb(0.53, 0.81, 0.98);\nlet lightSlateGray = rgb(0.47, 0.53, 0.6);\nlet lightSlateGrey = rgb(0.47, 0.53, 0.6);\nlet lightSteelBlue = rgb(0.69, 0.77, 0.87);\nlet lightYellow = rgb(1.0, 1.0, 0.88);\nlet lime = rgb(0.0, 1.0, 0.0);\nlet limeGreen = rgb(0.2, 0.8, 0.2);\nlet linen = rgb(0.98, 0.94, 0.9);\nlet magenta = rgb(1.0, 0.0, 1.0);\nlet maroon = rgb(0.5, 0.0, 0.0);\nlet mediumAquaMarine = rgb(0.4, 0.8, 0.67);\nlet mediumBlue = rgb(0.0, 0.0, 0.8);\nlet mediumOrchid = rgb(0.73, 0.33, 0.83);\nlet mediumPurple = rgb(0.58, 0.44, 0.86);\nlet mediumSeaGreen = rgb(0.24, 0.7, 0.44);\nlet mediumSlateBlue = rgb(0.48, 0.41, 0.93);\nlet mediumSpringGreen = rgb(0.0, 0.98, 0.6);\nlet mediumTurquoise = rgb(0.28, 0.82, 0.8);\nlet mediumVioletRed = rgb(0.78, 0.08, 0.52);\nlet midnightBlue = rgb(0.1, 0.1, 0.44);\nlet mintCream = rgb(0.96, 1.0, 0.98);\nlet mistyRose = rgb(1.0, 0.89, 0.88);\nlet moccasin = rgb(1.0, 0.89, 0.71);\nlet navajoWhite = rgb(1.0, 0.87, 0.68);\nlet navy = rgb(0.0, 0.0, 0.5);\nlet oldLace = rgb(0.99, 0.96, 0.9);\nlet olive = rgb(0.5, 0.5, 0.0);\nlet oliveDrab = rgb(0.42, 0.56, 0.14);\nlet orange = rgb(1.0, 0.65, 0.0);\nlet orangeRed = rgb(1.0, 0.27, 0.0);\nlet orchid = rgb(0.85, 0.44, 0.84);\nlet paleGoldenRod = rgb(0.93, 0.91, 0.67);\nlet paleGreen = rgb(0.6, 0.98, 0.6);\nlet paleTurquoise = rgb(0.69, 0.93, 0.93);\nlet paleVioletRed = rgb(0.86, 0.44, 0.58);\nlet papayaWhip = rgb(1.0, 0.94, 0.84);\nlet peachPuff = rgb(1.0, 0.85, 0.73);\nlet peru = rgb(0.8, 0.52, 0.25);\nlet pink = rgb(1.0, 0.75, 0.8);\nlet plum = rgb(0.87, 0.63, 0.87);\nlet powderBlue = rgb(0.69, 0.88, 0.9);\nlet purple = rgb(0.5, 0.0, 0.5);\nlet rebeccaPurple = rgb(0.4, 0.2, 0.6);\nlet red = rgb(1.0, 0.0, 0.0);\nlet rosyBrown = rgb(0.74, 0.56, 0.56);\nlet royalBlue = rgb(0.25, 0.41, 0.88);\nlet saddleBrown = rgb(0.55, 0.27, 0.07);\nlet salmon = rgb(0.98, 0.5, 0.45);\nlet sandyBrown = rgb(0.96, 0.64, 0.38);\nlet seaGreen = rgb(0.18, 0.55, 0.34);\nlet seaShell = rgb(1.0, 0.96, 0.93);\nlet sienna = rgb(0.63, 0.32, 0.18);\nlet silver = rgb(0.75, 0.75, 0.75);\nlet skyBlue = rgb(0.53, 0.81, 0.92);\nlet slateBlue = rgb(0.42, 0.35, 0.8);\nlet slateGray = rgb(0.44, 0.5, 0.56);\nlet slateGrey = rgb(0.44, 0.5, 0.56);\nlet snow = rgb(1.0, 0.98, 0.98);\nlet springGreen = rgb(0.0, 1.0, 0.5);\nlet steelBlue = rgb(0.27, 0.51, 0.71);\nlet tan = rgb(0.82, 0.71, 0.55);\nlet teal = rgb(0.0, 0.5, 0.5);\nlet thistle = rgb(0.85, 0.75, 0.85);\nlet tomato = rgb(1.0, 0.39, 0.28);\nlet turquoise = rgb(0.25, 0.88, 0.82);\nlet violet = rgb(0.93, 0.51, 0.93);\nlet wheat = rgb(0.96, 0.87, 0.7);\nlet white = rgb(1.0, 1.0, 1.0);\nlet whiteSmoke = rgb(0.96, 0.96, 0.96);\nlet yellow = rgb(1.0, 1.0, 0.0);\nlet yellowGreen = rgb(0.6, 0.8, 0.2);\n\nlet lookup = Hashtbl.create(150);\nHashtbl.add(lookup, \"aliceblue\", aliceBlue);\nHashtbl.add(lookup, \"antiquewhite\", antiqueWhite);\nHashtbl.add(lookup, \"aqua\", aqua);\nHashtbl.add(lookup, \"aquamarine\", aquamarine);\nHashtbl.add(lookup, \"azure\", azure);\nHashtbl.add(lookup, \"beige\", beige);\nHashtbl.add(lookup, \"bisque\", bisque);\nHashtbl.add(lookup, \"black\", black);\nHashtbl.add(lookup, \"blanchedalmond\", blanchedAlmond);\nHashtbl.add(lookup, \"blue\", blue);\nHashtbl.add(lookup, \"blueviolet\", blueViolet);\nHashtbl.add(lookup, \"brown\", brown);\nHashtbl.add(lookup, \"burlywood\", burlyWood);\nHashtbl.add(lookup, \"cadetblue\", cadetBlue);\nHashtbl.add(lookup, \"chartreuse\", chartreuse);\nHashtbl.add(lookup, \"chocolate\", chocolate);\nHashtbl.add(lookup, \"coral\", coral);\nHashtbl.add(lookup, \"cornflowerblue\", cornflowerBlue);\nHashtbl.add(lookup, \"cornsilk\", cornsilk);\nHashtbl.add(lookup, \"crimson\", crimson);\nHashtbl.add(lookup, \"cyan\", cyan);\nHashtbl.add(lookup, \"darkblue\", darkBlue);\nHashtbl.add(lookup, \"darkcyan\", darkCyan);\nHashtbl.add(lookup, \"darkgoldenrod\", darkGoldenRod);\nHashtbl.add(lookup, \"darkgray\", darkGray);\nHashtbl.add(lookup, \"darkgrey\", darkGrey);\nHashtbl.add(lookup, \"darkgreen\", darkGreen);\nHashtbl.add(lookup, \"darkkhaki\", darkKhaki);\nHashtbl.add(lookup, \"darkmagenta\", darkMagenta);\nHashtbl.add(lookup, \"darkolivegreen\", darkOliveGreen);\nHashtbl.add(lookup, \"darkorange\", darkOrange);\nHashtbl.add(lookup, \"darkorchid\", darkOrchid);\nHashtbl.add(lookup, \"darkred\", darkRed);\nHashtbl.add(lookup, \"darksalmon\", darkSalmon);\nHashtbl.add(lookup, \"darkseagreen\", darkSeaGreen);\nHashtbl.add(lookup, \"darkslateblue\", darkSlateBlue);\nHashtbl.add(lookup, \"darkslategray\", darkSlateGray);\nHashtbl.add(lookup, \"darkslategrey\", darkSlateGrey);\nHashtbl.add(lookup, \"darkturquoise\", darkTurquoise);\nHashtbl.add(lookup, \"darkviolet\", darkViolet);\nHashtbl.add(lookup, \"deeppink\", deepPink);\nHashtbl.add(lookup, \"deepskyblue\", deepSkyBlue);\nHashtbl.add(lookup, \"dimgray\", dimGray);\nHashtbl.add(lookup, \"dimgrey\", dimGrey);\nHashtbl.add(lookup, \"dodgerblue\", dodgerBlue);\nHashtbl.add(lookup, \"firebrick\", fireBrick);\nHashtbl.add(lookup, \"floralwhite\", floralWhite);\nHashtbl.add(lookup, \"forestgreen\", forestGreen);\nHashtbl.add(lookup, \"fuchsia\", fuchsia);\nHashtbl.add(lookup, \"gainsboro\", gainsboro);\nHashtbl.add(lookup, \"ghostwhite\", ghostWhite);\nHashtbl.add(lookup, \"gold\", gold);\nHashtbl.add(lookup, \"goldenrod\", goldenRod);\nHashtbl.add(lookup, \"gray\", gray);\nHashtbl.add(lookup, \"grey\", grey);\nHashtbl.add(lookup, \"green\", green);\nHashtbl.add(lookup, \"greenyellow\", greenYellow);\nHashtbl.add(lookup, \"honeydew\", honeyDew);\nHashtbl.add(lookup, \"hotpink\", hotPink);\nHashtbl.add(lookup, \"indianred\", indianRed);\nHashtbl.add(lookup, \"indigo\", indigo);\nHashtbl.add(lookup, \"ivory\", ivory);\nHashtbl.add(lookup, \"khaki\", khaki);\nHashtbl.add(lookup, \"lavender\", lavender);\nHashtbl.add(lookup, \"lavenderblush\", lavenderBlush);\nHashtbl.add(lookup, \"lawngreen\", lawnGreen);\nHashtbl.add(lookup, \"lemonchiffon\", lemonChiffon);\nHashtbl.add(lookup, \"lightblue\", lightBlue);\nHashtbl.add(lookup, \"lightcoral\", lightCoral);\nHashtbl.add(lookup, \"lightcyan\", lightCyan);\nHashtbl.add(lookup, \"lightgoldenrodyellow\", lightGoldenRodYellow);\nHashtbl.add(lookup, \"lightgray\", lightGray);\nHashtbl.add(lookup, \"lightgrey\", lightGrey);\nHashtbl.add(lookup, \"lightgreen\", lightGreen);\nHashtbl.add(lookup, \"lightpink\", lightPink);\nHashtbl.add(lookup, \"lightsalmon\", lightSalmon);\nHashtbl.add(lookup, \"lightseagreen\", lightSeaGreen);\nHashtbl.add(lookup, \"lightskyblue\", lightSkyBlue);\nHashtbl.add(lookup, \"lightslategray\", lightSlateGray);\nHashtbl.add(lookup, \"lightslategrey\", lightSlateGrey);\nHashtbl.add(lookup, \"lightsteelblue\", lightSteelBlue);\nHashtbl.add(lookup, \"lightyellow\", lightYellow);\nHashtbl.add(lookup, \"lime\", lime);\nHashtbl.add(lookup, \"limegreen\", limeGreen);\nHashtbl.add(lookup, \"linen\", linen);\nHashtbl.add(lookup, \"magenta\", magenta);\nHashtbl.add(lookup, \"maroon\", maroon);\nHashtbl.add(lookup, \"mediumaquamarine\", mediumAquaMarine);\nHashtbl.add(lookup, \"mediumblue\", mediumBlue);\nHashtbl.add(lookup, \"mediumorchid\", mediumOrchid);\nHashtbl.add(lookup, \"mediumpurple\", mediumPurple);\nHashtbl.add(lookup, \"mediumseagreen\", mediumSeaGreen);\nHashtbl.add(lookup, \"mediumslateblue\", mediumSlateBlue);\nHashtbl.add(lookup, \"mediumspringgreen\", mediumSpringGreen);\nHashtbl.add(lookup, \"mediumturquoise\", mediumTurquoise);\nHashtbl.add(lookup, \"mediumvioletred\", mediumVioletRed);\nHashtbl.add(lookup, \"midnightblue\", midnightBlue);\nHashtbl.add(lookup, \"mintcream\", mintCream);\nHashtbl.add(lookup, \"mistyrose\", mistyRose);\nHashtbl.add(lookup, \"moccasin\", moccasin);\nHashtbl.add(lookup, \"navajowhite\", navajoWhite);\nHashtbl.add(lookup, \"navy\", navy);\nHashtbl.add(lookup, \"oldlace\", oldLace);\nHashtbl.add(lookup, \"olive\", olive);\nHashtbl.add(lookup, \"olivedrab\", oliveDrab);\nHashtbl.add(lookup, \"orange\", orange);\nHashtbl.add(lookup, \"orangered\", orangeRed);\nHashtbl.add(lookup, \"orchid\", orchid);\nHashtbl.add(lookup, \"palegoldenrod\", paleGoldenRod);\nHashtbl.add(lookup, \"palegreen\", paleGreen);\nHashtbl.add(lookup, \"paleturquoise\", paleTurquoise);\nHashtbl.add(lookup, \"palevioletred\", paleVioletRed);\nHashtbl.add(lookup, \"papayawhip\", papayaWhip);\nHashtbl.add(lookup, \"peachpuff\", peachPuff);\nHashtbl.add(lookup, \"peru\", peru);\nHashtbl.add(lookup, \"pink\", pink);\nHashtbl.add(lookup, \"plum\", plum);\nHashtbl.add(lookup, \"powderblue\", powderBlue);\nHashtbl.add(lookup, \"purple\", purple);\nHashtbl.add(lookup, \"rebeccapurple\", rebeccaPurple);\nHashtbl.add(lookup, \"red\", red);\nHashtbl.add(lookup, \"rosybrown\", rosyBrown);\nHashtbl.add(lookup, \"royalblue\", royalBlue);\nHashtbl.add(lookup, \"saddlebrown\", saddleBrown);\nHashtbl.add(lookup, \"salmon\", salmon);\nHashtbl.add(lookup, \"sandybrown\", sandyBrown);\nHashtbl.add(lookup, \"seagreen\", seaGreen);\nHashtbl.add(lookup, \"seashell\", seaShell);\nHashtbl.add(lookup, \"sienna\", sienna);\nHashtbl.add(lookup, \"silver\", silver);\nHashtbl.add(lookup, \"skyblue\", skyBlue);\nHashtbl.add(lookup, \"slateblue\", slateBlue);\nHashtbl.add(lookup, \"slategray\", slateGray);\nHashtbl.add(lookup, \"slategrey\", slateGrey);\nHashtbl.add(lookup, \"snow\", snow);\nHashtbl.add(lookup, \"springgreen\", springGreen);\nHashtbl.add(lookup, \"steelblue\", steelBlue);\nHashtbl.add(lookup, \"tan\", tan);\nHashtbl.add(lookup, \"teal\", teal);\nHashtbl.add(lookup, \"thistle\", thistle);\nHashtbl.add(lookup, \"tomato\", tomato);\nHashtbl.add(lookup, \"turquoise\", turquoise);\nHashtbl.add(lookup, \"violet\", violet);\nHashtbl.add(lookup, \"wheat\", wheat);\nHashtbl.add(lookup, \"white\", white);\nHashtbl.add(lookup, \"whitesmoke\", whiteSmoke);\nHashtbl.add(lookup, \"yellow\", yellow);\nHashtbl.add(lookup, \"yellowgreen\", yellowGreen);\n\nlet fromString = name =>\n  Hashtbl.find_opt(lookup, String.lowercase_ascii(name));\nlet fromStringExn = name =>\n  try(Hashtbl.find(lookup, String.lowercase_ascii(name))) {\n  | Not_found => failwith(\"Unknown color: \" ++ name)\n  };\n"
  },
  {
    "path": "src/Core/Environment.re",
    "content": "let isNative =\n  switch (Sys.backend_type) {\n  | Native => true\n  | Bytecode => true\n  | _ => false\n  };\n\nlet webGL = !isNative;\n\nlet sleep = (t: Time.t) =>\n  /* No-op in JS */\n  if (isNative) {\n    Unix.sleepf(Time.toFloatSeconds(t));\n  };\n\nexternal yield: unit => unit = \"caml_thread_yield\";\n\ntype os =\n  Revery_Native.Environment.os =\n    | Unknown\n    | Android\n    | IOS\n    | Browser\n    | Mac({\n        major: int,\n        minor: int,\n        bugfix: int,\n      })\n    | Linux({\n        kernel: int,\n        major: int,\n        minor: int,\n        patch: int,\n      })\n    | Windows({\n        major: int,\n        minor: int,\n        build: int,\n      });\n\nlet os = {\n  webGL ? Browser : Revery_Native.Environment.getOS();\n};\n\nlet osString =\n  switch (os) {\n  | Mac({major, minor, bugfix}) =>\n    Printf.sprintf(\"macOS %d.%d.%d\", major, minor, bugfix)\n  | Linux({kernel, major, minor, patch}) =>\n    Printf.sprintf(\"Linux %d.%d.%d-%d\", kernel, major, minor, patch)\n  | Windows({major, minor, build}) =>\n    Printf.sprintf(\"Windows %d.%d Build %d\", major, minor, build)\n  | Android => \"Android\"\n  | Browser => \"Browser\"\n  | IOS => \"iOS\"\n  | Unknown => \"Unknown\"\n  };\n\nlet isMac =\n  switch (os) {\n  | Mac(_) => true\n  | _ => false\n  };\nlet isLinux =\n  switch (os) {\n  | Linux(_) => true\n  | _ => false\n  };\nlet isWindows =\n  switch (os) {\n  | Windows(_) => true\n  | _ => false\n  };\nlet isIOS = {\n  os == IOS;\n};\nlet isAndroid = {\n  os == Android;\n};\nlet isBrowser = {\n  os == Browser;\n};\n\nmodule Internal = {\n  let addTrailingSlash = dir => {\n    let len = String.length(dir);\n    if (len == 0) {\n      dir;\n    } else {\n      switch (dir.[len - 1]) {\n      | '/' => dir\n      | '\\\\' => dir\n      | _ => dir ++ Filename.dir_sep\n      };\n    };\n  };\n};\n\nlet getExecutingDirectory = () =>\n  if (!isNative) {\n    \"\";\n  } else {\n    let dir =\n      switch (os) {\n      /* The default strategy of preferring Sys.executable_name seems to only work\n       * reliably on Mac.  On Linux, it will return the symlink source instead of\n       * the symlink destination - this causes problems when trying to load assets\n       * relative to the binary location when symlinked.\n       */\n      | Mac(_) =>\n        switch (\n          String.rindex_opt(Sys.executable_name, '/'),\n          String.rindex_opt(Sys.executable_name, '\\\\'),\n        ) {\n        | (Some(v1), Some(v2)) =>\n          String.sub(Sys.executable_name, 0, max(v1, v2))\n        | (None, Some(v)) => String.sub(Sys.executable_name, 0, v)\n        | (Some(v), None) => String.sub(Sys.executable_name, 0, v)\n        | _ => Sys.executable_name\n        }\n      | _ =>\n        let candidatePath = Filename.dirname(Sys.argv[0]) ++ Filename.dir_sep;\n\n        if (Filename.is_relative(candidatePath)) {\n          Internal.addTrailingSlash(Sys.getcwd()) ++ candidatePath;\n        } else {\n          candidatePath;\n        };\n      };\n\n    /* Check if there is a trailing slash. If not, we need to add one. */\n    Internal.addTrailingSlash(dir);\n  };\n\nlet executingDirectory = getExecutingDirectory();\n\nlet getAssetPath = p => {\n  isNative\n    ? {\n      Rench.Path.isAbsolute(p) ? p : executingDirectory ++ p;\n    }\n    : p;\n};\n\nlet getWorkingDirectory = () => Sys.getcwd();\n\nlet getTempDirectory = () => Filename.get_temp_dir_name();\n\nlet getUserLocale = () => Revery_Native.Locale.getUser();\nlet userLocale = getUserLocale();\n"
  },
  {
    "path": "src/Core/Environment.rei",
    "content": "/**\n  [Environment] provides information about the run-time environment\n*/\n\n/** [true] if native / bytecode build, [false] if JSOO build */\nlet isNative: bool;\n\n/** [true] if JSOO build, [false] otherwise */\nlet webGL: bool;\n\n/**\n  [sleep(t)] sleeps the running thread for the specified time period.\n\n  {b Example usage}: [sleep(Seconds(1.0))]\n\n  {i No-op in JSOO builds.}\n*/\nlet sleep: Time.t => unit;\n\nlet yield: unit => unit;\n\n/**\n[@deprecated]\n*/\nlet getExecutingDirectory: unit => string;\n\n/**\n[executingDirectory] gets the directory where the running executable lives.\n*/\nlet executingDirectory: string;\n\nlet getWorkingDirectory: unit => string;\n\n/**\n[getAssetPath] resolves a path to an absolute path. If the path is not already\nabsolute, it is assumed to be relative to the current binary.\n*/\nlet getAssetPath: string => string;\n\n/**\n  [getTempDirectory]\n  Unix:\n  returns the value of the TMPDIR environment variable or \"/tmp\" if not set.\n  Windows:\n  returns the value of the TMPDIR environment variable or \".\" if not set.\n*/\nlet getTempDirectory: unit => string;\n\ntype os =\n  | Unknown\n  | Android\n  | IOS\n  | Browser\n  | Mac({\n      major: int,\n      minor: int,\n      bugfix: int,\n    })\n  | Linux({\n      kernel: int,\n      major: int,\n      minor: int,\n      patch: int,\n    })\n  | Windows({\n      major: int,\n      minor: int,\n      build: int,\n    });\n\nlet os: os;\nlet osString: string;\n\nlet isMac: bool;\nlet isIOS: bool;\nlet isWindows: bool;\nlet isAndroid: bool;\nlet isLinux: bool;\nlet isBrowser: bool;\n\n/**\n[getUserLocale] returns the current user locale. Note that on some platforms\n(including macOS) the locale can change during runtime\n*/\nlet getUserLocale: unit => string;\nlet userLocale: string;\n"
  },
  {
    "path": "src/Core/Event.re",
    "content": "/* Event.re */\n\nmodule Fanout = {\n  /* An event where subscribers can respond with a value */\n  type cb('a, 'b) = 'a => 'b;\n\n  type t('a, 'b) = ref(list(cb('a, 'b)));\n\n  let dispatch = (evt: t('a, 'b), merge: ('b, 'b) => 'b, zero: 'b, v: 'a) => {\n    List.fold_left((acc, listener) => merge(acc, listener(v)), zero, evt^);\n  };\n\n  let subscribe = (evt: t('a, 'b), f: cb('a, 'b)) => {\n    evt := List.append(evt^, [f]);\n    let unsubscribe = () => {\n      evt := List.filter(f => f !== f, evt^);\n    };\n    unsubscribe;\n  };\n};\n\n/* an event is just a fanout with a `unit` return type, but we duplicate the definition for simpler type errors */\n\ntype cb('a) = 'a => unit;\n\ntype t('a) = ref(list(cb('a)));\n\nlet create = () => ref([]);\n\nlet subscribe = (evt: t('a), f: cb('a)) => {\n  Fanout.subscribe(evt, f);\n};\n\nlet dispatch = (evt: t('a), v: 'a) => List.iter(c => c(v), evt^);\n"
  },
  {
    "path": "src/Core/Events.re",
    "content": "type keyPressEvent = {\n  codepoint: int,\n  character: string,\n};\n\ntype textInputEvent = {text: string};\n\ntype textEditEvent = {\n  text: string,\n  start: int,\n  length: int,\n};\n\ntype internalKeyboardEvent =\n  | InternalKeyUpEvent(Key.KeyEvent.t)\n  | InternalKeyDownEvent(Key.KeyEvent.t)\n  | InternalTextEditEvent(textEditEvent)\n  | InternalTextInputEvent(textInputEvent);\n\ntype mouseMoveEvent = {\n  mouseX: float,\n  mouseY: float,\n  keymod: Key.Keymod.t,\n};\n\ntype mouseWheelEvent = {\n  deltaX: float,\n  deltaY: float,\n  mouseX: float,\n  mouseY: float,\n  keymod: Key.Keymod.t,\n};\n\ntype mouseButtonEvent = {\n  button: MouseButton.t,\n  keymod: Key.Keymod.t,\n};\n\ntype internalMouseEvents =\n  | InternalMouseDown(mouseButtonEvent)\n  | InternalMouseMove(mouseMoveEvent)\n  | InternalMouseUp(mouseButtonEvent)\n  | InternalMouseWheel(mouseWheelEvent)\n  | InternalMouseEnter(mouseMoveEvent)\n  | InternalMouseLeave(mouseMoveEvent)\n  | InternalMouseOver(mouseMoveEvent)\n  | InternalMouseOut(mouseMoveEvent);\n\ntype fileDropEvent = {\n  mouseX: float,\n  mouseY: float,\n  paths: list(string),\n  keymod: Key.Keymod.t,\n};\n\ntype internalFileDropEvents =\n  | InternalFileDropped(fileDropEvent);\n"
  },
  {
    "path": "src/Core/GarbageCollector.re",
    "content": "/*\n * GarbageCollector.re\n *\n * Thin wrapper over the 'Gc' module to support some\n * basic primitives for tuning performance in the\n * Revery app model.\n */\n\nlet full = () =>\n  if (Environment.isNative) {\n    Gc.full_major();\n  };\n\nlet counters = () =>\n  if (Environment.isNative) {\n    Gc.counters();\n  } else {\n    (0., 0., 0.);\n  };\n"
  },
  {
    "path": "src/Core/Key.re",
    "content": "module Scancode = {\n  type t = Sdl2.Scancode.t;\n\n  let getName = Sdl2.Scancode.getName;\n  let toString = getName;\n};\n\nmodule Keycode = {\n  include Sdl2.Keycode;\n\n  let toString = getName;\n};\n\nmodule Keymod = {\n  include Sdl2.Keymod;\n};\n\nmodule KeyEvent = {\n  type t = {\n    keycode: Keycode.t,\n    scancode: Scancode.t,\n    keymod: Keymod.t,\n    repeat: bool,\n  };\n};\n"
  },
  {
    "path": "src/Core/Key.rei",
    "content": "module Scancode: {\n  type t = int;\n\n  let getName: t => string;\n  let toString: t => string;\n};\n\nmodule Keycode: {\n  type t = int;\n\n  let getName: t => string;\n\n  let ofScancode: Scancode.t => t;\n  let toString: t => string;\n  let toScancode: t => Scancode.t;\n\n  // Incrementally add these as needed from:\n  // https://wiki.libsdl.org/SDLKeycodeLookup\n  let unknown: int;\n  let backspace: int;\n\n  let return: int;\n  let escape: int;\n\n  let space: int;\n\n  let left_paren: int;\n  let right_paren: int;\n\n  let asterisk: int;\n  let plus: int;\n  let minus: int;\n  let period: int;\n  let slash: int;\n  let caret: int;\n\n  let equals: int;\n\n  let digit0: int;\n  let digit1: int;\n  let digit2: int;\n  let digit3: int;\n  let digit4: int;\n  let digit5: int;\n  let digit6: int;\n  let digit7: int;\n  let digit8: int;\n  let digit9: int;\n\n  let pad_multiply: int;\n  let pad_plus: int;\n  let pad_minus: int;\n  let pad_period: int;\n  let pad_divide: int;\n\n  let pad_equals: int;\n\n  let p_digit0: int;\n  let p_digit1: int;\n  let p_digit2: int;\n  let p_digit3: int;\n  let p_digit4: int;\n  let p_digit5: int;\n  let p_digit6: int;\n  let p_digit7: int;\n  let p_digit8: int;\n  let p_digit9: int;\n\n  let a: int;\n  let b: int;\n  let c: int;\n  let d: int;\n  let e: int;\n  let f: int;\n  let g: int;\n  let h: int;\n  let i: int;\n  let j: int;\n  let k: int;\n  let l: int;\n  let m: int;\n  let n: int;\n  let o: int;\n  let p: int;\n  let q: int;\n  let r: int;\n  let s: int;\n  let t: int;\n  let u: int;\n  let v: int;\n  let w: int;\n  let x: int;\n  let y: int;\n  let z: int;\n\n  let delete: int;\n\n  let right: int;\n  let left: int;\n};\n\nmodule Keymod: {\n  type t = int;\n\n  let isLeftShiftDown: t => bool;\n  let isRightShiftDown: t => bool;\n\n  let isShiftDown: t => bool;\n\n  let isLeftControlDown: t => bool;\n  let isRightControlDown: t => bool;\n\n  let isControlDown: t => bool;\n\n  let isLeftAltDown: t => bool;\n  let isRightAltDown: t => bool;\n\n  let isAltDown: t => bool;\n\n  let isLeftGuiDown: t => bool;\n  let isRightGuiDown: t => bool;\n\n  let isGuiDown: t => bool;\n\n  let isNumLockDown: t => bool;\n  let isCapsLockDown: t => bool;\n  let isAltGrKeyDown: t => bool;\n};\n\nmodule KeyEvent: {\n  type t = {\n    keycode: Keycode.t,\n    scancode: Scancode.t,\n    keymod: Keymod.t,\n    repeat: bool,\n  };\n};\n"
  },
  {
    "path": "src/Core/Log.re",
    "content": "include Timber.Log;\n\nmodule type Logger = Timber.Logger;\n"
  },
  {
    "path": "src/Core/Log.rei",
    "content": "module type Logger = {\n  let errorf: Timber.msgf(_, unit) => unit;\n  let error: string => unit;\n  let warnf: Timber.msgf(_, unit) => unit;\n  let warn: string => unit;\n  let infof: Timber.msgf(_, unit) => unit;\n  let info: string => unit;\n  let debugf: Timber.msgf(_, unit) => unit;\n  let debug: string => unit;\n  let tracef: Timber.msgf(_, unit) => unit;\n  let trace: string => unit;\n  let fn: (string, 'a => 'b, ~pp: 'b => string=?, 'a) => 'b;\n};\n\nlet withNamespace: string => (module Logger);\n"
  },
  {
    "path": "src/Core/MouseButton.re",
    "content": "[@deriving show({with_path: false})]\ntype t =\n  | BUTTON_LEFT\n  | BUTTON_MIDDLE\n  | BUTTON_RIGHT\n  | BUTTON_UNKNOWN;\n\nlet convert = sdlMouseButton =>\n  switch (sdlMouseButton) {\n  | Sdl2.MouseButton.Left => BUTTON_LEFT\n  | Sdl2.MouseButton.Middle => BUTTON_MIDDLE\n  | Sdl2.MouseButton.Right => BUTTON_RIGHT\n  | _ => BUTTON_UNKNOWN\n  };\n"
  },
  {
    "path": "src/Core/MouseButton.rei",
    "content": "[@deriving show]\ntype t =\n  | BUTTON_LEFT\n  | BUTTON_MIDDLE\n  | BUTTON_RIGHT\n  | BUTTON_UNKNOWN;\n\nlet convert: Sdl2.MouseButton.t => t;\n"
  },
  {
    "path": "src/Core/MouseCursors.re",
    "content": "open Sdl2;\n\n/* This module must use lazy values for the cursors, because GLFW will return\n   [GLFW_NOT_INITIALIZED] (which renders as an arrow) if this gets called before\n   [glfwInit]. Since these are global variables, they WILL be initialized first.\n   We could also return a new cursor every time, but this would cause a memory\n   leak. */\ntype t = [ | `Arrow | `Text | `Pointer | `Crosshair | `HResize | `VResize];\n\nlet arrow_lazy = lazy(Cursor.createSystem(Cursor.Arrow));\nlet text_lazy = lazy(Cursor.createSystem(Cursor.IBeam));\nlet pointer_lazy = lazy(Cursor.createSystem(Cursor.Hand));\nlet crosshair_lazy = lazy(Cursor.createSystem(Cursor.Crosshair));\nlet horizontalResize_lazy = lazy(Cursor.createSystem(Cursor.SizeWE));\nlet verticalResize_lazy = lazy(Cursor.createSystem(Cursor.SizeNS));\n\nlet _toSdlCursor = cursorType => {\n  (\n    switch (cursorType) {\n    | `Arrow => arrow_lazy\n    | `Text => text_lazy\n    | `Pointer => pointer_lazy\n    | `Crosshair => crosshair_lazy\n    | `HResize => horizontalResize_lazy\n    | `VResize => verticalResize_lazy\n    }\n  )\n  |> Lazy.force;\n};\n\nlet setCursor = v => {\n  _toSdlCursor(v) |> Cursor.set;\n};\n\nlet arrow = `Arrow;\nlet text = `Text;\nlet pointer = `Pointer;\nlet crosshair = `Crosshair;\nlet horizontalResize = `HResize;\nlet verticalResize = `VResize;\n\nlet toString = (v: t) =>\n  switch (v) {\n  | `Arrow => \"arrow\"\n  | `Text => \"text\"\n  | `Pointer => \"pointer\"\n  | `Crosshair => \"crosshair\"\n  | `HResize => \"hresize\"\n  | `VResize => \"vresize\"\n  };\n"
  },
  {
    "path": "src/Core/MouseCursors.rei",
    "content": "type t;\n\nlet setCursor: t => unit;\n\nlet arrow: t;\nlet text: t;\nlet pointer: t;\nlet crosshair: t;\nlet horizontalResize: t;\nlet verticalResize: t;\n\nlet toString: t => string;\n"
  },
  {
    "path": "src/Core/Performance.re",
    "content": "type performanceFunction('a) = unit => 'a;\n\nlet nestingLevel = ref(0);\n\nmodule Log = (val Log.withNamespace(\"Revery.Core.Performance\"));\n\nmodule MemoryAllocations = {\n  type t = {\n    minorWords: int,\n    promotedWords: int,\n    majorWords: int,\n  };\n\n  let toString = ({minorWords, promotedWords, majorWords}: t) =>\n    Printf.sprintf(\n      \"| minor: %n | major: %n | promoted: %n |\",\n      minorWords,\n      majorWords,\n      promotedWords,\n    );\n};\nlet getMemoryAllocations = (startCounters, endCounters) => {\n  let (startMinor, startPromoted, startMajor) = startCounters;\n  let (endMinor, endPromoted, endMajor) = endCounters;\n\n  let ret: MemoryAllocations.t = {\n    minorWords: int_of_float(endMinor -. startMinor),\n    promotedWords: int_of_float(endPromoted -. startPromoted),\n    majorWords: int_of_float(endMajor -. startMajor),\n  };\n  ret;\n};\n\nlet isBenchmarking =\n  switch (Sys.getenv_opt(\"REVERY_PERF\")) {\n  | Some(_) => true\n  | None => false\n  };\n\nlet bench: (string, performanceFunction('a)) => 'a =\n  (name, f) =>\n    if (isBenchmarking) {\n      nestingLevel := nestingLevel^ + 1;\n      let startTime = Unix.gettimeofday();\n      let startCounters = GarbageCollector.counters();\n      Log.tracef(m =>\n        m(\"%s[BEGIN: %s]\", String.make(nestingLevel^, '-'), name)\n      );\n      let ret = f();\n      let endTime = Unix.gettimeofday();\n      let endCounters = GarbageCollector.counters();\n      let allocations = getMemoryAllocations(startCounters, endCounters);\n      Log.tracef(m =>\n        m(\n          \"%s[END: %s] Time: %fms Memory: %s\",\n          String.make(nestingLevel^, '-'),\n          name,\n          (endTime -. startTime) *. 1000.,\n          MemoryAllocations.toString(allocations),\n        )\n      );\n\n      nestingLevel := nestingLevel^ - 1;\n      ret;\n    } else {\n      f();\n    };\n"
  },
  {
    "path": "src/Core/Revery_Core.re",
    "content": "module Color = Color;\nmodule Colors = Colors;\nmodule Key = Key;\nmodule MouseButton = MouseButton;\nmodule MouseCursors = MouseCursors;\n\nmodule Window = Window;\nmodule App = App;\nmodule Time = Time;\n\nmodule Environment = Environment;\n\nmodule Event = Event;\nmodule Events = Events;\n\nmodule Log = Log;\n\nmodule Performance = Performance;\nmodule UniqueId = UniqueId;\n\nmodule TextWrapping = TextWrapping;\nmodule TextOverflow = TextOverflow;\n\nmodule Vsync = Vsync;\nmodule WindowCreateOptions = WindowCreateOptions;\nmodule WindowStyles = WindowStyles;\n\n/*\n * Internally exposed modules, just for testing.\n */\nmodule Internal = {\n  module Tick = Tick;\n};\n\nmodule Tick = Tick.Default;\nmodule Zed_utf8 = Zed_utf8;\n"
  },
  {
    "path": "src/Core/TextOverflow.re",
    "content": "type t =\n  | Overflow\n  | Clip\n  | Ellipsis\n  | UserDefined(string);\n\nlet removeLineBreaks = text => {\n  let re = Str.regexp(\"\\n\");\n  Str.global_replace(re, \"\", text);\n};\n\nlet rec handleOverflow = (~maxWidth, ~text, ~measure, ~character=\"…\", ()) => {\n  let clippedText = Zed_utf8.rchop(text);\n\n  let width = measure(clippedText ++ character);\n  width >= maxWidth && String.length(clippedText) > 1\n    ? handleOverflow(~maxWidth, ~text=clippedText, ~measure, ~character, ())\n    : clippedText ++ character;\n};\n"
  },
  {
    "path": "src/Core/TextWrapping.re",
    "content": "open Revery_TextWrap;\n\ntype wrapType =\n  | NoWrap\n  | Wrap\n  | WrapIgnoreWhitespace\n  | WrapHyphenate;\n\nlet wrapText = (~text, ~measureWidth as width_of_token, ~maxWidth, ~mode) => {\n  switch (mode) {\n  | NoWrap => [text]\n  | Wrap => wrap(text, ~max_width=maxWidth, ~width_of_token)\n  | WrapIgnoreWhitespace =>\n    wrap(\n      text,\n      ~max_width=maxWidth,\n      ~width_of_token,\n      ~ignore_preceding_whitespace=false,\n    )\n  | WrapHyphenate =>\n    wrap(text, ~max_width=maxWidth, ~width_of_token, ~hyphenate=true)\n  };\n};\n"
  },
  {
    "path": "src/Core/Tick.re",
    "content": "module type Clock = {let time: unit => Time.t;};\n\nmodule Log = (val Log.withNamespace(\"Revery.Tick\"));\n\nmodule DefaultClock = {\n  let time = Time.now;\n};\n\ntype callback = Time.t => unit;\ntype dispose = unit => unit;\n\nmodule IntMap =\n  Map.Make({\n    type t = int;\n    let compare = compare;\n  });\n\nmodule Make = (ClockImpl: Clock) => {\n  type nonrec callback = callback;\n  module TickId =\n    UniqueId.Make({});\n\n  type tickType =\n    | Timeout\n    | Interval;\n\n  type tickFunction = {\n    tickType,\n    id: int,\n    lastExecutionTime: Time.t,\n    frequency: Time.t,\n    f: callback,\n  };\n\n  let showTickFunction = (v: tickFunction) => {\n    string_of_int(v.id);\n  };\n\n  let _activeTickers: ref(list(tickFunction)) = ref([]);\n  let _scheduledTickers: ref(list(tickFunction)) = ref([]);\n  let _cancelledTickers: ref(IntMap.t(bool)) = ref(IntMap.empty);\n\n  let getActiveTickers = () => {\n    _activeTickers^ |> List.map(tf => tf.id);\n  };\n\n  let toString = () =>\n    _activeTickers^\n    |> List.fold_left(\n         (prev, curr) => showTickFunction(curr) ++ \", \" ++ prev,\n         \"\",\n       );\n\n  let pump = () => {\n    // Add any newly-scheduled tickers\n    _activeTickers := List.concat([_scheduledTickers^, _activeTickers^]);\n    _scheduledTickers := [];\n\n    Log.tracef(m =>\n      m(\n        \"Tick.pump - starting with %d active tickers.\",\n        List.length(_activeTickers^),\n      )\n    );\n\n    // Clear any pending tickers\n    let cancelled = _cancelledTickers^;\n    _activeTickers :=\n      List.fold_left(\n        (prev, curr) => {\n          switch (IntMap.find_opt(curr.id, cancelled)) {\n          | None => [curr, ...prev]\n          | Some(_) => prev\n          }\n        },\n        [],\n        _activeTickers^,\n      );\n    _cancelledTickers := IntMap.empty;\n\n    let currentTime = ClockImpl.time();\n\n    let f = (tf: tickFunction) => {\n      let nextTime = Time.(tf.lastExecutionTime + tf.frequency);\n\n      if (nextTime <= currentTime) {\n        let elapsedTime = Time.(currentTime - tf.lastExecutionTime);\n        ignore(tf.f(elapsedTime));\n        switch (tf.tickType) {\n        | Timeout => None\n        | Interval => Some({...tf, lastExecutionTime: currentTime})\n        };\n      } else {\n        Some(tf);\n      };\n    };\n    let newTickers = _activeTickers^ |> List.filter_map(f);\n    _activeTickers := newTickers;\n\n    Log.tracef(m =>\n      m(\n        \"Tick.pump - ending with %d active tickers.\",\n        List.length(_activeTickers^),\n      )\n    );\n  };\n\n  let _clear = (~name, id: int, ()) => {\n    Log.tracef(m => m(\"Clearing interval/timeout: %s %d\", name, id));\n    _cancelledTickers := IntMap.add(id, true, _cancelledTickers^);\n  };\n\n  exception Stop;\n\n  let interval = (~name: string, f: callback, frequency: Time.t) => {\n    let id = TickId.getUniqueId();\n    Log.tracef(m => m(\"Interval - starting timer: %s %d\", name, id));\n\n    let f = t => {\n      Log.tracef(m => m(\"Interval - running timer: %s %d\", name, id));\n      try(f(t)) {\n      | Stop => _clear(~name, id, ())\n      };\n    };\n\n    let tf: tickFunction = {\n      tickType: Interval,\n      id,\n      lastExecutionTime: ClockImpl.time(),\n      frequency,\n      f,\n    };\n\n    _scheduledTickers := [tf, ..._scheduledTickers^];\n    _clear(~name, id);\n  };\n\n  let timeout = (~name, f, waitTime: Time.t) => {\n    let id = TickId.getUniqueId();\n    Log.tracef(m => m(\"Timeout - starting timer: %s %d\", name, id));\n\n    let f = _t => {\n      Log.tracef(m => m(\"Timeout - running timer: %s %d\", name, id));\n      f();\n    };\n\n    let tf: tickFunction = {\n      tickType: Timeout,\n      id,\n      lastExecutionTime: ClockImpl.time(),\n      frequency: waitTime,\n      f,\n    };\n\n    _scheduledTickers := [tf, ..._scheduledTickers^];\n    _clear(~name, id);\n  };\n};\n\nmodule Default = Make(DefaultClock);\n"
  },
  {
    "path": "src/Core/Time.re",
    "content": "type t = float;\n\nlet zero = 0.;\nlet seconds = t => float(t);\nlet milliseconds = t => float(t) /. 1000.;\nlet ms = milliseconds;\n\nlet ofFloatSeconds = t => t;\nlet toFloatSeconds = t => t;\n\nlet (+) = (+.);\nlet (-) = (-.);\nlet ( * ) = ( *. );\nlet ( *. ) = ( *. );\nlet (/) = (/.);\nlet (/.) = (/.);\n\nlet max = Float.max;\nlet min = Float.min;\nlet (mod) = mod_float;\n\nlet toString = t => string_of_float(t) ++ \"s\";\n\nlet now = Unix.gettimeofday;\n"
  },
  {
    "path": "src/Core/Time.rei",
    "content": "type t;\n\nlet zero: t;\nlet seconds: int => t;\nlet milliseconds: int => t;\nlet ms: int => t;\n\n/**\n  [ofFloatSeconds(seconds)] converts a float value in seconds to a [t] representing the time.\n*/\nlet ofFloatSeconds: float => t;\n\n/**\n  [toFloatSeconds(time)] converts the time [t] to a float value representing the time in seconds.\n*/\nlet toFloatSeconds: t => float;\n\nlet (+): (t, t) => t;\nlet (-): (t, t) => t;\nlet ( * ): (t, t) => t;\nlet ( *. ): (t, float) => t;\nlet (/): (t, t) => t;\nlet (/.): (t, float) => t;\n\nlet max: (t, t) => t;\nlet min: (t, t) => t;\nlet (mod): (t, t) => t;\n\nlet toString: t => string;\n\n/**\n    [now()] returns the current system time [t]\n*/\nlet now: unit => t;\n"
  },
  {
    "path": "src/Core/UniqueId.re",
    "content": "module Make = (()) => {\n  let current = ref(0);\n\n  let getUniqueId = () => {\n    let ret = current^;\n    incr(current);\n    ret;\n  };\n};\n"
  },
  {
    "path": "src/Core/Vsync.re",
    "content": "type t =\n  | Immediate /* no vsync */\n  | Synchronized; /* waiting for vsync */\n// TODO: Enable adaptive vsync\n//| Adaptive /* like vsync, but swaps buffers immediately if vertical retrace missed. Not supported everywhere */\n\nlet toInt = v =>\n  switch (v) {\n  | Immediate => 0\n  | Synchronized => 1\n  //| Adaptive => -1\n  };\n\nlet toString = v =>\n  switch (v) {\n  | Immediate => \"Immediate\"\n  | Synchronized => \"Synchronized\"\n  //| Adaptive => \"Adaptive\"\n  };\n"
  },
  {
    "path": "src/Core/Window.re",
    "content": "open Events;\n\ntype unsubscribe = unit => unit;\n\n[@deriving show({with_path: false})]\ntype size =\n  Sdl2.Size.t = {\n    width: int,\n    height: int,\n  };\n\nlet scaleSize = (~scale: float, size) => {\n  let width = int_of_float(float_of_int(size.width) /. scale);\n  let height = int_of_float(float_of_int(size.height) /. scale);\n\n  {width, height};\n};\n\nmodule Log = (val Log.withNamespace(\"Revery.Core.Window\"));\n\nmodule WindowMetrics: {\n  // This disables all unused value warnings for this module. Unfortunately necessary due to `deriving show`.\n  [@warning \"-32\"];\n\n  [@deriving show]\n  type t =\n    pri {\n      /* [scaledSize] is the size of the window, in scaled screen coordinates, based on the display settings of the platform */\n      scaledSize: size,\n      /* [unscaledSize] is the size of the window, in screen coordinates, without display scaling applied */\n      unscaledSize: size,\n      /* [framebufferSize] is the actual size in pixels of the framebuffer - the render surface. On high DPI displays, this may be\n         some multiple of the screen sizes described by [scaledSize] and [unscaledSize]. */\n      framebufferSize: size,\n      /* [devicePixelRatio] is the ratio of pixels to screen coordinates (ie, [framebufferSize / unscaledSize]) */\n      devicePixelRatio: float,\n      /* [scaleFactor] is the ratio between [unscaledSize] and [scaledSize] */\n      scaleFactor: float,\n      zoom: float,\n      isDirty: bool,\n    };\n\n  let fromSdlWindow:\n    (~forceScaleFactor: float=?, ~zoom: float=?, Sdl2.Window.t) => t;\n\n  let setZoom: (float, t) => t;\n  let markDirty: t => t;\n} = {\n  [@deriving show({with_path: false})]\n  type t = {\n    scaledSize: size,\n    unscaledSize: size,\n    framebufferSize: size,\n    devicePixelRatio: float,\n    scaleFactor: float,\n    zoom: float,\n    isDirty: bool,\n  };\n\n  module Internal = {\n    let getScaleFactor = (~forceScaleFactor=?, sdlWindow) => {\n      switch (forceScaleFactor) {\n      // If a scale factor is forced... prefer that!\n      | Some(v) => v\n      // Otherwise, the way we figure out the scale factor depends on the platform\n      | None =>\n        switch (Environment.os) {\n        // Mac and iOS is easy... there isn't any scaling factor.  The window is automatically\n        // proportioned for us. The scaling is handled by the ratio of size / framebufferSize.\n        | IOS\n        | Mac(_) => 1.0\n        // On Windows, we need to try a Win32 API to get the scale factor\n        | Windows(_) =>\n          let scale = Sdl2.Window.getWin32ScaleFactor(sdlWindow);\n          Log.tracef(m =>\n            m(\"_getScaleFactor - from getWin32ScaleFactor: %f\", scale)\n          );\n          scale;\n\n        // On Linux it can be pretty tricky depending on the display server and other factors.\n        // - First, we'll look for a [GDK_SCALE] environment variable, and prefer that.\n        // - Otherwise we default to 1.0 until we have a reliable method to obtain the value.\n        //   See the following links for more details:\n        //     https://github.com/revery-ui/revery/issues/878\n        //     https://github.com/glfw/glfw/issues/1019\n        //     https://github.com/mosra/magnum/commit/ae31c3cd82ba53454b8ab49d3f9d8ca385560d4b\n        //     https://github.com/glfw/glfw/blob/250b94cd03e6f947ba516869c7f3b277f8d0cacc/src/x11_init.c#L938\n        //     https://wiki.archlinux.org/index.php/HiDPI\n        | Linux(_) =>\n          switch (Rench.Environment.getEnvironmentVariable(\"GDK_SCALE\")) {\n          | None => 1.0\n          | Some(v) =>\n            Log.trace(\n              \"_getScaleFactor - Linux - got GDK_SCALE variable: \" ++ v,\n            );\n            switch (Float.of_string_opt(v)) {\n            | Some(v) => v\n            | None => 1.0\n            };\n          }\n        // On Android we can mostly trust the getDPI and the base DPI is 160\n        // ddpi indicates the scale factor choosen by the manufacturer\n        // hdpi is the real horizontal dpi, vdpi is the real vertical dpi, they can be different\n        // also any non integer scaleFactor is valid as it's choosen by the manufecturer\n        | Android =>\n          let display = Sdl2.Window.getDisplay(sdlWindow);\n          let dpi = Sdl2.Display.getDPI(display);\n          let scaleFactor = dpi.ddpi /. 160.0;\n          Log.tracef(m =>\n            m(\n              \"_getScaleFactor - Android - inferring from DPI: %f\",\n              scaleFactor,\n            )\n          );\n          scaleFactor;\n        | _ => 1.0\n        }\n      };\n    };\n  };\n\n  let fromSdlWindow = (~forceScaleFactor=?, ~zoom=1.0, sdlWindow) => {\n    let unscaledSize = Sdl2.Window.getSize(sdlWindow);\n    let framebufferSize = Sdl2.Gl.getDrawableSize(sdlWindow);\n\n    let scaleFactor = Internal.getScaleFactor(~forceScaleFactor?, sdlWindow);\n\n    let devicePixelRatio =\n      float_of_int(framebufferSize.width) /. float_of_int(unscaledSize.width);\n\n    let scaledSize = unscaledSize |> scaleSize(~scale=scaleFactor);\n\n    {\n      scaledSize,\n      unscaledSize: {\n        width: unscaledSize.width,\n        height: unscaledSize.height,\n      },\n      framebufferSize: {\n        width: framebufferSize.width,\n        height: framebufferSize.height,\n      },\n      scaleFactor,\n      devicePixelRatio,\n      zoom,\n      isDirty: false,\n    };\n  };\n\n  let setZoom = (zoom, metrics) => {\n    Log.tracef(m => m(\"Setting zoom: %f\", zoom));\n    {...metrics, zoom, isDirty: true};\n  };\n  let markDirty = metrics => {...metrics, isDirty: true};\n};\n\nmodule FPS = {\n  let timerInterval = 1000;\n  type t = {\n    // tickAfterLastRender is in milliseconds\n    mutable tickAfterLastRender: int,\n    mutable fps: int,\n    mutable timer: int,\n    mutable frameCount: int,\n  };\n\n  let default = () => {\n    tickAfterLastRender: Sdl2.Timekeeping.getTicks(),\n    fps: 0,\n    timer: 0,\n    frameCount: 0,\n  };\n  let getFPS = (c: t) => c.fps;\n  let update = (state: t) => {\n    let tick = Sdl2.Timekeeping.getTicks();\n    let deltaTime = tick - state.tickAfterLastRender;\n\n    state.timer = state.timer + deltaTime;\n    state.frameCount = state.frameCount + 1;\n    if (state.timer >= timerInterval) {\n      state.fps = state.frameCount;\n      state.timer = 0;\n      state.frameCount = 0;\n    };\n\n    state.tickAfterLastRender = tick;\n  };\n};\n\ntype t = {\n  mutable backgroundColor: Color.t,\n  sdlWindow: Sdl2.Window.t,\n  sdlContext: Sdl2.Gl.context,\n  uniqueId: int,\n  forceScaleFactor: option(float),\n  mutable render: unit => unit,\n  mutable shouldRender: unit => bool,\n  mutable canQuit: unit => bool,\n  mutable metrics: WindowMetrics.t,\n  mutable isRendering: bool,\n  mutable requestedUnscaledSize: option(size),\n  mutable fpsCounter: FPS.t,\n  mutable showFPSCounter: bool,\n  // True if composition (IME) is active\n  mutable isComposingText: bool,\n  mutable dropState: option(list(string)),\n  mutable opacity: float,\n  titlebarStyle: WindowStyles.titlebar,\n  isDecorated: bool,\n  onBeforeRender: Event.t(unit),\n  onAfterRender: Event.t(unit),\n  onBeforeSwap: Event.t(unit),\n  onAfterSwap: Event.t(unit),\n  onFocusGained: Event.t(unit),\n  onFocusLost: Event.t(unit),\n  onExposed: Event.t(unit),\n  onKeyDown: Event.t(Key.KeyEvent.t),\n  onKeyUp: Event.t(Key.KeyEvent.t),\n  onMouseUp: Event.t(mouseButtonEvent),\n  onMouseMove: Event.t(mouseMoveEvent),\n  onMouseWheel: Event.t(mouseWheelEvent),\n  onMouseDown: Event.t(mouseButtonEvent),\n  onMouseEnter: Event.t(unit),\n  onMouseLeave: Event.t(unit),\n  onMaximized: Event.t(unit),\n  onFullscreen: Event.t(unit),\n  onMinimized: Event.t(unit),\n  onRestored: Event.t(unit),\n  onSizeChanged: Event.t(size),\n  onMoved: Event.t((int, int)),\n  onCompositionStart: Event.t(unit),\n  onCompositionEdit: Event.t(textEditEvent),\n  onCompositionEnd: Event.t(unit),\n  onTextInputCommit: Event.t(textInputEvent),\n  onFileDropped: Event.t(fileDropEvent),\n};\n\nmodule Internal = {\n  let setTitlebarStyle = (w: Sdl2.Window.t, style: WindowStyles.titlebar) => {\n    switch (Environment.os) {\n    | Mac(_) =>\n      switch (style) {\n      | Transparent => Sdl2.Window.setMacTitlebarTransparent(w)\n      | Hidden => Sdl2.Window.setMacTitlebarHidden(w)\n      | System => ()\n      }\n    | Android\n    | Browser\n    // NOTE: may work for IOS?\n    | IOS\n    | Linux(_)\n    | Unknown\n    | Windows(_) => ()\n    };\n  };\n\n  let resetTitlebarStyle = window =>\n    // On restore, we need to set the titlebar transparent again on Mac\n    switch (window.titlebarStyle) {\n    | Transparent => setTitlebarStyle(window.sdlWindow, Transparent)\n    | Hidden => setTitlebarStyle(window.sdlWindow, Hidden)\n    | System => ()\n    };\n\n  let updateMetrics = (w: t) => {\n    w.metrics =\n      WindowMetrics.fromSdlWindow(\n        ~forceScaleFactor=?w.forceScaleFactor,\n        ~zoom=w.metrics.zoom,\n        w.sdlWindow,\n      );\n    Log.trace(\n      \"updateMetrics - new metrics: \" ++ WindowMetrics.show(w.metrics),\n    );\n  };\n\n  let setRawSize = (win: t, adjWidth: int, adjHeight: int) => {\n    Log.tracef(m =>\n      m(\"setRawSize - calling with: %ix%i\", adjWidth, adjHeight)\n    );\n\n    if (adjWidth != win.metrics.unscaledSize.width\n        || adjHeight != win.metrics.unscaledSize.height) {\n      /*\n       *  Don't resize in the middle of a render -\n       *  we'll queue up the render operation for next time.\n       */\n      if (win.isRendering) {\n        Log.trace(\"setRawSize - queuing for next render\");\n        win.requestedUnscaledSize =\n          Some({width: adjWidth, height: adjHeight});\n      } else {\n        Log.trace(\"setRawSize - calling Sdl2.Window.setSize\");\n        Sdl2.Window.setSize(win.sdlWindow, adjWidth, adjHeight);\n        win.requestedUnscaledSize = None;\n        win.metrics = WindowMetrics.markDirty(win.metrics);\n        Log.tracef(m => {\n          let Sdl2.Size.{width, height} = Sdl2.Window.getSize(win.sdlWindow);\n          m(\n            \"setRawSize: SDL size reported after resize: %ux%u\",\n            width,\n            height,\n          );\n        });\n      };\n    };\n  };\n\n  let resizeIfNecessary = (w: t) =>\n    switch (w.requestedUnscaledSize) {\n    | Some({width, height}) => setRawSize(w, width, height)\n    | None => ()\n    };\n};\n\nlet onBeforeRender = w => Event.subscribe(w.onBeforeRender);\nlet onAfterRender = w => Event.subscribe(w.onAfterRender);\nlet onBeforeSwap = w => Event.subscribe(w.onBeforeSwap);\nlet onAfterSwap = w => Event.subscribe(w.onAfterSwap);\nlet onFocusGained = w => Event.subscribe(w.onFocusGained);\nlet onFocusLost = w => Event.subscribe(w.onFocusLost);\nlet onMaximized = w => Event.subscribe(w.onMaximized);\nlet onFullscreen = w => Event.subscribe(w.onFullscreen);\nlet onMinimized = w => Event.subscribe(w.onMinimized);\nlet onRestored = w => Event.subscribe(w.onRestored);\nlet onExposed = w => Event.subscribe(w.onExposed);\nlet onKeyDown = w => Event.subscribe(w.onKeyDown);\nlet onKeyUp = w => Event.subscribe(w.onKeyUp);\nlet onMouseMove = w => Event.subscribe(w.onMouseMove);\nlet onMouseWheel = w => Event.subscribe(w.onMouseWheel);\nlet onMouseEnter = w => Event.subscribe(w.onMouseEnter);\nlet onMouseLeave = w => Event.subscribe(w.onMouseLeave);\nlet onMouseDown = w => Event.subscribe(w.onMouseDown);\nlet onMouseUp = w => Event.subscribe(w.onMouseUp);\nlet onCompositionStart = w => Event.subscribe(w.onCompositionStart);\nlet onCompositionEdit = w => Event.subscribe(w.onCompositionEdit);\nlet onCompositionEnd = w => Event.subscribe(w.onCompositionEnd);\nlet onTextInputCommit = w => Event.subscribe(w.onTextInputCommit);\nlet onSizeChanged = w => Event.subscribe(w.onSizeChanged);\nlet onMoved = w => Event.subscribe(w.onMoved);\nlet onFileDropped = w => Event.subscribe(w.onFileDropped);\n\nlet getUniqueId = (w: t) => w.uniqueId;\n\nlet isDirty = (w: t) =>\n  if (w.shouldRender() || w.metrics.isDirty) {\n    true;\n  } else {\n    w.requestedUnscaledSize != None;\n  };\n\nlet isDecorated = (w: t) => w.isDecorated;\n\nlet getSdlWindow = (w: t) => w.sdlWindow;\n\nlet setTitle = (v: t, title: string) => {\n  Sdl2.Window.setTitle(v.sdlWindow, title);\n};\n\nlet getTitlebarHeight = (v: t) =>\n  Sdl2.Window.getMacTitlebarHeight(v.sdlWindow);\n\nlet setSize = (~width: int, ~height: int, win: t) => {\n  Log.tracef(m => m(\"setSize - calling with: %ux%u\", width, height));\n  // On platforms that return a non-unit scale factor (Windows and Linux),\n  // we also have to scale the window size by the scale factor\n  let adjWidth =\n    int_of_float(float_of_int(width) *. win.metrics.scaleFactor);\n  let adjHeight =\n    int_of_float(float_of_int(height) *. win.metrics.scaleFactor);\n\n  Internal.setRawSize(win, adjWidth, adjHeight);\n};\n\nlet setMinimumSize = (~width: int, ~height: int, win: t) => {\n  Log.tracef(m => m(\"setMinimumSize - calling with: %ux%u\", width, height));\n\n  Sdl2.Window.setMinimumSize(win.sdlWindow, width, height);\n};\n\nlet setZoom = (window, zoom) => {\n  window.metrics = WindowMetrics.setZoom(max(zoom, 0.1), window.metrics);\n};\n\nlet setUnsavedWork = (window, truth) => {\n  Revery_Native.Window.setUnsavedWork(window.sdlWindow, truth);\n};\n\nlet render = window => {\n  Internal.resizeIfNecessary(window);\n\n  if (window.metrics.isDirty) {\n    Internal.updateMetrics(window);\n  };\n\n  window.isRendering = true;\n\n  Event.dispatch(window.onBeforeRender, ());\n  window.render();\n  Event.dispatch(window.onAfterRender, ());\n\n  Event.dispatch(window.onBeforeSwap, ());\n  Performance.bench(\"swapWindow\", () => Sdl2.Gl.swapWindow(window.sdlWindow));\n  Event.dispatch(window.onAfterSwap, ());\n  window.isRendering = false;\n\n  FPS.update(window.fpsCounter);\n};\n\nlet handleEvent = (sdlEvent: Sdl2.Event.t, v: t) => {\n  Log.tracef(m => m(\"Window.handleEvent: %s\", Sdl2.Event.show(sdlEvent)));\n  switch (sdlEvent) {\n  | Sdl2.Event.MouseWheel({deltaX, deltaY, _}) =>\n    let (windowX, windowY) = Sdl2.Window.getPosition(v.sdlWindow);\n    let (mouseX, mouseY) = Sdl2.Mouse.getGlobalPosition();\n    let wheelEvent: Events.mouseWheelEvent = {\n      deltaX: float(deltaX),\n      deltaY: float(deltaY),\n      mouseX: float(mouseX - windowX),\n      mouseY: float(mouseY - windowY),\n      keymod: Sdl2.Keymod.getState(),\n    };\n    Event.dispatch(v.onMouseWheel, wheelEvent);\n\n  | Sdl2.Event.MouseMotion({x, y, _}) =>\n    let mouseEvent: Events.mouseMoveEvent = {\n      mouseX: float(x),\n      mouseY: float(y),\n      keymod: Sdl2.Keymod.getState(),\n    };\n    Event.dispatch(v.onMouseMove, mouseEvent);\n\n  | Sdl2.Event.MouseButtonUp(event) =>\n    Event.dispatch(\n      v.onMouseUp,\n      {\n        button: MouseButton.convert(event.button),\n        keymod: Sdl2.Keymod.getState(),\n      },\n    )\n\n  | Sdl2.Event.MouseButtonDown(event) =>\n    Event.dispatch(\n      v.onMouseDown,\n      {\n        button: MouseButton.convert(event.button),\n        keymod: Sdl2.Keymod.getState(),\n      },\n    )\n\n  | Sdl2.Event.KeyDown({keycode, keymod, scancode, repeat, _}) =>\n    let keyEvent: Key.KeyEvent.t = {keycode, scancode, keymod, repeat};\n    Event.dispatch(v.onKeyDown, keyEvent);\n\n  | Sdl2.Event.KeyUp({keycode, keymod, scancode, repeat, _}) =>\n    let keyEvent: Key.KeyEvent.t = {keycode, scancode, keymod, repeat};\n    Event.dispatch(v.onKeyUp, keyEvent);\n\n  | Sdl2.Event.TextEditing(te) =>\n    if (!v.isComposingText) {\n      Event.dispatch(v.onCompositionStart, ());\n      v.isComposingText = true;\n    };\n\n    Event.dispatch(\n      v.onCompositionEdit,\n      {text: te.text, start: te.start, length: te.length},\n    );\n  | Sdl2.Event.TextInput(ti) =>\n    if (v.isComposingText) {\n      Event.dispatch(v.onCompositionEnd, ());\n      v.isComposingText = false;\n    };\n    Event.dispatch(v.onTextInputCommit, {text: ti.text});\n\n  | Sdl2.Event.WindowResized(_) =>\n    v.metrics = WindowMetrics.markDirty(v.metrics)\n\n  | Sdl2.Event.WindowSizeChanged({width, height, _}) =>\n    v.metrics = WindowMetrics.markDirty(v.metrics);\n\n    // Scale the window size changed event, so that is in the same 'scaled-screen-space' as\n    // other size functions, like [getSize], [setSize], and [create].\n    let unscaledSize = {width, height};\n    let scaledSize = scaleSize(~scale=v.metrics.scaleFactor, unscaledSize);\n    Event.dispatch(v.onSizeChanged, scaledSize);\n\n  | Sdl2.Event.WindowMoved({x, y, _}) =>\n    v.metrics = WindowMetrics.markDirty(v.metrics);\n    Event.dispatch(v.onMoved, (x, y));\n\n  | Sdl2.Event.WindowEnter(_) => Event.dispatch(v.onMouseEnter, ())\n  | Sdl2.Event.WindowLeave(_) => Event.dispatch(v.onMouseLeave, ())\n  | Sdl2.Event.WindowExposed(_) => Event.dispatch(v.onExposed, ())\n  | Sdl2.Event.WindowMaximized(_) => Event.dispatch(v.onMaximized, ())\n  | Sdl2.Event.WindowFullscreen(_) => Event.dispatch(v.onFullscreen, ())\n  | Sdl2.Event.WindowMinimized(_) => Event.dispatch(v.onMinimized, ())\n\n  | Sdl2.Event.WindowRestored(_) =>\n    Internal.resetTitlebarStyle(v);\n    Event.dispatch(v.onRestored, ());\n\n  | Sdl2.Event.WindowFocusGained(_) => Event.dispatch(v.onFocusGained, ())\n  | Sdl2.Event.WindowFocusLost(_) => Event.dispatch(v.onFocusLost, ())\n  | Sdl2.Event.DropBegin(_) => v.dropState = Some([])\n  | Sdl2.Event.DropFile({file, _}) =>\n    switch (v.dropState) {\n    | Some(list) => v.dropState = Some([file, ...list])\n    | None =>\n      Log.warn(\"Received drop file event without preceding drop begin\")\n    }\n  | Sdl2.Event.DropComplete({x, y, _}) =>\n    switch (v.dropState) {\n    | None\n    | Some([]) =>\n      Log.warn(\"Received drop complete event without preceding drop events\")\n    | Some(list) =>\n      v.dropState = None;\n      Event.dispatch(\n        v.onFileDropped,\n        {\n          mouseX: float(x),\n          mouseY: float(y),\n          paths: List.rev(list),\n          keymod: Sdl2.Keymod.getState(),\n        },\n      );\n    }\n  | Sdl2.Event.Quit => ()\n  | _ => ()\n  };\n};\n\nlet setVsync =\n    (\n      _window: t, // TODO: Multiple windows - set context\n      vsync: Vsync.t,\n    ) => {\n  Log.info(\"Using vsync: \" ++ Vsync.toString(vsync));\n\n  switch (vsync) {\n  | Vsync.Immediate => Sdl2.Gl.setSwapInterval(0)\n  | Vsync.Synchronized => Sdl2.Gl.setSwapInterval(1)\n  };\n};\n\nlet create = (name: string, options: WindowCreateOptions.t) => {\n  Log.debug(\"Starting window creation...\");\n\n  let displays = Sdl2.Display.getDisplays();\n\n  Log.infof(m => m(\"Display count: %d\", List.length(displays)));\n\n  displays\n  |> List.iteri((idx, display) => {\n       Log.infof(m =>\n         m(\n           \"Display %d:%s - %s Bounds: %s\",\n           idx,\n           Sdl2.Display.getName(display),\n           Sdl2.Display.getCurrentMode(display) |> Sdl2.Display.Mode.show,\n           Sdl2.Display.getBounds(display) |> Sdl2.Rect.toString,\n         )\n       )\n     });\n\n  let width = options.width == 0 ? 800 : options.width;\n  let height = options.height == 0 ? 480 : options.height;\n\n  let x = options.x;\n  let y = options.y;\n\n  Log.infof(m =>\n    m(\"Creating window %s width: %u height: %u\", name, width, height)\n  );\n  let sdlWindow =\n    Sdl2.Window.create(name, x, y, width, height, options.acceleration);\n  Log.info(\"Window created successfully.\");\n\n  let uniqueId = Sdl2.Window.getId(sdlWindow);\n  Log.debugf(m => m(\"- Id: %i\", uniqueId));\n  let pixelFormat =\n    Sdl2.Window.getPixelFormat(sdlWindow) |> Sdl2.PixelFormat.toString;\n  Log.debugf(m => m(\"- PixelFormat: %s\", pixelFormat));\n\n  // We need to let Windows know that we are DPI-aware and that we are going to\n  // properly handle scaling. This is a no-op on other platforms.\n  Sdl2.Window.setWin32ProcessDPIAware(sdlWindow);\n\n  Log.debug(\"Setting window context\");\n  let context = Sdl2.Gl.setup(sdlWindow);\n  Sdl2.Gl.makeCurrent(sdlWindow, context);\n  Log.debug(\"GL setup. Checking GL version...\");\n  let version = Sdl2.Gl.getString(Sdl2.Gl.Version);\n  Log.debug(\"Checking GL vendor...\");\n  let vendor = Sdl2.Gl.getString(Sdl2.Gl.Vendor);\n  Log.debug(\"Checking GL shading language version...\");\n  let shadingLanguageVersion =\n    Sdl2.Gl.getString(Sdl2.Gl.ShadingLanguageVersion);\n  let renderer = Sdl2.Gl.getString(Sdl2.Gl.Renderer);\n\n  Log.info(\"OpenGL hardware info:\");\n  Log.infof(m => m(\"  renderer: %s\", renderer));\n  Log.infof(m => m(\"  version: %s\", version));\n  Log.infof(m => m(\"  vendor: %s\", vendor));\n  Log.infof(m => m(\"  shadingLanguageVersion: %s\", shadingLanguageVersion));\n\n  switch (options.icon) {\n  | None => Log.debug(\"No icon to load.\")\n\n  | Some(path) =>\n    let execDir = Environment.getExecutingDirectory();\n    let relativeImagePath = execDir ++ path;\n\n    Log.debug(\"Loading icon from: \" ++ relativeImagePath);\n    switch (Sdl2.Surface.createFromImagePath(relativeImagePath)) {\n    | Ok(v) =>\n      Log.debug(\"Icon loaded successfully.\");\n      Sdl2.Window.setIcon(sdlWindow, v);\n      Log.debug(\"Icon set successfully.\");\n\n    | Error(msg) => Log.error(\"Error loading icon: \" ++ msg)\n    };\n  };\n\n  Log.debug(\"Getting window metrics\");\n  let metrics =\n    WindowMetrics.fromSdlWindow(\n      ~forceScaleFactor=?options.forceScaleFactor,\n      sdlWindow,\n    );\n  Log.debug(\"Metrics: \" ++ WindowMetrics.show(metrics));\n\n  let window = {\n    backgroundColor: options.backgroundColor,\n    sdlWindow,\n    sdlContext: context,\n    uniqueId,\n\n    render: () => (),\n    shouldRender: () => false,\n    canQuit: () => true,\n\n    metrics,\n    isRendering: false,\n    requestedUnscaledSize: None,\n\n    fpsCounter: FPS.default(),\n    showFPSCounter: false,\n\n    isComposingText: false,\n    dropState: None,\n\n    forceScaleFactor: options.forceScaleFactor,\n\n    isDecorated: options.decorated,\n    opacity: options.opacity,\n\n    onBeforeRender: Event.create(),\n    onAfterRender: Event.create(),\n    onBeforeSwap: Event.create(),\n    onAfterSwap: Event.create(),\n\n    onFocusGained: Event.create(),\n    onFocusLost: Event.create(),\n\n    onMinimized: Event.create(),\n    onMaximized: Event.create(),\n    onFullscreen: Event.create(),\n    onRestored: Event.create(),\n    onExposed: Event.create(),\n    onSizeChanged: Event.create(),\n    onMoved: Event.create(),\n\n    onMouseMove: Event.create(),\n    onMouseWheel: Event.create(),\n    onMouseUp: Event.create(),\n    onMouseDown: Event.create(),\n    onMouseEnter: Event.create(),\n    onMouseLeave: Event.create(),\n\n    onKeyDown: Event.create(),\n    onKeyUp: Event.create(),\n\n    onCompositionStart: Event.create(),\n    onCompositionEdit: Event.create(),\n    onCompositionEnd: Event.create(),\n    onTextInputCommit: Event.create(),\n    onFileDropped: Event.create(),\n\n    titlebarStyle: options.titlebarStyle,\n  };\n  // Call setSize to account for scaling, if necessary.\n  // On some platforms - ie, Windows, we don't know about the scale factor\n  // until after we create the window, so after creation we have to check\n  // to make sure we're at the correct size for scaling.\n  setSize(~width, ~height, window);\n\n  // Set a minimum size for the window\n  setMinimumSize(\n    ~width=options.minimumWidth,\n    ~height=options.minimumHeight,\n    window,\n  );\n\n  setVsync(window, options.vsync);\n\n  if (!options.decorated) {\n    Sdl2.Window.setBordered(sdlWindow, false);\n  };\n\n  if (!options.resizable) {\n    Sdl2.Window.setResizable(sdlWindow, false);\n  };\n\n  if (options.visible) {\n    Sdl2.Window.show(sdlWindow);\n  };\n\n  switch (options.titlebarStyle) {\n  | System => ()\n  | Transparent => Internal.setTitlebarStyle(sdlWindow, Transparent)\n  | Hidden => Internal.setTitlebarStyle(sdlWindow, Hidden)\n  };\n\n  Revery_Native.initWindow(sdlWindow);\n\n  if (options.maximized) {\n    Sdl2.Window.maximize(sdlWindow);\n  };\n\n  // Sdl2 has this named as Transparency, but it actually works as opacity\n  // where a value of 1.0 means it's fully opaque\n  Sdl2.Window.setTransparency(sdlWindow, options.opacity);\n\n  Internal.updateMetrics(window);\n\n  window;\n};\n\nlet startTextInput = (_w: t) => {\n  Sdl2.TextInput.start();\n};\n\nlet stopTextInput = (_w: t) => {\n  Sdl2.TextInput.stop();\n};\n\nlet isTextInputActive = (_w: t) => {\n  Sdl2.TextInput.isActive();\n};\n\nlet setInputRect = (_w: t, x, y, width, height) => {\n  // TODO: Do we need to apply scale factor here?\n  Sdl2.TextInput.setInputRect(\n    x,\n    y,\n    width,\n    height,\n  );\n};\n\nlet setOpacity = (w: t, opacity) => w.opacity = opacity;\n\nlet setBackgroundColor = (w: t, color: Color.t) => {\n  w.backgroundColor = color;\n};\n\nlet getBackgroundColor = window => window.backgroundColor;\n\nlet setPosition = (w: t, x: int, y: int) => {\n  Sdl2.Window.setPosition(w.sdlWindow, x, y);\n  w.metrics = WindowMetrics.markDirty(w.metrics);\n};\n\nlet center = (w: t) => {\n  Sdl2.Window.center(w.sdlWindow);\n};\n\nlet show = w => {\n  Sdl2.Window.show(w.sdlWindow);\n};\n\nlet hide = w => {\n  Sdl2.Window.hide(w.sdlWindow);\n};\n\nlet getSize = ({metrics, _}) => metrics.scaledSize;\n\nlet getPosition = window => {\n  Sdl2.Window.getPosition(window.sdlWindow);\n};\n\nlet getFramebufferSize = (w: t) => {\n  w.metrics.framebufferSize;\n};\n\nlet maximize = (w: t) => {\n  Sdl2.Window.maximize(w.sdlWindow);\n};\n\nlet isMaximized = (w: t) => {\n  Sdl2.Window.isMaximized(w.sdlWindow);\n};\n\nlet isFullscreen = (w: t) => {\n  Sdl2.Window.isFullscreen(w.sdlWindow);\n};\n\nlet raise = (w: t) => {\n  Sdl2.Window.raise(w.sdlWindow);\n};\n\nlet restore = (w: t) => {\n  Sdl2.Window.restore(w.sdlWindow);\n};\n\nlet minimize = (w: t) => {\n  Sdl2.Window.minimize(w.sdlWindow);\n};\n\nlet getDevicePixelRatio = (w: t) => {\n  w.metrics.devicePixelRatio;\n};\n\nlet getScaleAndZoom = (w: t) => {\n  w.metrics.scaleFactor *. w.metrics.zoom;\n};\n\nlet getZoom = (w: t) => {\n  w.metrics.zoom;\n};\n\nlet takeScreenshot = (_w: t, _filename: string) => {\n  (); // TODO: Migrate to Skia\n    /*let width = w.metrics.framebufferSize.width;\n      let height = w.metrics.framebufferSize.height;\n\n      let pixels =\n        Bigarray.Array2.create(\n          Bigarray.int8_unsigned,\n          Bigarray.c_layout,\n          height,\n          width * 4,\n        );\n\n      /* let image = Image.create(~width, ~height, ~numChannels=4, ~channelSize=1); */\n      /* let buffer = Image.getBuffer(image); */\n\n      /* WebGL is weird in that we can't capture with glReadPixels during\n         a render operation. Instead, we want to wait till it's over (we\n         can force this by triggering a new render) and then taking the\n         screenshot */\n      render(w);\n      Gl.glReadPixels(0, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);\n\n      let image = Image.create(pixels);\n\n      Image.save(image, filename);\n      Image.destroy(image);\n      */\n};\n\nlet canQuit = (w: t) => {\n  w.canQuit();\n};\n\nlet setCanQuitCallback = (window: t, callback) => {\n  window.canQuit = callback;\n};\n\nlet setRenderCallback = (window: t, callback) => window.render = callback;\n\nlet setShouldRenderCallback = (window: t, callback) =>\n  window.shouldRender = callback;\n\nlet getFPS = (w: t) => {\n  FPS.getFPS(w.fpsCounter);\n};\n\nlet showFPSCounter = (w: t) => {\n  w.showFPSCounter = true;\n};\nlet hideFPSCounter = (w: t) => {\n  w.showFPSCounter = false;\n};\n\nlet shouldShowFPSCounter = (w: t) => {\n  w.showFPSCounter;\n};\n"
  },
  {
    "path": "src/Core/Window.rei",
    "content": "open Events;\n\ntype t;\n\ntype size = {\n  width: int,\n  height: int,\n};\n\ntype unsubscribe = unit => unit;\n\nlet onBeforeRender: (t, unit => unit) => unsubscribe;\nlet onAfterRender: (t, unit => unit) => unsubscribe;\nlet onBeforeSwap: (t, unit => unit) => unsubscribe;\nlet onAfterSwap: (t, unit => unit) => unsubscribe;\nlet onFocusGained: (t, unit => unit) => unsubscribe;\nlet onFocusLost: (t, unit => unit) => unsubscribe;\nlet onMaximized: (t, unit => unit) => unsubscribe;\nlet onFullscreen: (t, unit => unit) => unsubscribe;\nlet onMinimized: (t, unit => unit) => unsubscribe;\nlet onRestored: (t, unit => unit) => unsubscribe;\nlet onCompositionStart: (t, unit => unit) => unsubscribe;\nlet onCompositionEdit: (t, textEditEvent => unit) => unsubscribe;\nlet onCompositionEnd: (t, unit => unit) => unsubscribe;\nlet onExposed: (t, unit => unit) => unsubscribe;\nlet onKeyDown: (t, Key.KeyEvent.t => unit) => unsubscribe;\nlet onKeyUp: (t, Key.KeyEvent.t => unit) => unsubscribe;\nlet onMouseEnter: (t, unit => unit) => unsubscribe;\nlet onMouseLeave: (t, unit => unit) => unsubscribe;\nlet onMouseMove: (t, mouseMoveEvent => unit) => unsubscribe;\nlet onMouseDown: (t, mouseButtonEvent => unit) => unsubscribe;\nlet onMouseUp: (t, mouseButtonEvent => unit) => unsubscribe;\nlet onMouseWheel: (t, mouseWheelEvent => unit) => unsubscribe;\nlet onSizeChanged: (t, size => unit) => unsubscribe;\nlet onMoved: (t, ((int, int)) => unit) => unsubscribe;\nlet onTextInputCommit: (t, textInputEvent => unit) => unsubscribe;\nlet onFileDropped: (t, fileDropEvent => unit) => unsubscribe;\n\nlet canQuit: t => bool;\n\nlet getDevicePixelRatio: t => float;\n\n/**\n  [getSize(window)] returns a [size] describing the window dimensions, accounting for display scaling.\n*/\nlet getSize: t => size;\n\nlet getFramebufferSize: t => size;\n\n/**\n  [setSize(~width, ~height, window)] sets the window size, taking display scaling into account.\n\n  For example, for a Windows display with 200% scaling, [setSize(~width=400, ~height=300, window)],\n  will actually set the window to a height of 800 pixels wide by 600 pixels high. This is usually\n  the behavior you want. To directly set the size, without considering display scaling, use [setUnscaledSize]\n*/\nlet setSize: (~width: int, ~height: int, t) => unit;\n\n/**\n  [setMinimumSize(~width, ~height, window)] sets the minimum window size.\n*/\nlet setMinimumSize: (~width: int, ~height: int, t) => unit;\n\nlet getPosition: t => (int, int);\nlet getScaleAndZoom: t => float;\nlet getSdlWindow: t => Sdl2.Window.t;\nlet getZoom: t => float;\nlet getUniqueId: t => int;\nlet getBackgroundColor: t => Color.t;\n\nlet isDirty: t => bool;\n\nlet isDecorated: t => bool;\n\nlet setInputRect: (t, int, int, int, int) => unit;\nlet startTextInput: t => unit;\nlet stopTextInput: t => unit;\nlet isTextInputActive: t => bool;\n\nlet center: t => unit;\nlet hide: t => unit;\nlet show: t => unit;\nlet maximize: t => unit;\nlet isMaximized: t => bool;\nlet isFullscreen: t => bool;\nlet raise: t => unit;\nlet restore: t => unit;\nlet minimize: t => unit;\n\nlet setOpacity: (t, float) => unit;\nlet setBackgroundColor: (t, Color.t) => unit;\nlet setPosition: (t, int, int) => unit;\nlet setTitle: (t, string) => unit;\nlet setZoom: (t, float) => unit;\nlet setVsync: (t, Vsync.t) => unit;\nlet setUnsavedWork: (t, bool) => unit;\n\nlet render: t => unit;\nlet handleEvent: (Sdl2.Event.t, t) => unit;\n\nlet getTitlebarHeight: t => float;\n\n/**\n  [create(name, options)] creates a new Revery application window.\n\n  See [WindowCreateOptions] for a list of available options.\n*/\nlet create: (string, WindowCreateOptions.t) => t;\n\nlet takeScreenshot: (t, string) => unit;\n\nlet setCanQuitCallback: (t, unit => bool) => unit;\nlet setRenderCallback: (t, unit => unit) => unit;\nlet setShouldRenderCallback: (t, unit => bool) => unit;\n\nlet getFPS: t => int;\nlet showFPSCounter: t => unit;\nlet hideFPSCounter: t => unit;\nlet shouldShowFPSCounter: t => bool;\n"
  },
  {
    "path": "src/Core/WindowCreateOptions.re",
    "content": "/** [t] is the type representing creation options for a window */\ntype t = {\n  resizable: bool,\n  /**\n    If [visible] is true, the window will be visible immediately upon creation.\n\n    If [visible] is false, the window will be invisible, and will require a call\n    to Window.toString to make it visible. Can be useful to hide transient UI states.\n  */\n  visible: bool,\n  /**\n    If [maximized] is true, the window will be expanded immediately upon creation.\n   */\n  maximized: bool,\n  /**\n    If [decorated] is true, the window will be framed with the OS-specific adorners.\n    If false, the default chrome behavior on the OS will be disabled and (if you want\n    to maintain that functionality) you must implement it yourself through the\n    mouseBehavior attribute on Views\n    */\n  decorated: bool,\n  /**\n   [titlebarStyle] sets the appearance of the titlebar. Eventually this will be platform\n   independent, but as of right now, [Transparent] and [Hidden] only works on macOS.\n    */\n  titlebarStyle: WindowStyles.titlebar,\n  /**\n   [opacity] sets the opacity of the window.\n   A value of 1.0 means the window is fully opaque,\n   and a value of 0.0 means the window is completely transparent.\n   */\n  opacity: float,\n  /**\n    [x] is the initial horizontal position of the [Window], either [`Centered]\n    or [`Absolute(x)], where [x] is the horizontal pixel coordinate of the left\n    edge of the [Window]\n    */\n  x: [ | `Undefined | `Centered | `Absolute(int)],\n  /**\n    [y] is the initial horizontal position of the [Window], either [`Centered]\n    or [`Absolute(x)], where [y] is the vertical pixel coordinate of the top\n    edge of the [Window]\n    */\n  y: [ | `Undefined | `Centered | `Absolute(int)],\n  /**\n    [width] is the initial horizontal size of the [Window], with display scaling applied.\n    */\n  width: int,\n  /**\n    [height] is the initial vertical size of the [Window], with display scaling applied.\n  */\n  height: int,\n  /**\n    [minimumWidth] is the minimum horizontal size of the [Window].\n    */\n  minimumWidth: int,\n  /**\n    [minimumWidth] is the minimum vertical size of the [Window].\n    */\n  minimumHeight: int,\n  /**\n    [backgroundColor] specifies the initial Color of the [Window]\n  */\n  backgroundColor: Color.t,\n  /**\n    If [vsync] is true (default), rendering will be synchronized to the monitor refresh rate.\n\n    This can decrease tearing, but also limits the frame rate to the monito refresh rate.\n  */\n  vsync: Vsync.t,\n  /*\n      [icon] is an optional path to an icon to show in the window frame.\n   */\n  icon: option(string),\n  /*\n       [forceScaleFactor] is an optional value to force scaling factor. Scaling affects both the outer window dimensions,\n       as well as the scaling of UI elements.\n   */\n  forceScaleFactor: option(float),\n  /*\n       [acceleration] sets the strategy for picking a renderer - one of:\n       - [`Auto] - will select either a hardware or software renderer (most platforms prefer a hardware renderer)\n       - [`ForceHardware] - forces selection of a hardware-accelerated renderer\n       - [`ForceSoftware] - forces selection of a software renderer\n       This is a proxy for the [SDL_GL_ACCELERATED_VISUAL](https://wiki.libsdl.org/SDL_GL_SetAttribute) setting.\n   */\n  acceleration: [ | `Auto | `ForceHardware | `ForceSoftware],\n};\n\nlet create =\n    (\n      ~resizable=true,\n      ~visible=true,\n      ~maximized=false,\n      ~decorated=true,\n      ~titlebarStyle=WindowStyles.System,\n      ~opacity=1.0,\n      ~x=`Centered,\n      ~y=`Centered,\n      ~width=800,\n      ~height=600,\n      ~minimumWidth=200,\n      ~minimumHeight=100,\n      ~backgroundColor=Colors.cornflowerBlue,\n      ~vsync=Vsync.Synchronized,\n      ~icon=None,\n      ~forceScaleFactor=None,\n      ~acceleration=`Auto,\n      (),\n    ) => {\n  resizable,\n  visible,\n  maximized,\n  decorated,\n  titlebarStyle,\n  opacity,\n  x,\n  y,\n  width,\n  height,\n  minimumWidth,\n  minimumHeight,\n  backgroundColor,\n  forceScaleFactor,\n  vsync,\n  icon,\n  acceleration,\n};\n\nlet default = create();\n"
  },
  {
    "path": "src/Core/WindowStyles.re",
    "content": "type titlebar =\n  | System\n  // Only works on macOS as of right now\n  | Hidden\n  | Transparent;\n"
  },
  {
    "path": "src/Core/dune",
    "content": "(library\n (name Revery_Core)\n (public_name Revery.Core)\n (preprocess\n  (pps ppx_deriving.show ppx_optcomp))\n (preprocessor_deps ../Native/config.h)\n (libraries threads str lwt sdl2 skia flex Rench re Revery_Native\n   Revery_TextWrap timber Revery_Zed))\n"
  },
  {
    "path": "src/Draw/CanvasContext.re",
    "content": "/**\n * Canvas.re\n *\n * Module for integrating with the Skia canvas\n */\nopen Revery_Core;\n\nmodule Log = (val Log.withNamespace(\"Revery.CanvasContext\"));\n\nopen Skia;\n\ntype t = {\n  maybeGPUContext: option(Skia.Gr.Context.t),\n  surface: Skia.Surface.t,\n  canvas: Skia.Canvas.t,\n  mutable rootTransform: option(Skia.Matrix.t),\n  maybeWindow: option(Window.t),\n};\n\nlet createFromSurface = (surface: Skia.Surface.t) => {\n  maybeGPUContext: None,\n  surface,\n  canvas: Skia.Surface.getCanvas(surface),\n  rootTransform: None,\n  maybeWindow: None,\n};\n\nlet createGpuContext = () => {\n  let interface =\n    switch (Skia.Gr.Gl.Interface.makeNative()) {\n    | None =>\n      Log.debug(\"Unable to create native interface. Falling back to SDL2...\");\n      // On Windows - we use the OpenGL ES context, in order to support ANGLE, for improved compatibility.\n      // ANGLE provides a Direct3D implementation of the OpenGL ES API.\n      Skia.Gr.Gl.Interface.(Sys.win32 ? makeSdl2ES() : makeSdl2());\n    | Some(_) as nativeInterface =>\n      Log.debug(\"Native interface created successfully.\");\n      nativeInterface;\n    };\n  Log.info(\"Creating Skia context...\");\n  Skia.Gr.Context.makeGl(interface);\n};\n\nlet create = (gpuContext, window: Revery_Core.Window.t) => {\n  // Issue #759 - first, let's try to create a native context, since that is the most reliable...\n  // We'll fall back to an SDL2 context if not available (ie, in Wayland)\n  // TODO: There is still something busted with the way GL is being setup, for the SDL strategy not to work!\n  // Likely a fix or change is required here: https://github.com/revery-ui/reason-sdl2/blob/94dcd9094534c693998984fd684c642b0f658a43/src/sdl2_wrapper.cpp#L1065\n  switch (gpuContext) {\n  | None =>\n    Log.error(\"Unable to create skia context\");\n    None;\n  | Some(glContext) =>\n    Log.debug(\"Skia context created successfully.\");\n    let framebufferId = Sdl2.Gl.getFramebufferBinding();\n    Log.debug(Printf.sprintf(\"Framebuffer binding %d.\", framebufferId));\n    let framebufferInfo =\n      Gr.Gl.FramebufferInfo.make(\n        Unsigned.UInt.of_int(framebufferId),\n        Unsigned.UInt.of_int(0x8058),\n      );\n\n    let framebufferSize = Window.getFramebufferSize(window);\n    let backendRenderTarget =\n      Gr.BackendRenderTarget.makeGl(\n        framebufferSize.width,\n        framebufferSize.height,\n        0,\n        8,\n        framebufferInfo,\n      );\n    let surfaceProps = SurfaceProps.make(Unsigned.UInt32.of_int(0), RgbH);\n    switch (\n      Surface.makeFromBackendRenderTarget(\n        glContext,\n        backendRenderTarget,\n        BottomLeft,\n        Rgba8888,\n        None,\n        Some(surfaceProps),\n      )\n    ) {\n    | None =>\n      Log.error(\"Unable to create skia surface\");\n      None;\n    | Some(v) =>\n      Log.infof(m =>\n        m(\n          \"Successfully created canvas: %dx%d\",\n          framebufferSize.width,\n          framebufferSize.height,\n        )\n      );\n      let surface = v;\n      Some({\n        maybeGPUContext: Some(glContext),\n        surface,\n        canvas: Surface.getCanvas(surface),\n        rootTransform: None,\n        maybeWindow: Some(window),\n      });\n    };\n  };\n};\n\nlet createLayer = (~forceCpu=false, ~width, ~height, context: t) => {\n  Log.infof(m => m(\"Creating layer: %ldx%ld\", width, height));\n  let width =\n    if (width <= 0l) {\n      1l;\n    } else {\n      width;\n    };\n\n  let height =\n    if (height <= 0l) {\n      1l;\n    } else {\n      height;\n    };\n\n  let imageInfo = ImageInfo.make(width, height, Rgba8888, Premul, None);\n\n  let createCpuSurface = () => {\n    Log.tracef(m => m(\"Created CPU surface: %ld x %ld\", width, height));\n    Surface.makeRaster(imageInfo, 0, None);\n  };\n\n  let createGpuSurface = gpuContext => {\n    Log.trace(\"Trying to create GPU surface...\");\n    let surfaceProps = SurfaceProps.make(Unsigned.UInt32.of_int(0), RgbH);\n    Skia.Surface.makeRenderTarget(\n      gpuContext,\n      false,\n      imageInfo,\n      0,\n      BottomLeft,\n      Some(surfaceProps),\n      false,\n    );\n  };\n\n  let (gpuContext, maybeSurface) =\n    switch (context.maybeGPUContext) {\n    | None => (None, createCpuSurface())\n    | Some(_) when forceCpu => (None, createCpuSurface())\n    | Some(gpuContext) as outContext =>\n      let maybeGpuSurface = createGpuSurface(gpuContext);\n\n      // The gpu surface creation can fail, so we should be\n      // prepared to fall back to a CPU surface.\n      switch (maybeGpuSurface) {\n      | Some(_) as surf =>\n        Log.tracef(m =>\n          m(\"Successfully created GPU surface: %ld x %ld\", width, height)\n        );\n        (outContext, surf);\n      | None =>\n        Log.warn(\"Unable to create GPU surface; falling back to CPU surface\");\n        (None, createCpuSurface());\n      };\n    };\n\n  maybeSurface\n  |> Option.map(surface => {\n       {\n         maybeGPUContext: gpuContext,\n         surface,\n         canvas: Surface.getCanvas(surface),\n         rootTransform: None,\n         maybeWindow: context.maybeWindow,\n       }\n     });\n};\n\nlet width = ({surface, _}) => {\n  Surface.getWidth(surface);\n};\n\nlet height = ({surface, _}) => {\n  Surface.getHeight(surface);\n};\n\nlet resize = (window: Revery_Core.Window.t, v: option(t)) => {\n  switch (v) {\n  | None => None\n  | Some({maybeGPUContext, surface, _}) as v =>\n    let framebufferSize = Window.getFramebufferSize(window);\n    if (Surface.getWidth(surface) != framebufferSize.width\n        || Surface.getHeight(surface) != framebufferSize.height) {\n      Log.infof(m =>\n        m(\n          \"Resizing canvas: %dx%d->%dx%d\",\n          Surface.getWidth(surface),\n          Surface.getHeight(surface),\n          framebufferSize.width,\n          framebufferSize.height,\n        )\n      );\n      create(maybeGPUContext, window);\n    } else {\n      v;\n    };\n  };\n};\n\nlet setRootTransform = (matrix: Skia.Matrix.t, canvas: t) => {\n  canvas.rootTransform = Some(matrix);\n};\n\nlet save = (v: t) => {\n  Skia.Canvas.save(v.canvas);\n};\n\nlet restore = (v: t) => {\n  Skia.Canvas.restore(v.canvas);\n};\n\nlet flush = (v: t) => {\n  Skia.Canvas.flush(v.canvas);\n};\n\nlet translate = (v: t, x: float, y: float) => {\n  Skia.Canvas.translate(v.canvas, x, y);\n};\n\nlet clear = (~color: Skia.Color.t, v: t) => {\n  Canvas.clear(v.canvas, color);\n};\n\nlet drawLayer =\n    (~paint: Skia.Paint.t, ~layer: t, ~x: float, ~y: float, target: t) => {\n  Surface.draw(\n    ~paint=Some(paint),\n    ~canvas=target.canvas,\n    ~x,\n    ~y,\n    layer.surface,\n  );\n};\n\nlet drawPath = (~path: Skia.Path.t, ~paint: Paint.t, canvasContext: t) => {\n  Canvas.drawPath(canvasContext.canvas, path, paint);\n};\n\nlet drawRect = (~rect: Skia.Rect.t, ~paint: Paint.t, v: t) => {\n  Canvas.drawRect(v.canvas, rect, paint);\n};\n\nlet drawRectLtwh =\n    (\n      ~left: float,\n      ~top: float,\n      ~width: float,\n      ~height: float,\n      ~paint: Paint.t,\n      v: t,\n    ) => {\n  Canvas.drawRectLtwh(v.canvas, left, top, width, height, paint);\n};\n\nlet drawRoundRect =\n    (~rect: Skia.Rect.t, ~rx: float, ~ry: float, ~paint: Paint.t, v: t) => {\n  Canvas.drawRoundRect(v.canvas, rect, rx, ry, paint);\n};\n\nlet drawOval = (~rect: Skia.Rect.t, ~paint: Paint.t, v: t) => {\n  Canvas.drawOval(v.canvas, rect, paint);\n};\n\nlet drawCircle = (~x: float, ~y: float, ~radius: float, ~paint: Paint.t, v: t) => {\n  Canvas.drawCircle(v.canvas, x, y, radius, paint);\n};\n\nlet drawRRect = (v: t, rRect: Skia.RRect.t, paint) => {\n  Canvas.drawRRect(v.canvas, rRect, paint);\n};\n\nlet drawImage = (~x, ~y, ~width, ~height, ~paint=?, data: Skia.Image.t, v: t) => {\n  Canvas.drawImageRect(\n    v.canvas,\n    data,\n    None,\n    Rect.makeLtrb(x, y, x +. width, y +. height),\n    paint,\n  );\n};\n\nlet drawText = (~paint, ~x=0., ~y=0., ~text, v: t) => {\n  Canvas.drawText(v.canvas, text, x, y, paint);\n};\n\nlet _topMatrix = Skia.Matrix.make();\nlet setMatrix = (v: t, mat: Skia.Matrix.t) => {\n  switch (v.rootTransform) {\n  | None => Canvas.setMatrix(v.canvas, mat)\n  | Some(rootMatrix) =>\n    Skia.Matrix.concat(_topMatrix, rootMatrix, mat);\n    Canvas.setMatrix(v.canvas, _topMatrix);\n  };\n};\n\nlet concat = (transform, context) => {\n  Canvas.concat(context.canvas, transform);\n};\n\nlet clipRect =\n    (v: t, ~clipOp: clipOp=Intersect, ~antiAlias=false, rect: Skia.Rect.t) => {\n  Canvas.clipRect(v.canvas, rect, clipOp, antiAlias);\n};\n\nlet clipRRect =\n    (v: t, ~clipOp: clipOp=Intersect, ~antiAlias=false, rRect: Skia.RRect.t) => {\n  Canvas.clipRRect(v.canvas, rRect, clipOp, antiAlias);\n};\n\nlet clipPath =\n    (v: t, ~clipOp: clipOp=Intersect, ~antiAlias=false, path: Skia.Path.t) => {\n  Canvas.clipPath(v.canvas, path, clipOp, antiAlias);\n};\n"
  },
  {
    "path": "src/Draw/DebugDraw.re",
    "content": "/**\n * DebugDraw.re\n *\n * Helper to visualize some aspects of the UI, like which element the mouse is over\n */\nopen Revery_Math;\n\ntype t = {bbox: BoundingBox2d.t};\n\nmodule Internal = {\n  let isEnabled: ref(bool) = ref(false);\n  let activeRect: ref(option(t)) = ref(None);\n\n  let paint = Skia.Paint.make();\n  Skia.Paint.setColor(paint, Skia.Color.makeArgb(50l, 255l, 0l, 0l));\n};\n\nlet enable = () => Internal.isEnabled := true;\nlet disable = () => Internal.isEnabled := false;\n\nlet isEnabled = () => Internal.isEnabled^;\n\nlet setActive = (bbox: BoundingBox2d.t) => {\n  let v: t = {bbox: bbox};\n  Internal.activeRect := Some(v);\n};\n\nlet draw = (canvas: CanvasContext.t) =>\n  if (Internal.isEnabled^) {\n    switch (Internal.activeRect^) {\n    | None => ()\n    | Some(v) =>\n      let (x0, y0, x1, y1) = BoundingBox2d.getBounds(v.bbox);\n      let rectangle = Skia.Rect.makeLtrb(x0, y0, x1, y1);\n      CanvasContext.drawRect(~rect=rectangle, ~paint=Internal.paint, canvas);\n    };\n  };\n"
  },
  {
    "path": "src/Draw/ImageResizeMode.re",
    "content": "type t =\n  | Stretch\n  | Repeat;\n"
  },
  {
    "path": "src/Draw/Revery_Draw.re",
    "content": "module CanvasContext = CanvasContext;\nmodule Skia = Skia;\n\nmodule DebugDraw = DebugDraw;\nmodule ImageResizeMode = ImageResizeMode;\nmodule Text = Text;\n"
  },
  {
    "path": "src/Draw/Text.re",
    "content": "open Revery_Font;\n\n// INTERNAL\nopen {\n       let fontMetrics = (size, skiaFace) => {\n         switch (FontCache.load(skiaFace)) {\n         // TODO: Actually get metrics\n         | Ok(font) => FontCache.getMetrics(font, size)\n         | Error(_) => FontMetrics.empty(0.)\n         };\n       };\n     };\n\nlet lineHeight = (~italic=?, family, size, weight) => {\n  let maybeSkia = Family.toSkia(~italic?, weight, family);\n  fontMetrics(size, maybeSkia).lineHeight;\n};\n\nlet ascent = (~italic=?, family, size, weight) => {\n  let maybeSkia = Family.toSkia(~italic?, weight, family);\n  fontMetrics(size, maybeSkia).ascent;\n};\n\nlet descent = (~italic=?, family, size, weight) => {\n  let maybeSkia = Family.toSkia(~italic?, weight, family);\n  fontMetrics(size, maybeSkia).descent;\n};\n\nlet charWidth =\n    (\n      ~smoothing=Smoothing.default,\n      ~italic=?,\n      ~fontFamily,\n      ~fontSize,\n      ~fontWeight,\n      uchar,\n    ) => {\n  let maybeSkia = Family.toSkia(~italic?, fontWeight, fontFamily);\n\n  switch (FontCache.load(maybeSkia)) {\n  | Ok(font) =>\n    let text = Zed_utf8.singleton(uchar);\n    FontRenderer.measure(~smoothing, font, fontSize, text).width;\n\n  | Error(_) => 0.\n  };\n};\n\ntype dimensions =\n  FontRenderer.measureResult = {\n    width: float,\n    height: float,\n  };\n\nlet dimensions =\n    (\n      ~smoothing=Smoothing.default,\n      ~italic=?,\n      ~fontFamily,\n      ~fontSize,\n      ~fontWeight,\n      text,\n    ) => {\n  let maybeSkia = Family.toSkia(~italic?, fontWeight, fontFamily);\n\n  switch (FontCache.load(maybeSkia)) {\n  // TODO: Properly implement\n  | Ok(font) => FontRenderer.measure(~smoothing, font, fontSize, text)\n\n  | Error(_) => {width: 0., height: 0.}\n  };\n};\n\nlet indexNearestOffset = (~measure, text, offset) => {\n  let length = String.length(text);\n\n  let rec loop = (~last, i) =>\n    if (i > length) {\n      i - 1;\n    } else {\n      let width = measure(String.sub(text, 0, i));\n\n      if (width > offset) {\n        let isCurrentNearest = width - offset < offset - last;\n        isCurrentNearest ? i : i - 1;\n      } else {\n        loop(~last=width, i + 1);\n      };\n    };\n\n  loop(~last=0, 1);\n};\n"
  },
  {
    "path": "src/Draw/Text.rei",
    "content": "open Revery_Font;\n\nlet lineHeight: (~italic: bool=?, Family.t, float, Weight.t) => float;\nlet ascent: (~italic: bool=?, Family.t, float, Weight.t) => float;\nlet descent: (~italic: bool=?, Family.t, float, Weight.t) => float;\n\nlet charWidth:\n  (\n    ~smoothing: Smoothing.t=?,\n    ~italic: bool=?,\n    ~fontFamily: Family.t,\n    ~fontSize: float,\n    ~fontWeight: Weight.t,\n    Uchar.t\n  ) =>\n  float;\n\ntype dimensions = {\n  width: float,\n  height: float,\n};\n\nlet dimensions:\n  (\n    ~smoothing: Smoothing.t=?,\n    ~italic: bool=?,\n    ~fontFamily: Family.t,\n    ~fontSize: float,\n    ~fontWeight: Weight.t,\n    string\n  ) =>\n  dimensions;\n\nlet indexNearestOffset: (~measure: string => int, string, int) => int;\n"
  },
  {
    "path": "src/Draw/dune",
    "content": "(library\n (name Revery_Draw)\n (public_name Revery.Draw)\n (preprocess\n  (pps lwt_ppx))\n (libraries skia brisk-reconciler lwt lwt.unix sdl2 flex rebez.lib\n   Revery_Core Revery_Font Revery_Math Revery_Zed))\n"
  },
  {
    "path": "src/Font/Discovery.re",
    "content": "/**\n    Discovery.re\n\n*/\nexception Font_not_found;\n\nlet find =\n    (\n      ~weight=FontWeight.Normal,\n      ~width=FontWidth.Normal,\n      ~italic=false,\n      family,\n    ) => {\n  let style =\n    Skia.FontStyle.make(\n      FontWeight.toInt(weight),\n      FontWidth.toInt(width),\n      italic ? Italic : Upright,\n    );\n  Skia.FontManager.matchFamilyStyle(FontManager.instance, family, style);\n};\n"
  },
  {
    "path": "src/Font/Feature.re",
    "content": "type t = Harfbuzz.feature;\ntype tag = string;\ntype position = [ | `Start | `End | `Position(int)];\n\nlet customTag: string => tag = s => s;\nlet toString: tag => string = s => s;\n\nlet make = (~tag: tag, ~value) =>\n  Harfbuzz.{tag, value, start: `Start, stop: `End};\n"
  },
  {
    "path": "src/Font/Feature.rei",
    "content": "type t = Harfbuzz.feature;\n\ntype position = [ | `Start | `End | `Position(int)];\ntype tag;\n\nlet customTag: string => tag;\nlet toString: tag => string;\n\nlet make: (~tag: tag, ~value: int) => t;\n"
  },
  {
    "path": "src/Font/Features.re",
    "content": "open Feature;\nlet accessAllAlternates: tag = customTag(\"aalt\");\nlet aboveBaseForms: tag = customTag(\"abvf\");\nlet aboveBaseMarkPositioning: tag = customTag(\"abvm\");\nlet aboveBaseSubstitutions: tag = customTag(\"abvs\");\nlet alternativeFractions: tag = customTag(\"afrc\");\nlet akhands: tag = customTag(\"akhn\");\nlet belowBaseForms: tag = customTag(\"blwf\");\nlet belowBaseMarkPositioning: tag = customTag(\"blwm\");\nlet belowBaseSubstitutions: tag = customTag(\"blws\");\nlet contextualAlternates: tag = customTag(\"calt\");\nlet caseSensitiveForms: tag = customTag(\"case\");\nlet glyphComposition: tag = customTag(\"ccmp\");\nlet conjunctFormAfterRo: tag = customTag(\"cfar\");\nlet conjunctForms: tag = customTag(\"cjct\");\nlet contextualLigatures: tag = customTag(\"clig\");\nlet centeredCjkPunctuation: tag = customTag(\"cpct\");\nlet capitalSpacing: tag = customTag(\"cpsp\");\nlet contextualSwash: tag = customTag(\"cswh\");\nlet cursivePositioning: tag = customTag(\"curs\");\nlet petiteCapitalsFromCapitals: tag = customTag(\"c2pc\");\nlet smallCapitalsFromCapitals: tag = customTag(\"c2sc\");\nlet distances: tag = customTag(\"dist\");\nlet discretionaryLigatures: tag = customTag(\"dlig\");\nlet denominators: tag = customTag(\"dnom\");\nlet dotlessForms: tag = customTag(\"dtls\");\nlet expertForms: tag = customTag(\"expt\");\nlet finalGlyphOnLineAlternates: tag = customTag(\"falt\");\nlet terminalForms2: tag = customTag(\"fin2\");\nlet terminalForms3: tag = customTag(\"fin3\");\nlet terminalForms: tag = customTag(\"fina\");\nlet flattenedAccentForms: tag = customTag(\"flac\");\nlet fractions: tag = customTag(\"frac\");\nlet fullWidths: tag = customTag(\"fwid\");\nlet halfForms: tag = customTag(\"half\");\nlet halantForms: tag = customTag(\"haln\");\nlet alternateHalfWidths: tag = customTag(\"halt\");\nlet historicalForms: tag = customTag(\"hist\");\nlet horizontalKanaAlternates: tag = customTag(\"hkna\");\nlet historicalLigatures: tag = customTag(\"hlig\");\nlet hangul: tag = customTag(\"hngl\");\nlet hojoKanjiForms: tag = customTag(\"hojo\");\nlet halfWidths: tag = customTag(\"hwid\");\nlet initialForms: tag = customTag(\"init\");\nlet isolatedForms: tag = customTag(\"isol\");\nlet italics: tag = customTag(\"ital\");\nlet justificationAlternates: tag = customTag(\"jalt\");\nlet jis78Forms: tag = customTag(\"jp78\");\nlet jis83Forms: tag = customTag(\"jp83\");\nlet jis90Forms: tag = customTag(\"jp90\");\nlet jis2004Forms: tag = customTag(\"jp04\");\nlet kerning: tag = customTag(\"kern\");\nlet leftBounds: tag = customTag(\"lfbd\");\nlet standardLigatures: tag = customTag(\"liga\");\nlet leadingJamoForms: tag = customTag(\"ljmo\");\nlet liningFigures: tag = customTag(\"lnum\");\nlet localizedForms: tag = customTag(\"locl\");\nlet leftToRightAlternates: tag = customTag(\"ltra\");\nlet leftToRightMirroredForms: tag = customTag(\"ltrm\");\nlet markPositioning: tag = customTag(\"mark\");\nlet medialForms2: tag = customTag(\"med2\");\nlet medialForms: tag = customTag(\"medi\");\nlet mathematicalGreek: tag = customTag(\"mgrk\");\nlet markToMarkPositioning: tag = customTag(\"mkmk\");\nlet markPositioningViaSubstitution: tag = customTag(\"mset\");\nlet alternateAnnotationForms: tag = customTag(\"nalt\");\nlet nlcKanjiForms: tag = customTag(\"nlck\");\nlet nuktaForms: tag = customTag(\"nukt\");\nlet numerators: tag = customTag(\"numr\");\nlet oldstyleFigures: tag = customTag(\"onum\");\nlet opticalBounds: tag = customTag(\"opbd\");\nlet ordinals: tag = customTag(\"ordn\");\nlet ornaments: tag = customTag(\"ornm\");\nlet proportionalAlternateWidths: tag = customTag(\"palt\");\nlet petiteCapitals: tag = customTag(\"pcap\");\nlet proportionalKana: tag = customTag(\"pkna\");\nlet proportionalFigures: tag = customTag(\"pnum\");\nlet preBaseForms: tag = customTag(\"pref\");\nlet preBaseSubstitutions: tag = customTag(\"pres\");\nlet postBaseForms: tag = customTag(\"pstf\");\nlet postBaseSubstitutions: tag = customTag(\"psts\");\nlet proportionalWidths: tag = customTag(\"pwid\");\nlet quarterWidths: tag = customTag(\"qwid\");\nlet randomize: tag = customTag(\"rand\");\nlet requiredContextualAlternates: tag = customTag(\"rclt\");\nlet rakarForms: tag = customTag(\"rkrf\");\nlet requiredLigatures: tag = customTag(\"rlig\");\nlet rephForms: tag = customTag(\"rphf\");\nlet rightBounds: tag = customTag(\"rtbd\");\nlet rightToLeftAlternates: tag = customTag(\"rtla\");\nlet rightToLeftMirroredForms: tag = customTag(\"rtlm\");\nlet rubyNotationForms: tag = customTag(\"ruby\");\nlet requiredVariationAlternates: tag = customTag(\"rvrn\");\nlet stylisticAlternates: tag = customTag(\"salt\");\nlet scientificInferiors: tag = customTag(\"sinf\");\nlet opticalSize: tag = customTag(\"size\");\nlet smallCapitals: tag = customTag(\"smcp\");\nlet simplifiedForms: tag = customTag(\"smpl\");\nlet stylisticSet1: tag = customTag(\"ss01\");\nlet stylisticSet2: tag = customTag(\"ss02\");\nlet stylisticSet3: tag = customTag(\"ss03\");\nlet stylisticSet4: tag = customTag(\"ss04\");\nlet stylisticSet5: tag = customTag(\"ss05\");\nlet stylisticSet6: tag = customTag(\"ss06\");\nlet stylisticSet7: tag = customTag(\"ss07\");\nlet stylisticSet8: tag = customTag(\"ss08\");\nlet stylisticSet9: tag = customTag(\"ss09\");\nlet stylisticSet10: tag = customTag(\"ss10\");\nlet stylisticSet11: tag = customTag(\"ss11\");\nlet stylisticSet12: tag = customTag(\"ss12\");\nlet stylisticSet13: tag = customTag(\"ss13\");\nlet stylisticSet14: tag = customTag(\"ss14\");\nlet stylisticSet15: tag = customTag(\"ss15\");\nlet stylisticSet16: tag = customTag(\"ss16\");\nlet stylisticSet17: tag = customTag(\"ss17\");\nlet stylisticSet18: tag = customTag(\"ss18\");\nlet stylisticSet19: tag = customTag(\"ss19\");\nlet stylisticSet20: tag = customTag(\"ss20\");\nlet mathScriptStyleAlternates: tag = customTag(\"ssty\");\nlet stretchingGlyphDecomposition: tag = customTag(\"stch\");\nlet subscript: tag = customTag(\"subs\");\nlet superscript: tag = customTag(\"sups\");\nlet swash: tag = customTag(\"swsh\");\nlet titling: tag = customTag(\"titl\");\nlet trailingJamoForms: tag = customTag(\"tjmo\");\nlet traditionalNameForms: tag = customTag(\"tnam\");\nlet tabularFigures: tag = customTag(\"tnum\");\nlet traditionalForms: tag = customTag(\"trad\");\nlet thirdWidths: tag = customTag(\"twid\");\nlet unicase: tag = customTag(\"unic\");\nlet alternateVerticalMetrics: tag = customTag(\"valt\");\nlet vattuVariants: tag = customTag(\"vatu\");\nlet verticalWriting: tag = customTag(\"vert\");\nlet alternateVerticalHalfMetrics: tag = customTag(\"vhal\");\nlet vowelJamoForms: tag = customTag(\"vjmo\");\nlet verticalKanaAlternates: tag = customTag(\"vkna\");\nlet verticalKerning: tag = customTag(\"vkrn\");\nlet proportionalAlternateVerticalMetrics: tag = customTag(\"vpal\");\nlet verticalAlternatesAndRotation: tag = customTag(\"vrt2\");\nlet verticalAlternatesForRotation: tag = customTag(\"vrtr\");\nlet slashedZero: tag = customTag(\"zero\");\nlet characterValue1: tag = customTag(\"cv01\");\nlet characterValue2: tag = customTag(\"cv02\");\nlet characterValue3: tag = customTag(\"cv03\");\nlet characterValue4: tag = customTag(\"cv04\");\nlet characterValue5: tag = customTag(\"cv05\");\nlet characterValue6: tag = customTag(\"cv06\");\nlet characterValue7: tag = customTag(\"cv07\");\nlet characterValue8: tag = customTag(\"cv08\");\nlet characterValue9: tag = customTag(\"cv09\");\nlet characterValue10: tag = customTag(\"cv10\");\nlet characterValue11: tag = customTag(\"cv11\");\nlet characterValue12: tag = customTag(\"cv12\");\nlet characterValue13: tag = customTag(\"cv13\");\nlet characterValue14: tag = customTag(\"cv14\");\nlet characterValue15: tag = customTag(\"cv15\");\nlet characterValue16: tag = customTag(\"cv16\");\nlet characterValue17: tag = customTag(\"cv17\");\nlet characterValue18: tag = customTag(\"cv18\");\nlet characterValue19: tag = customTag(\"cv19\");\nlet characterValue20: tag = customTag(\"cv20\");\nlet characterValue21: tag = customTag(\"cv21\");\nlet characterValue22: tag = customTag(\"cv22\");\nlet characterValue23: tag = customTag(\"cv23\");\nlet characterValue24: tag = customTag(\"cv24\");\nlet characterValue25: tag = customTag(\"cv25\");\nlet characterValue26: tag = customTag(\"cv26\");\nlet characterValue27: tag = customTag(\"cv27\");\nlet characterValue28: tag = customTag(\"cv28\");\nlet characterValue29: tag = customTag(\"cv29\");\nlet characterValue30: tag = customTag(\"cv30\");\nlet characterValue31: tag = customTag(\"cv31\");\nlet characterValue32: tag = customTag(\"cv32\");\nlet characterValue33: tag = customTag(\"cv33\");\nlet characterValue34: tag = customTag(\"cv34\");\nlet characterValue35: tag = customTag(\"cv35\");\nlet characterValue36: tag = customTag(\"cv36\");\nlet characterValue37: tag = customTag(\"cv37\");\nlet characterValue38: tag = customTag(\"cv38\");\nlet characterValue39: tag = customTag(\"cv39\");\nlet characterValue40: tag = customTag(\"cv40\");\nlet characterValue41: tag = customTag(\"cv41\");\nlet characterValue42: tag = customTag(\"cv42\");\nlet characterValue43: tag = customTag(\"cv43\");\nlet characterValue44: tag = customTag(\"cv44\");\nlet characterValue45: tag = customTag(\"cv45\");\nlet characterValue46: tag = customTag(\"cv46\");\nlet characterValue47: tag = customTag(\"cv47\");\nlet characterValue48: tag = customTag(\"cv48\");\nlet characterValue49: tag = customTag(\"cv49\");\nlet characterValue50: tag = customTag(\"cv50\");\nlet characterValue51: tag = customTag(\"cv51\");\nlet characterValue52: tag = customTag(\"cv52\");\nlet characterValue53: tag = customTag(\"cv53\");\nlet characterValue54: tag = customTag(\"cv54\");\nlet characterValue55: tag = customTag(\"cv55\");\nlet characterValue56: tag = customTag(\"cv56\");\nlet characterValue57: tag = customTag(\"cv57\");\nlet characterValue58: tag = customTag(\"cv58\");\nlet characterValue59: tag = customTag(\"cv59\");\nlet characterValue60: tag = customTag(\"cv60\");\nlet characterValue61: tag = customTag(\"cv61\");\nlet characterValue62: tag = customTag(\"cv62\");\nlet characterValue63: tag = customTag(\"cv63\");\nlet characterValue64: tag = customTag(\"cv64\");\nlet characterValue65: tag = customTag(\"cv65\");\nlet characterValue66: tag = customTag(\"cv66\");\nlet characterValue67: tag = customTag(\"cv67\");\nlet characterValue68: tag = customTag(\"cv68\");\nlet characterValue69: tag = customTag(\"cv69\");\nlet characterValue70: tag = customTag(\"cv70\");\nlet characterValue71: tag = customTag(\"cv71\");\nlet characterValue72: tag = customTag(\"cv72\");\nlet characterValue73: tag = customTag(\"cv73\");\nlet characterValue74: tag = customTag(\"cv74\");\nlet characterValue75: tag = customTag(\"cv75\");\nlet characterValue76: tag = customTag(\"cv76\");\nlet characterValue77: tag = customTag(\"cv77\");\nlet characterValue78: tag = customTag(\"cv78\");\nlet characterValue79: tag = customTag(\"cv79\");\nlet characterValue80: tag = customTag(\"cv80\");\nlet characterValue81: tag = customTag(\"cv81\");\nlet characterValue82: tag = customTag(\"cv82\");\nlet characterValue83: tag = customTag(\"cv83\");\nlet characterValue84: tag = customTag(\"cv84\");\nlet characterValue85: tag = customTag(\"cv85\");\nlet characterValue86: tag = customTag(\"cv86\");\nlet characterValue87: tag = customTag(\"cv87\");\nlet characterValue88: tag = customTag(\"cv88\");\nlet characterValue89: tag = customTag(\"cv89\");\nlet characterValue90: tag = customTag(\"cv90\");\nlet characterValue91: tag = customTag(\"cv91\");\nlet characterValue92: tag = customTag(\"cv92\");\nlet characterValue93: tag = customTag(\"cv93\");\nlet characterValue94: tag = customTag(\"cv94\");\nlet characterValue95: tag = customTag(\"cv95\");\nlet characterValue96: tag = customTag(\"cv96\");\nlet characterValue97: tag = customTag(\"cv97\");\nlet characterValue98: tag = customTag(\"cv98\");\nlet characterValue99: tag = customTag(\"cv99\");\n"
  },
  {
    "path": "src/Font/Features.rei",
    "content": "open Feature;\n\nlet accessAllAlternates: tag;\nlet aboveBaseForms: tag;\nlet aboveBaseMarkPositioning: tag;\nlet aboveBaseSubstitutions: tag;\nlet alternativeFractions: tag;\nlet akhands: tag;\nlet belowBaseForms: tag;\nlet belowBaseMarkPositioning: tag;\nlet belowBaseSubstitutions: tag;\nlet contextualAlternates: tag;\nlet caseSensitiveForms: tag;\nlet glyphComposition: tag;\nlet conjunctFormAfterRo: tag;\nlet conjunctForms: tag;\nlet contextualLigatures: tag;\nlet centeredCjkPunctuation: tag;\nlet capitalSpacing: tag;\nlet contextualSwash: tag;\nlet cursivePositioning: tag;\nlet petiteCapitalsFromCapitals: tag;\nlet smallCapitalsFromCapitals: tag;\nlet distances: tag;\nlet discretionaryLigatures: tag;\nlet denominators: tag;\nlet dotlessForms: tag;\nlet expertForms: tag;\nlet finalGlyphOnLineAlternates: tag;\nlet terminalForms2: tag;\nlet terminalForms3: tag;\nlet terminalForms: tag;\nlet flattenedAccentForms: tag;\nlet fractions: tag;\nlet fullWidths: tag;\nlet halfForms: tag;\nlet halantForms: tag;\nlet alternateHalfWidths: tag;\nlet historicalForms: tag;\nlet horizontalKanaAlternates: tag;\nlet historicalLigatures: tag;\nlet hangul: tag;\nlet hojoKanjiForms: tag;\nlet halfWidths: tag;\nlet initialForms: tag;\nlet isolatedForms: tag;\nlet italics: tag;\nlet justificationAlternates: tag;\nlet jis78Forms: tag;\nlet jis83Forms: tag;\nlet jis90Forms: tag;\nlet jis2004Forms: tag;\nlet kerning: tag;\nlet leftBounds: tag;\nlet standardLigatures: tag;\nlet leadingJamoForms: tag;\nlet liningFigures: tag;\nlet localizedForms: tag;\nlet leftToRightAlternates: tag;\nlet leftToRightMirroredForms: tag;\nlet markPositioning: tag;\nlet medialForms2: tag;\nlet medialForms: tag;\nlet mathematicalGreek: tag;\nlet markToMarkPositioning: tag;\nlet markPositioningViaSubstitution: tag;\nlet alternateAnnotationForms: tag;\nlet nlcKanjiForms: tag;\nlet nuktaForms: tag;\nlet numerators: tag;\nlet oldstyleFigures: tag;\nlet opticalBounds: tag;\nlet ordinals: tag;\nlet ornaments: tag;\nlet proportionalAlternateWidths: tag;\nlet petiteCapitals: tag;\nlet proportionalKana: tag;\nlet proportionalFigures: tag;\nlet preBaseForms: tag;\nlet preBaseSubstitutions: tag;\nlet postBaseForms: tag;\nlet postBaseSubstitutions: tag;\nlet proportionalWidths: tag;\nlet quarterWidths: tag;\nlet randomize: tag;\nlet requiredContextualAlternates: tag;\nlet rakarForms: tag;\nlet requiredLigatures: tag;\nlet rephForms: tag;\nlet rightBounds: tag;\nlet rightToLeftAlternates: tag;\nlet rightToLeftMirroredForms: tag;\nlet rubyNotationForms: tag;\nlet requiredVariationAlternates: tag;\nlet stylisticAlternates: tag;\nlet scientificInferiors: tag;\nlet opticalSize: tag;\nlet smallCapitals: tag;\nlet simplifiedForms: tag;\nlet stylisticSet1: tag;\nlet stylisticSet2: tag;\nlet stylisticSet3: tag;\nlet stylisticSet4: tag;\nlet stylisticSet5: tag;\nlet stylisticSet6: tag;\nlet stylisticSet7: tag;\nlet stylisticSet8: tag;\nlet stylisticSet9: tag;\nlet stylisticSet10: tag;\nlet stylisticSet11: tag;\nlet stylisticSet12: tag;\nlet stylisticSet13: tag;\nlet stylisticSet14: tag;\nlet stylisticSet15: tag;\nlet stylisticSet16: tag;\nlet stylisticSet17: tag;\nlet stylisticSet18: tag;\nlet stylisticSet19: tag;\nlet stylisticSet20: tag;\nlet mathScriptStyleAlternates: tag;\nlet stretchingGlyphDecomposition: tag;\nlet subscript: tag;\nlet superscript: tag;\nlet swash: tag;\nlet titling: tag;\nlet trailingJamoForms: tag;\nlet traditionalNameForms: tag;\nlet tabularFigures: tag;\nlet traditionalForms: tag;\nlet thirdWidths: tag;\nlet unicase: tag;\nlet alternateVerticalMetrics: tag;\nlet vattuVariants: tag;\nlet verticalWriting: tag;\nlet alternateVerticalHalfMetrics: tag;\nlet vowelJamoForms: tag;\nlet verticalKanaAlternates: tag;\nlet verticalKerning: tag;\nlet proportionalAlternateVerticalMetrics: tag;\nlet verticalAlternatesAndRotation: tag;\nlet verticalAlternatesForRotation: tag;\nlet slashedZero: tag;\nlet characterValue1: tag;\nlet characterValue2: tag;\nlet characterValue3: tag;\nlet characterValue4: tag;\nlet characterValue5: tag;\nlet characterValue6: tag;\nlet characterValue7: tag;\nlet characterValue8: tag;\nlet characterValue9: tag;\nlet characterValue10: tag;\nlet characterValue11: tag;\nlet characterValue12: tag;\nlet characterValue13: tag;\nlet characterValue14: tag;\nlet characterValue15: tag;\nlet characterValue16: tag;\nlet characterValue17: tag;\nlet characterValue18: tag;\nlet characterValue19: tag;\nlet characterValue20: tag;\nlet characterValue21: tag;\nlet characterValue22: tag;\nlet characterValue23: tag;\nlet characterValue24: tag;\nlet characterValue25: tag;\nlet characterValue26: tag;\nlet characterValue27: tag;\nlet characterValue28: tag;\nlet characterValue29: tag;\nlet characterValue30: tag;\nlet characterValue31: tag;\nlet characterValue32: tag;\nlet characterValue33: tag;\nlet characterValue34: tag;\nlet characterValue35: tag;\nlet characterValue36: tag;\nlet characterValue37: tag;\nlet characterValue38: tag;\nlet characterValue39: tag;\nlet characterValue40: tag;\nlet characterValue41: tag;\nlet characterValue42: tag;\nlet characterValue43: tag;\nlet characterValue44: tag;\nlet characterValue45: tag;\nlet characterValue46: tag;\nlet characterValue47: tag;\nlet characterValue48: tag;\nlet characterValue49: tag;\nlet characterValue50: tag;\nlet characterValue51: tag;\nlet characterValue52: tag;\nlet characterValue53: tag;\nlet characterValue54: tag;\nlet characterValue55: tag;\nlet characterValue56: tag;\nlet characterValue57: tag;\nlet characterValue58: tag;\nlet characterValue59: tag;\nlet characterValue60: tag;\nlet characterValue61: tag;\nlet characterValue62: tag;\nlet characterValue63: tag;\nlet characterValue64: tag;\nlet characterValue65: tag;\nlet characterValue66: tag;\nlet characterValue67: tag;\nlet characterValue68: tag;\nlet characterValue69: tag;\nlet characterValue70: tag;\nlet characterValue71: tag;\nlet characterValue72: tag;\nlet characterValue73: tag;\nlet characterValue74: tag;\nlet characterValue75: tag;\nlet characterValue76: tag;\nlet characterValue77: tag;\nlet characterValue78: tag;\nlet characterValue79: tag;\nlet characterValue80: tag;\nlet characterValue81: tag;\nlet characterValue82: tag;\nlet characterValue83: tag;\nlet characterValue84: tag;\nlet characterValue85: tag;\nlet characterValue86: tag;\nlet characterValue87: tag;\nlet characterValue88: tag;\nlet characterValue89: tag;\nlet characterValue90: tag;\nlet characterValue91: tag;\nlet characterValue92: tag;\nlet characterValue93: tag;\nlet characterValue94: tag;\nlet characterValue95: tag;\nlet characterValue96: tag;\nlet characterValue97: tag;\nlet characterValue98: tag;\nlet characterValue99: tag;\n"
  },
  {
    "path": "src/Font/FontCache.re",
    "content": "open Revery_Core;\n\nmodule Log = (val Revery_Core.Log.withNamespace(\"Revery.FontCache\"));\n\nmodule StringFeaturesHashable = {\n  type t = (string, list(Feature.t));\n  let equal = ((str1, features1), (str2, features2)) =>\n    String.equal(str1, str2) && features1 == features2;\n  let hash = Hashtbl.hash;\n};\n\nmodule SkiaTypefaceHashable = {\n  type t = option(Skia.Typeface.t);\n  let equal =\n    Option.equal((tf1, tf2) =>\n      Skia.Typeface.getUniqueID(tf1) == Skia.Typeface.getUniqueID(tf2)\n    );\n  let hash = maybeTypeface =>\n    switch (maybeTypeface) {\n    | Some(tf) => Skia.Typeface.getUniqueID(tf) |> Int32.to_int\n    | None => 0\n    };\n};\n\nmodule UcharHashable = {\n  type t = Uchar.t;\n  let equal = Uchar.equal;\n  let hash = Uchar.hash;\n};\n\nmodule FloatHashable = {\n  type t = float;\n  let equal = Float.equal;\n  let hash = Float.hash;\n};\nmodule StringHash =\n  Hashtbl.Make({\n    type t = string;\n    let equal = String.equal;\n    let hash = Hashtbl.hash;\n  });\n\ntype fontLoaded = Event.t(unit);\nlet onFontLoaded: fontLoaded = Event.create();\n\nmodule MetricsWeighted = {\n  type t = FontMetrics.t;\n  let weight = _ => 1;\n};\n\nmodule ShapeResultWeighted = {\n  type t = ShapeResult.t;\n\n  let weight = ShapeResult.size;\n};\n\nmodule FallbackWeighted = {\n  type t = list(ShapeResult.shapeNode);\n  let weight = _ => 1;\n};\n\nmodule SkiaTypefaceWeighted = {\n  type t = option(Skia.Typeface.t);\n  let weight = _ => 1;\n};\n\nmodule MetricsCache = Lru.M.Make(FloatHashable, MetricsWeighted);\nmodule ShapeResultCache =\n  Lru.M.Make(StringFeaturesHashable, ShapeResultWeighted);\nmodule FallbackCache = Lru.M.Make(StringFeaturesHashable, FallbackWeighted);\nmodule FallbackCharacterCache =\n  Lru.M.Make(UcharHashable, SkiaTypefaceWeighted);\n\ntype t = {\n  hbFace: Harfbuzz.hb_face,\n  skiaFace: Skia.Typeface.t,\n  metricsCache: MetricsCache.t,\n  shapeCache: ShapeResultCache.t,\n  fallbackCache: FallbackCache.t,\n  fallbackCharacterCache: FallbackCharacterCache.t,\n};\n\nmodule FontWeight = {\n  type font = t;\n  type t = result(font, string);\n  let weight = _ => 1;\n};\n\nmodule FontCache = Lru.M.Make(SkiaTypefaceHashable, FontWeight);\n\nmodule Internal = {\n  let cache = FontCache.create(~initialSize=8, 64);\n};\n\nmodule Constants = {\n  let unresolvedGlyphID = 0;\n};\n\nlet skiaFaceToHarfbuzzFace = skiaFace => {\n  let stream = Skia.Typeface.toStream(skiaFace);\n  let length = Skia.Stream.getLength(stream);\n  let data = Skia.Data.makeFromStream(stream, length);\n  let bytes = Skia.Data.makeString(data);\n\n  Harfbuzz.hb_face_from_data(bytes);\n};\n\nlet load: option(Skia.Typeface.t) => result(t, string) =\n  (skiaTypeface: option(Skia.Typeface.t)) => {\n    switch (FontCache.find(skiaTypeface, Internal.cache)) {\n    | Some(v) =>\n      FontCache.promote(skiaTypeface, Internal.cache);\n      v;\n    | None =>\n      let harfbuzzFace = skiaTypeface |> Option.map(skiaFaceToHarfbuzzFace);\n      let metricsCache = MetricsCache.create(~initialSize=8, 64);\n      let shapeCache = ShapeResultCache.create(~initialSize=1024, 128 * 1024);\n      let fallbackCache = FallbackCache.create(~initialSize=1024, 128 * 1024);\n      let fallbackCharacterCache =\n        FallbackCharacterCache.create(~initialSize=1024, 128 * 1024);\n\n      let ret =\n        switch (skiaTypeface, harfbuzzFace) {\n        | (Some(skiaFace), Some(Ok(hbFace))) =>\n          Event.dispatch(onFontLoaded, ());\n          Log.infof(m =>\n            m(\"Loaded: %s\", Skia.Typeface.getFamilyName(skiaFace))\n          );\n          Ok({\n            hbFace,\n            skiaFace,\n            metricsCache,\n            shapeCache,\n            fallbackCache,\n            fallbackCharacterCache,\n          });\n        | (_, Some(Error(msg))) =>\n          Log.warn(\"Error loading typeface: \" ++ msg);\n          Error(\"Error loading typeface: \" ++ msg);\n        | (None, _) =>\n          Log.warn(\"Error loading typeface (skia)\");\n          Error(\"Error loading typeface.\");\n        | (_, None) =>\n          Log.warn(\"Error loading typeface (harfbuzz)\");\n          Error(\"Error loading typeface\");\n        };\n\n      FontCache.add(skiaTypeface, ret, Internal.cache);\n      FontCache.trim(Internal.cache);\n      ret;\n    };\n  };\n\nlet getMetrics: (t, float) => FontMetrics.t =\n  ({skiaFace, metricsCache, _}, size) => {\n    switch (MetricsCache.find(size, metricsCache)) {\n    | Some(v) =>\n      MetricsCache.promote(size, metricsCache);\n      v;\n    | None =>\n      let paint = Skia.Paint.make();\n      Skia.Paint.setTypeface(paint, skiaFace);\n      Skia.Paint.setTextSize(paint, size);\n\n      let metrics = Skia.FontMetrics.make();\n      let lineHeight = Skia.Paint.getFontMetrics(paint, metrics, 1.0);\n\n      let ret = FontMetrics.ofSkia(size, lineHeight, metrics);\n      MetricsCache.add(size, ret, metricsCache);\n      MetricsCache.trim(metricsCache);\n      ret;\n    };\n  };\n\nlet getSkiaTypeface: t => Skia.Typeface.t = font => font.skiaFace;\n\n/* [Fallback.strategy] encapsulates the logic for discovering a font, based on a character [Uchar.t] */\nmodule Fallback = {\n  type strategy = Uchar.t => option(Skia.Typeface.t);\n\n  let none = _uchar => None;\n\n  let constant = (typeface, _uchar) => Some(typeface);\n\n  let skia = ({skiaFace, fallbackCharacterCache, _}: t, uchar) => {\n    switch (FallbackCharacterCache.find(uchar, fallbackCharacterCache)) {\n    | Some(maybeTypeface) =>\n      FallbackCharacterCache.promote(uchar, fallbackCharacterCache);\n      maybeTypeface;\n    | None =>\n      let familyName = skiaFace |> Skia.Typeface.getFamilyName;\n      let maybeTypeface =\n        Skia.FontManager.matchFamilyStyleCharacter(\n          FontManager.instance,\n          familyName,\n          skiaFace |> Skia.Typeface.getFontStyle,\n          [Environment.userLocale],\n          uchar,\n        );\n      Log.debugf(m =>\n        m(\n          \"Unresolved glyph: character : U+%04X font: %s\",\n          Uchar.to_int(uchar),\n          familyName,\n        )\n      );\n      FallbackCharacterCache.add(\n        uchar,\n        maybeTypeface,\n        fallbackCharacterCache,\n      );\n      FallbackCharacterCache.trim(fallbackCharacterCache);\n      maybeTypeface;\n    };\n  };\n\n  let custom = (f: strategy) => f;\n};\n\nlet generateShapes:\n  (~fallback: Fallback.strategy, ~features: list(Feature.t), t, string) =>\n  list(ShapeResult.shapeNode) =\n  (~fallback, ~features, font, str) => {\n    let fallbackFor = (~byteOffset, str) => {\n      Log.debugf(m =>\n        m(\n          \"Resolving fallback for: %s at byte offset %d - source font is: %s\",\n          str,\n          byteOffset,\n          font.skiaFace |> Skia.Typeface.getFamilyName,\n        )\n      );\n      let maybeUchar =\n        try(Some(Zed_utf8.extract(str, byteOffset))) {\n        | exn =>\n          Log.debugf(m =>\n            m(\"Unable to get uchar from string: %s\", Printexc.to_string(exn))\n          );\n          None;\n        };\n      Option.bind(maybeUchar, uchar\n        // Only fallback if the character is non-ASCII (UTF-8)\n        =>\n          if (Uchar.to_int(uchar) > 256) {\n            fallback(uchar);\n          } else {\n            None;\n          }\n        )\n      |> (\n        fun\n        | Some(_) as font => {\n            load(font);\n          }\n        | None => {\n            Error(\"No fallback font found\");\n          }\n      );\n    };\n\n    /* A hole is a space in a string where the current font\n       can't render the text. For instance, most standard fonts\n       don't include emojis, and Latin fonts often don't include\n       CJK characters. This module contains functions that\n       relate to the creation and resolution of these \"holes\" */\n    let rec resolveHole = (~attempts, ~acc, ~start, ~stop) =>\n      if (start >= stop) {\n        acc;\n      } else {\n        switch (fallbackFor(~byteOffset=start, str)) {\n        | Ok(fallbackFont)\n            when Skia.Typeface.equal(fallbackFont.skiaFace, font.skiaFace) =>\n          resolveHole(\n            ~acc=[\n              ShapeResult.{\n                hbFace: font.hbFace,\n                skiaFace: font.skiaFace,\n                glyphId: Constants.unresolvedGlyphID,\n                cluster: start,\n              },\n              ...acc,\n            ],\n            ~start=start + 1,\n            ~stop,\n            ~attempts=0 // Reset attempts, because we've moved to the next character\n          )\n        | Error(_) =>\n          // Just because we can't find a font for this character doesn't mean\n          // the rest of the hole can't be resolved. Here we insert the \"unknown\"\n          // glyph and try to resolve the rest of the string.\n          resolveHole(\n            ~acc=[\n              ShapeResult.{\n                hbFace: font.hbFace,\n                skiaFace: font.skiaFace,\n                glyphId: Constants.unresolvedGlyphID,\n                cluster: start,\n              },\n              ...acc,\n            ],\n            ~start=start + 1,\n            ~stop,\n            ~attempts=0 // Reset attempts, becaused we've moved to the next character\n          )\n        | Ok(fallbackFont) =>\n          Log.debugf(m =>\n            m(\n              \"Got fallback font - id: %d name: %s (from source font - id: %d %s) - attempt %d\",\n              fallbackFont.skiaFace\n              |> Skia.Typeface.getUniqueID\n              |> Int32.to_int,\n              fallbackFont.skiaFace |> Skia.Typeface.getFamilyName,\n              font.skiaFace |> Skia.Typeface.getUniqueID |> Int32.to_int,\n              font.skiaFace |> Skia.Typeface.getFamilyName,\n              attempts,\n            )\n          );\n\n          // We found a fallback font! Now we just have to shape it the same way\n          // we shape the super-string.\n          loop(~attempts=attempts + 1, ~start, ~stop, ~acc, fallbackFont);\n        };\n      }\n    and loopShapes =\n        (\n          ~attempts,\n          ~stopCluster,\n          ~acc,\n          ~holeStart=?,\n          ~index,\n          {hbFace, skiaFace, _} as font,\n          shapes,\n        ) => {\n      let resolvePossibleHole = (~stop) => {\n        switch (holeStart) {\n        | Some(start) => resolveHole(~attempts, ~acc, ~start, ~stop)\n        | None => acc\n        };\n      };\n\n      if (index == Array.length(shapes)) {\n        resolvePossibleHole(~stop=stopCluster);\n      } else {\n        let Harfbuzz.{glyphId, cluster} = shapes[index];\n\n        // If we have an unknown glyph (part of a hole), extend\n        // the current hole to encapsulate it. We cannot resolve unresolved\n        // glyphs individually since a character can span several code points,\n        // and an unresolved glyph only represents a single code point.\n        if (glyphId == Constants.unresolvedGlyphID) {\n          let holeStart = Option.value(holeStart, ~default=cluster);\n          loopShapes(\n            ~attempts,\n            ~stopCluster,\n            ~acc,\n            ~holeStart,\n            ~index=index + 1,\n            font,\n            shapes,\n          );\n        } else {\n          // Otherwise resolve any hole the preceded this one and add the\n          // current glyph to the list.\n          let acc = resolvePossibleHole(~stop=cluster);\n          let acc = [\n            ShapeResult.{hbFace, skiaFace, glyphId, cluster},\n            ...acc,\n          ];\n          loopShapes(\n            // TODO: Bring back fix! Just see if we can repro\n            ~attempts,\n            ~stopCluster,\n            ~acc,\n            ~index=index + 1,\n            font,\n            shapes,\n          );\n        };\n      };\n    }\n\n    and loop = (~attempts, ~acc, ~start, ~stop, font) =>\n      // This [attempts] counter is used to 'circuit-break' - verify\n      // we don't end up in an infinite loop. If we've tried multiple times\n      // to fallback, give up, use the unresolved glyph ID, and move on.\n      if (attempts >= 3) {\n        loop(\n          ~attempts=0,\n          ~acc=[\n            ShapeResult.{\n              hbFace: font.hbFace,\n              skiaFace: font.skiaFace,\n              glyphId: Constants.unresolvedGlyphID,\n              cluster: start,\n            },\n            ...acc,\n          ],\n          ~start=start + 1,\n          ~stop,\n          font,\n        );\n      } else {\n        Harfbuzz.hb_shape(\n          ~features,\n          ~start=`Position(start),\n          ~stop=`Position(stop),\n          font.hbFace,\n          str,\n        )\n        |> loopShapes(~attempts, ~stopCluster=stop, ~acc, ~index=0, font);\n      };\n\n    loop(~attempts=0, ~start=0, ~stop=String.length(str), ~acc=[], font);\n  };\n\nlet shape:\n  (~fallback: Fallback.strategy=?, ~features: list(Feature.t)=?, t, string) =>\n  ShapeResult.t =\n  (~fallback=?, ~features=[], {shapeCache, _} as font, str) => {\n    // Default to skia fallback strategy\n    let fallbackToUse =\n      switch (fallback) {\n      | None => Fallback.skia(font)\n      | Some(fallbackStrategy) => fallbackStrategy\n      };\n\n    switch (ShapeResultCache.find((str, features), shapeCache)) {\n    | Some(result) =>\n      ShapeResultCache.promote((str, features), shapeCache);\n      result;\n    | None =>\n      let result =\n        generateShapes(~fallback=fallbackToUse, ~features, font, str)\n        |> ShapeResult.ofHarfbuzz;\n      ShapeResultCache.add((str, features), result, shapeCache);\n      ShapeResultCache.trim(shapeCache);\n      result;\n    };\n  };\n"
  },
  {
    "path": "src/Font/FontCache.rei",
    "content": "type t;\n\nmodule Fallback: {\n  /* [strategy] encapsulates the fallback logic for discovering a font, based on a character [Uchar.t] */\n  type strategy;\n\n  // [none] never falls back to a font (always fails)\n  let none: strategy;\n\n  // [constant(typeface)] always falls back to [typeface]\n  let constant: Skia.Typeface.t => strategy;\n\n  // [skia(font)] uses skia's [matchFamilyStyleCharacter] API to fall-back\n  let skia: t => strategy;\n\n  // [custom] provides a custom matching strategy\n  let custom: (Uchar.t => option(Skia.Typeface.t)) => strategy;\n};\n\nlet load: option(Skia.Typeface.t) => result(t, string);\n\nlet getMetrics: (t, float) => FontMetrics.t;\n\nlet getSkiaTypeface: t => Skia.Typeface.t;\n\nlet shape:\n  (~fallback: Fallback.strategy=?, ~features: list(Feature.t)=?, t, string) =>\n  ShapeResult.t;\n\nlet onFontLoaded: Revery_Core.Event.t(unit);\n"
  },
  {
    "path": "src/Font/FontFamily.re",
    "content": "type t = (~italic: bool, FontWeight.t) => option(Skia.Typeface.t);\n\nmodule FontFamilyHashable = {\n  type t = {\n    familyName: string,\n    weight: FontWeight.t,\n    italic: bool,\n  };\n\n  let equal = (a, b) =>\n    String.equal(a.familyName, b.familyName)\n    && a.weight == b.weight\n    && a.italic == b.italic;\n\n  let hash = Hashtbl.hash;\n};\n\nmodule FontDescriptorWeight = {\n  type t = option(Skia.Typeface.t);\n  let weight = _ => 1;\n};\n\nmodule FontFamilyCache = Lru.M.Make(FontFamilyHashable, FontDescriptorWeight);\n\nlet cache = FontFamilyCache.create(~initialSize=8, 64);\n\nlet system = (familyName): t =>\n  (~italic, weight) => {\n    let fontDescr: FontFamilyHashable.t = {familyName, weight, italic};\n    switch (FontFamilyCache.find(fontDescr, cache)) {\n    | Some(fd) =>\n      FontFamilyCache.promote(fontDescr, cache);\n      fd;\n    | None =>\n      let fd = Discovery.find(~weight, ~italic, familyName);\n      FontFamilyCache.add(fontDescr, fd, cache);\n      FontFamilyCache.trim(cache);\n      fd;\n    };\n  };\n\nlet fromFiles =\n    (\n      solver: (~weight: FontWeight.t, ~italic: bool) => string,\n      ~italic,\n      weight,\n    ) => {\n  let familyName = solver(~italic, ~weight);\n  let fontDescr: FontFamilyHashable.t = {familyName, weight, italic};\n  switch (FontFamilyCache.find(fontDescr, cache)) {\n  | Some(tf) =>\n    FontFamilyCache.promote(fontDescr, cache);\n    tf;\n  | None =>\n    let assetPath = Revery_Core.Environment.getAssetPath(familyName);\n    let tf = Skia.Typeface.makeFromFile(assetPath, 0);\n    FontFamilyCache.add(fontDescr, tf, cache);\n    FontFamilyCache.trim(cache);\n    tf;\n  };\n};\n\nlet fromFile = (fileName, ~italic as _, _) => {\n  let fontDescr: FontFamilyHashable.t = {\n    familyName: fileName,\n    weight: FontWeight.Normal,\n    italic: false,\n  };\n  switch (FontFamilyCache.find(fontDescr, cache)) {\n  | Some(tf) =>\n    FontFamilyCache.promote(fontDescr, cache);\n    tf;\n  | None =>\n    let assetPath = Revery_Core.Environment.getAssetPath(fileName);\n    let tf = Skia.Typeface.makeFromFile(assetPath, 0);\n    FontFamilyCache.add(fontDescr, tf, cache);\n    FontFamilyCache.trim(cache);\n    tf;\n  };\n};\n\nlet default =\n  switch (Revery_Core.Environment.os) {\n  | Linux(_) => system(\"Liberation Sans\")\n  | Mac(_) => system(\"System Font\")\n  | _ => system(\"Arial\")\n  };\n\nlet defaultMono =\n  switch (Revery_Core.Environment.os) {\n  | Mac(_) => system(\"Menlo\")\n  | Windows(_) => system(\"Consolas\")\n  | _ => fromFile(\"Inconsolata.otf\")\n  };\n\nlet defaultSerif =\n  switch (Revery_Core.Environment.os) {\n  | Mac(_) => system(\"Palatino\")\n  | Linux(_) => system(\"Liberation Serif\")\n  | _ => system(\"Times New Roman\")\n  };\n\nlet resolve = (~italic=false, weight, solver) =>\n  solver(~italic, weight) |> FontCache.load;\n\nlet toSkia = (~italic=false, weight, solver) => solver(~italic, weight);\n"
  },
  {
    "path": "src/Font/FontFamily.rei",
    "content": "type t;\n\nlet default: t;\nlet defaultMono: t;\nlet defaultSerif: t;\n\nlet fromFiles: ((~weight: FontWeight.t, ~italic: bool) => string) => t;\nlet fromFile: string => t;\nlet system: string => t;\n\nlet resolve:\n  (~italic: bool=?, FontWeight.t, t) => result(FontCache.t, string);\n\nlet toSkia: (~italic: bool=?, FontWeight.t, t) => option(Skia.Typeface.t);\n"
  },
  {
    "path": "src/Font/FontManager.re",
    "content": "/* FontManager.re\n   This module is so that multiple other modules can use the same font\n   manager without creating circular imports */\n\nlet instance = Skia.FontManager.makeDefault();\n"
  },
  {
    "path": "src/Font/FontMetrics.re",
    "content": "type t = {\n  height: float,\n  lineHeight: float,\n  ascent: float,\n  descent: float,\n  underlinePosition: float,\n  underlineThickness: float,\n  avgCharWidth: float,\n  maxCharWidth: float,\n};\n\nlet empty = (size: float) => {\n  height: size,\n  lineHeight: size,\n  ascent: 0.,\n  descent: 0.,\n  underlinePosition: 0.,\n  underlineThickness: 0.,\n  maxCharWidth: 0.,\n  avgCharWidth: 0.,\n};\n\nlet ofSkia = (size: float, lineHeight: float, metrics: Skia.FontMetrics.t) => {\n  let ascent = Skia.FontMetrics.getAscent(metrics);\n  let descent = Skia.FontMetrics.getDescent(metrics);\n  let underlinePosition = Skia.FontMetrics.getUnderlinePosition(metrics);\n  let underlineThickness = Skia.FontMetrics.getUnderlineThickness(metrics);\n  let maxCharWidth = Skia.FontMetrics.getMaxCharacterWidth(metrics);\n  let avgCharWidth = Skia.FontMetrics.getAvgCharacterWidth(metrics);\n  {\n    height: size,\n    lineHeight,\n    ascent,\n    descent,\n    underlinePosition,\n    underlineThickness,\n    maxCharWidth,\n    avgCharWidth,\n  };\n};\n"
  },
  {
    "path": "src/Font/FontRenderer.re",
    "content": "type measureResult = {\n  width: float,\n  height: float,\n};\n\nlet measure = {\n  let paint = Skia.Paint.make();\n  Skia.Paint.setTextEncoding(paint, GlyphId);\n\n  (~smoothing: Smoothing.t, ~features=[], font, size, text: string) => {\n    let {height, _}: FontMetrics.t = FontCache.getMetrics(font, size);\n\n    let glyphStrings =\n      text |> FontCache.shape(~features, font) |> ShapeResult.getGlyphStrings;\n\n    Smoothing.setPaint(~smoothing, paint);\n\n    Skia.Paint.setTextSize(paint, size);\n\n    let width =\n      glyphStrings\n      |> List.fold_left(\n           (acc, (skiaFace, str)) => {\n             Skia.Paint.setTypeface(paint, skiaFace);\n             acc +. Skia.Paint.measureText(paint, str, None);\n           },\n           0.,\n         );\n    {height, width};\n  };\n};\n"
  },
  {
    "path": "src/Font/FontWeight.re",
    "content": "type t =\n  | Undefined\n  | Thin\n  | UltraLight\n  | Light\n  | Normal\n  | Medium\n  | SemiBold\n  | Bold\n  | UltraBold\n  | Heavy;\n\nlet toInt = (v: t) => {\n  switch (v) {\n  | Undefined => 0\n  | Thin => 100\n  | UltraLight => 200\n  | Light => 300\n  | Normal => 400\n  | Medium => 500\n  | SemiBold => 600\n  | Bold => 700\n  | UltraBold => 800\n  | Heavy => 900\n  };\n};\n\nlet ofInt = (v: int) => {\n  switch (v) {\n  | 100 => Thin\n  | 200 => UltraLight\n  | 300 => Light\n  | 400 => Normal\n  | 500 => Medium\n  | 600 => SemiBold\n  | 700 => Bold\n  | 800 => UltraBold\n  | 900 => Heavy\n  | _ => Undefined\n  };\n};\n\nlet show = (v: t) => {\n  switch (v) {\n  | Undefined => \"Undefined\"\n  | Thin => \"Thin\"\n  | UltraLight => \"UltraLight\"\n  | Light => \"Light\"\n  | Normal => \"Normal\"\n  | Medium => \"Medium\"\n  | SemiBold => \"SemiBold\"\n  | Bold => \"Bold\"\n  | UltraBold => \"UltraBold\"\n  | Heavy => \"Heavy\"\n  };\n};\n"
  },
  {
    "path": "src/Font/FontWidth.re",
    "content": "type t =\n  | Undefined\n  | UltraCondensed\n  | ExtraCondensed\n  | Condensed\n  | SemiCondensed\n  | Normal\n  | SemiExpanded\n  | Expanded\n  | ExtraExpanded\n  | UltraExpanded;\n\nlet toInt = (v: t) => {\n  switch (v) {\n  | Undefined => 0\n  | UltraCondensed => 1\n  | ExtraCondensed => 2\n  | Condensed => 3\n  | SemiCondensed => 4\n  | Normal => 5\n  | SemiExpanded => 6\n  | Expanded => 7\n  | ExtraExpanded => 8\n  | UltraExpanded => 9\n  };\n};\n\nlet ofInt = (v: int) => {\n  switch (v) {\n  | 1 => UltraCondensed\n  | 2 => ExtraCondensed\n  | 3 => Condensed\n  | 4 => SemiCondensed\n  | 5 => Normal\n  | 6 => SemiExpanded\n  | 7 => Expanded\n  | 8 => ExtraExpanded\n  | 9 => UltraExpanded\n  | _ => Undefined\n  };\n};\n\nlet show = (v: t) => {\n  switch (v) {\n  | Undefined => \"Undefined\"\n  | UltraCondensed => \"UltraCondensed\"\n  | ExtraCondensed => \"ExtraCondensed\"\n  | Condensed => \"Condensed\"\n  | SemiCondensed => \"SemiCondensed\"\n  | Normal => \"Normal\"\n  | SemiExpanded => \"SemiExpanded\"\n  | Expanded => \"Expanded\"\n  | ExtraExpanded => \"ExtraExpanded\"\n  | UltraExpanded => \"UltraExpanded\"\n  };\n};\n"
  },
  {
    "path": "src/Font/Revery_Font.re",
    "content": "/**\n    Revery_Font.re\n\n    Module exposing font-related functionality, like:\n    - Discovering fonts\n    - Loading fonts\n*/\nmodule Weight = FontWeight;\nmodule Width = FontWidth;\nmodule FontMetrics = FontMetrics;\nmodule FontCache = FontCache;\nmodule FontRenderer = FontRenderer;\nmodule ShapeResult = ShapeResult;\nmodule Smoothing = Smoothing;\nmodule Family = FontFamily;\nmodule Feature = Feature;\nmodule Features = Features;\n\ntype t = FontCache.t;\n\ntype measureResult = FontRenderer.measureResult;\n\nlet load = FontCache.load;\nlet getMetrics = FontCache.getMetrics;\nlet getSkiaTypeface = FontCache.getSkiaTypeface;\nlet measure = FontRenderer.measure;\nlet shape = FontCache.shape;\n\nmodule Discovery = {\n  type t = option(Skia.Typeface.t);\n\n  let find = Discovery.find;\n};\n"
  },
  {
    "path": "src/Font/ShapeResult.re",
    "content": "type shapeNode = {\n  hbFace: Harfbuzz.hb_face,\n  skiaFace: Skia.Typeface.t,\n  glyphId: int,\n  cluster: int,\n};\n\ntype t = {glyphStrings: list((Skia.Typeface.t, string))};\n\nlet size = ({glyphStrings, _}) =>\n  glyphStrings\n  |> List.fold_left((acc, (_, str)) => acc + String.length(str), 0);\n\nlet bitsFromGlyph = glyphId => {\n  let lowBit = glyphId land 255 |> Char.chr |> String.make(1);\n  let highBit = (glyphId land 255 lsl 8) lsr 8 |> Char.chr |> String.make(1);\n  (lowBit, highBit);\n};\n\nlet ofHarfbuzz: list(shapeNode) => t =\n  nodes => {\n    let rec loop = (~nodes, ~strList, ~maybeTypeface) =>\n      switch (nodes, maybeTypeface) {\n      | ([], Some(typeface)) => [(typeface, String.concat(\"\", strList))]\n      | ([], None) => []\n      | ([{skiaFace, glyphId, _}, ...rest], Some(skFace)) =>\n        let (lowBit, highBit) = bitsFromGlyph(glyphId);\n        if (skFace === skiaFace) {\n          let strList = [lowBit, highBit, ...strList];\n          loop(~nodes=rest, ~strList, ~maybeTypeface=Some(skFace));\n        } else {\n          let newStrList = [lowBit, highBit];\n          [\n            (skFace, String.concat(\"\", strList)),\n            ...loop(\n                 ~nodes=rest,\n                 ~strList=newStrList,\n                 ~maybeTypeface=Some(skiaFace),\n               ),\n          ];\n        };\n      | ([{skiaFace, glyphId, _}, ...rest], None) =>\n        let (lowBit, highBit) = bitsFromGlyph(glyphId);\n        let newStrList = [lowBit, highBit];\n        loop(\n          ~nodes=rest,\n          ~strList=newStrList,\n          ~maybeTypeface=Some(skiaFace),\n        );\n      };\n\n    let glyphStrings =\n      loop(~nodes, ~strList=[], ~maybeTypeface=None) |> List.rev;\n\n    {glyphStrings: glyphStrings};\n  };\n\nlet getGlyphStrings = v => v.glyphStrings;\n"
  },
  {
    "path": "src/Font/Smoothing.re",
    "content": "type t =\n  | None\n  | Antialiased\n  | SubpixelAntialiased;\n\n// Default to subpixel-antialiased, as it has the most reliable\n// scaling characteristics - see Onivim 2 bugs:\n// - https://github.com/onivim/oni2/issues/1475\n// - https://github.com/onivim/oni2/issues/1592\nlet default = SubpixelAntialiased;\n\nlet setPaint = (~smoothing: t, paint: Skia.Paint.t) => {\n  switch (smoothing) {\n  | None =>\n    Skia.Paint.setAntiAlias(paint, false);\n    Skia.Paint.setSubpixelText(paint, false);\n  | Antialiased =>\n    Skia.Paint.setAntiAlias(paint, true);\n    Skia.Paint.setSubpixelText(paint, false);\n  | SubpixelAntialiased =>\n    Skia.Paint.setAntiAlias(paint, true);\n    Skia.Paint.setSubpixelText(paint, true);\n  };\n};\n"
  },
  {
    "path": "src/Font/dune",
    "content": "(library\n (name Revery_Font)\n (public_name Revery.Font)\n (preprocess\n  (pps lwt_ppx))\n (libraries harfbuzz lru lwt lwt.unix sdl2 flex rebez.lib Revery_Core\n   Revery_Math Revery_Zed))\n\n(copy_files files/*)\n\n(install\n (section bin)\n (package Revery)\n (files Inconsolata.otf))\n"
  },
  {
    "path": "src/IO/File.re",
    "content": "open Revery_Core;\nmodule Log = (val Log.withNamespace(\"Revery.IO.File\"));\n\nlet delete = filePath => {\n  switch (Fpath.of_string(filePath)) {\n  | Ok(fpath) =>\n    switch (Bos.OS.File.delete(~must_exist=true, fpath)) {\n    | Ok(_) =>\n      Log.info(\"Deleted file: \" ++ filePath);\n      Lwt.return(Ok());\n    | Error(`Msg(error)) =>\n      Log.error(\"Error deleting file: \" ++ error);\n      Lwt.return(Error(error));\n    }\n  | Error(`Msg(error)) => Lwt.return(Error(error))\n  };\n};\n\nlet write = (~path, data) => {\n  switch (Fpath.of_string(path)) {\n  | Ok(fpath) =>\n    switch (Bos.OS.File.write(fpath, data)) {\n    | Ok(_success) =>\n      Log.debug(\"Successfully wrote data to file: \" ++ path);\n      Lwt.return(Ok());\n    | Error(`Msg(error)) =>\n      Log.error(\"Error writing data to file: \" ++ error);\n      Lwt.return(Error(error));\n    }\n  | Error(`Msg(error)) => Lwt.return(Error(error))\n  };\n};\n"
  },
  {
    "path": "src/IO/File.rei",
    "content": "/**\n * write\n *\n * Takes a [path] and writes [data] to that file.\n *\n * Examples:\n * let result: Lwt.t(result(unit, string)) = File.write(~path=\"example.txt\", \"Hello World!\");\n */\nlet write: (~path: string, string) => Lwt.t(result(unit, string));\n\n/**\n * delete\n *\n * Takes a [path] and tries to delete that file.\n *\n * Examples:\n * let result: Lwt.t(result(unit, string)) = File.delete(\"example.txt\");\n */\nlet delete: string => Lwt.t(result(unit, string));\n"
  },
  {
    "path": "src/IO/Image.re",
    "content": "open Revery_Core;\n//open LwtLetOperators;\n\nmodule Log = (val Log.withNamespace(\"Revery.IO.Image\"));\n\nmodule Internal = {\n  //  let mediaTypeToFileExtension =\n  //    fun\n  //    | \"image/apng\" => \".apng\"\n  //    | \"image/bmp\" => \".bmp\"\n  //    | \"image/gif\" => \".gif\"\n  //    | \"image/x-icon\" => \".ico\"\n  //    | \"image/jpeg\" => \".jpg\"\n  //    | \"image/jpg\" => \".jpg\"\n  //    | \"image/png\" => \".png\"\n  //    | \"image/svg+xml\" => \".svg\"\n  //    | \"image/tiff\" => \".tif\"\n  //    | \"image/webp\" => \".webp\"\n  //    | _ => \".jpg\";\n  //  let textToAlphaNumeric = text =>\n  //    Str.(global_replace(regexp(\"[^a-zA-Z0-9_]\"), \"\", text));\n  //  let createFilePath = (url, ~fileExtension) => {\n  //    let fileNameCleaned =\n  //      Fpath.(\n  //        append(\n  //          v(Environment.getTempDirectory()),\n  //          v(textToAlphaNumeric(url)),\n  //        )\n  //      );\n  //\n  //    let filePath = Fpath.add_ext(fileExtension, fileNameCleaned);\n  //\n  //    Fpath.to_string(filePath);\n  //  };\n};\n\n// NOTE: The reason for these different states is to hold up any\n// additional requests until we've actually got a response\n// from the first one\n//type urlCacheItem =\n//  | Image(option(Skia.Image.t))\n//  | Pending;\n\n//type urlCache = Hashtbl.t(string, urlCacheItem);\n//let urlContextCache: urlCache = Hashtbl.create(100);\n\n// NOTE: These could be moved elsewhere\n//let fromUrl = url => {\n//  let cacheResult = Hashtbl.find_opt(urlContextCache, url);\n//\n//  let.flatMap result =\n//    switch (cacheResult) {\n//    | Some(result) =>\n//      switch (result) {\n//      | Image(maybeImage) => Lwt.return(Ok(maybeImage))\n//      | Pending => Lwt.return(Ok(None))\n//      }\n//    | None =>\n//      Hashtbl.replace(urlContextCache, url, Pending);\n//      open Fetch;\n//\n//      let.flatMapOk {Response.body, headers, _} = get(url);\n//\n//      let data = Body.toString(body);\n//      let mediaType =\n//        headers\n//        |> List.find_opt(((k, _v)) =>\n//             String.lowercase_ascii(k) == \"content-type\"\n//           )\n//        |> Option.map(((_k, v)) => v)\n//        |> Option.value(~default=\"\");\n//\n//      let fileExtension = Internal.mediaTypeToFileExtension(mediaType);\n//      let fileName = Internal.createFilePath(url, ~fileExtension);\n//\n//      let.flatMapOk _writeOk = File.write(~path=fileName, data);\n//      let maybeData = Skia.Data.makeFromFileName(fileName);\n//      let.flatMapOk _deleted = File.delete(fileName);\n//\n//      let maybeSkiaImage =\n//        Option.bind(maybeData, data =>\n//          Skia.Image.makeFromEncoded(data, None)\n//        );\n//\n//      Hashtbl.replace(urlContextCache, url, Image(maybeSkiaImage));\n//\n//      Lwt.return(Ok(maybeSkiaImage));\n//    };\n//\n//  Lwt.return(Result.value(result, ~default=None));\n//};\n\ntype cache = Hashtbl.t(string, option(Skia.Image.t));\nlet contextCache: cache = Hashtbl.create(100);\n\nlet fromAssetPath = (imagePath: string) => {\n  let imagePath = Environment.getAssetPath(imagePath);\n\n  let cacheResult = Hashtbl.find_opt(contextCache, imagePath);\n\n  switch (cacheResult) {\n  | Some(maybeCachedImage) => maybeCachedImage\n  | None =>\n    Log.info(\"Loading from path: \" ++ imagePath);\n\n    let maybeData = Skia.Data.makeFromFileName(imagePath);\n\n    Log.info(\"Got data.\");\n\n    let maybeImage =\n      Option.bind(maybeData, data => Skia.Image.makeFromEncoded(data, None));\n\n    Hashtbl.replace(contextCache, imagePath, maybeImage);\n\n    maybeImage;\n  };\n};\n"
  },
  {
    "path": "src/IO/Image.rei",
    "content": "/**\n * fromUrl\n *\n * Given a network file-path this returns a promise, holding an option(Skia.Image.t)\n *\n * Examples:\n * let maybeImage: Lwt.t(option(Skia.Image.t)) = Image.fromUrl(\"https://example.com/hello.png\");\n */\n// let fromUrl: string => Lwt.t(option(Skia.Image.t));\n\n/**\n * fromAssetPath\n *\n * Given a path, reads an image-file from disk, returning an option-type.\n *\n * Examples:\n * let maybeImage: option(Skia.Image.t) = Image.fromAssetPath(\"example.png\");\n */\nlet fromAssetPath: string => option(Skia.Image.t);\n"
  },
  {
    "path": "src/IO/LwtLetOperators.re",
    "content": "let (let.map) = (promise, fn) => Lwt.map(fn, promise);\nlet (let.mapOk) = (promise, fn) =>\n  Lwt.map(\n    fun\n    | Ok(response) => fn(response)\n    | Error(e) => e,\n    promise,\n  );\n\nlet (let.flatMap) = (promise, fn) => Lwt.bind(promise, fn);\n\nlet (let.flatMapOk) = (promise, fn) =>\n  Lwt.bind(\n    promise,\n    fun\n    | Ok(response) => fn(response)\n    | Error(e) => Lwt.return(Error(e)),\n  );\n"
  },
  {
    "path": "src/IO/Revery_IO.re",
    "content": "module LwtLetOperators = LwtLetOperators;\nmodule Image = Image;\nmodule File = File;\n"
  },
  {
    "path": "src/IO/dune",
    "content": "(library\n (name Revery_IO)\n (public_name Revery.IO)\n (preprocess\n  (pps lwt_ppx))\n (libraries bos lwt lwt.unix Revery_Core))\n"
  },
  {
    "path": "src/Lwt/Revery_Lwt.re",
    "content": "/*\n * Lwt_integration.re\n *\n * Lwt engine loop integration\n *\n * The Revery 'app-model' doesn't fit into the Lwt_main.run model of execution - which\n * blocks the main thread until the promise is complete.\n *\n * However, a slightly lower-level primitive, Lwt_engine, which Lwt_main calls,\n * lets us fit the event-queue based model into Revery.\n *\n * This module is responsible for facilitating that integration.\n */\n\n/*\n * We're using some Lwt internals, so need to disable this warning...\n */\n[@ocaml.warning \"-3\"];\nopen Revery;\n\nlet startEventLoop = () => {\n  let yielded = Lwt_sequence.create();\n\n  Tick.interval(\n    _ =>\n      Performance.bench(\"Lwt engine pump\", () => {\n        Lwt.wakeup_paused();\n        Lwt_engine.iter(false);\n\n        if (!Lwt_sequence.is_empty(yielded)) {\n          let tmp = Lwt_sequence.create();\n          Lwt_sequence.transfer_r(yielded, tmp);\n          Lwt_sequence.iter_l(wakener => Lwt.wakeup(wakener, ()), tmp);\n        };\n      }),\n    Time.zero,\n  );\n};\n"
  },
  {
    "path": "src/Lwt/dune",
    "content": "(library\n (name Revery_Lwt)\n (public_name Revery.lwt)\n (libraries Revery))\n"
  },
  {
    "path": "src/Math/Angle.re",
    "content": "type t =\n  | Degrees(float)\n  | Radians(float);\n\nlet from_degrees = angle => Degrees(angle);\nlet from_radians = angle => Radians(angle);\n"
  },
  {
    "path": "src/Math/BoundingBox2d.re",
    "content": "/* BoundingBox2d */\n/* Implementation of an Axis-Aligned Bounding Box */\n\ntype t = Skia.Rect.t;\n\nlet create = Skia.Rect.makeLtrb;\n\nlet getBounds = (v: t) => {\n  let x0 = Skia.Rect.getLeft(v);\n  let y0 = Skia.Rect.getTop(v);\n  let x1 = Skia.Rect.getRight(v);\n  let y1 = Skia.Rect.getBottom(v);\n  (x0, y0, x1, y1);\n};\n\nlet equals = (b1: t, b2: t) => {\n  let (b1x0, b1y0, b1x1, b1y1) = getBounds(b1);\n  let (b2x0, b2y0, b2x1, b2y1) = getBounds(b2);\n\n  b1x0 == b2x0 && b1y0 == b2y0 && b1x1 == b2x1 && b1y1 == b2y1;\n};\n\nlet toString = (v: t) => {\n  let (x0, y0, x1, y1) = getBounds(v);\n\n  \"x0: \"\n  ++ string_of_float(x0)\n  ++ \" y0: \"\n  ++ string_of_float(y0)\n  ++ \" x1: \"\n  ++ string_of_float(x1)\n  ++ \" y1: \"\n  ++ string_of_float(y1);\n};\n\nlet isPointInside = (~x, ~y, bbox: t) => {\n  let pX = x;\n  let pY = y;\n\n  let (x0, y0, x1, y1) = getBounds(bbox);\n\n  pX >= x0 && pX <= x1 && pY >= y0 && pY <= y1;\n};\n\nlet intersects = (b0: t, b1: t) => {\n  let (box0_minX, box0_minY, box0_maxX, box0_maxY) = getBounds(b0);\n  let (box1_minX, box1_minY, box1_maxX, box1_maxY) = getBounds(b1);\n\n  /*\n   \tFrom stackoverflow: https://stackoverflow.com/questions/306316/determine-if-two-rectangles-overlap-each-other\n   */\n  let noOverlap =\n    box0_minX > box1_maxX\n    || box1_minX > box0_maxX\n    || box0_minY > box1_maxY\n    || box1_minY > box0_maxY;\n  !noOverlap;\n};\n\nmodule Mutable = {\n  let set = Skia.Rect.Mutable.setLtrb;\n  let intersect = (~out, b0: t, b1: t) =>\n    if (intersects(b0, b1)) {\n      let box0_minX = Skia.Rect.getLeft(b0);\n      let box0_minY = Skia.Rect.getTop(b0);\n      let box0_maxX = Skia.Rect.getRight(b0);\n      let box0_maxY = Skia.Rect.getBottom(b0);\n\n      let box1_minX = Skia.Rect.getLeft(b1);\n      let box1_minY = Skia.Rect.getTop(b1);\n      let box1_maxX = Skia.Rect.getRight(b1);\n      let box1_maxY = Skia.Rect.getBottom(b1);\n\n      let minX = max(box0_minX, box1_minX);\n      let minY = max(box0_minY, box1_minY);\n      let maxX = min(box0_maxX, box1_maxX);\n      let maxY = min(box0_maxY, box1_maxY);\n\n      Skia.Rect.Mutable.setLtrb(~out, minX, minY, maxX, maxY);\n    } else {\n      Skia.Rect.Mutable.setLtrb(~out, 0., 0., 0., 0.);\n    };\n  let transform = (~out: t, bbox: t, m: Skia.Matrix.t) => {\n    let () = Skia.Matrix.mapRect(m, out, bbox);\n    ();\n  };\n};\n\nlet intersect = (b0: t, b1: t) => {\n  let out = Skia.Rect.makeLtrb(0., 0., 0., 0.);\n  Mutable.intersect(~out, b0, b1);\n  out;\n};\n\n/* TODO: For a more efficient implementation, we should consider something like:\n      http://dev.theomader.com/transform-bounding-boxes/\n      Significantly less matrix multiplications in that strategy!\n   */\nlet transform = (bbox: t, m: Skia.Matrix.t) => {\n  let out = Skia.Rect.makeEmpty();\n  Mutable.transform(~out, bbox, m);\n  out;\n};\n"
  },
  {
    "path": "src/Math/BoundingBox2d.rei",
    "content": "type t;\n\nlet create: (float, float, float, float) => t;\n\nlet equals: (t, t) => bool;\n\nlet getBounds: t => (float, float, float, float);\n\nlet intersects: (t, t) => bool;\n\nlet intersect: (t, t) => t;\n\nlet isPointInside: (~x: float, ~y: float, t) => bool;\n\nlet transform: (t, Skia.Matrix.t) => t;\n\nlet toString: t => string;\n\nmodule Mutable: {\n  let set: (~out: t, float, float, float, float) => unit;\n  let intersect: (~out: t, t, t) => unit;\n  let transform: (~out: t, t, Skia.Matrix.t) => unit;\n};\n"
  },
  {
    "path": "src/Math/Revery_Math.re",
    "content": "let pi = 4.0 *. atan(1.);\n\nmodule BoundingBox2d = BoundingBox2d;\nmodule Angle = Angle;\n\nlet clamp = (f: float, min: float, max: float) =>\n  f < min ? min : f > max ? max : f;\n\nlet interpolate = (fromValue, toValue, t) => {\n  let t = clamp(t, 0., 1.);\n\n  fromValue +. (toValue -. fromValue) *. t;\n};\n"
  },
  {
    "path": "src/Math/dune",
    "content": "(library\n (name Revery_Math)\n (public_name Revery.Math)\n (libraries skia))\n"
  },
  {
    "path": "src/Native/Dialog.re",
    "content": "[@noalloc] external alertSupported: unit => bool = \"revery_alertSupported\";\n\nexternal _openFiles:\n  (\n    ~startDirectory: option(string),\n    ~fileTypes: option(array(string)),\n    ~allowMultiple: bool,\n    ~canChooseFiles: bool,\n    ~canChooseDirectories: bool,\n    ~showHidden: bool,\n    ~buttonText: option(string),\n    ~title: option(string),\n    unit\n  ) =>\n  option(array(string)) =\n  \"revery_alertOpenFiles_bytecode\" \"revery_alertOpenFiles_native\";\n\nlet openFiles =\n    (\n      ~startDirectory=?,\n      ~fileTypes=?,\n      ~allowMultiple=false,\n      ~canChooseFiles=true,\n      ~canChooseDirectories=false,\n      ~showHidden=false,\n      ~buttonText=?,\n      ~title=?,\n      (),\n    ) =>\n  _openFiles(\n    ~startDirectory,\n    ~fileTypes,\n    ~allowMultiple,\n    ~canChooseFiles,\n    ~canChooseDirectories,\n    ~showHidden,\n    ~buttonText,\n    ~title,\n    (),\n  );\n"
  },
  {
    "path": "src/Native/Environment.re",
    "content": "/* If you change these you *must* keep environment.c up to date. */\ntype os =\n  /* Int values */\n  | Unknown // 0\n  | Android // 1\n  | IOS // 2\n  | Browser // 4\n  /* Block values */\n  | Mac({\n      major: int,\n      minor: int,\n      bugfix: int,\n    }) // 0\n  | Linux({\n      kernel: int,\n      major: int,\n      minor: int,\n      patch: int,\n    }) // 1\n  | Windows({\n      major: int,\n      minor: int,\n      build: int,\n    }); // 2\n\nexternal getOS: unit => os = \"revery_getOperatingSystem\";\n"
  },
  {
    "path": "src/Native/Gtk.re",
    "content": "%import\n\"config.h\";\n\n%ifdef\nUSE_GTK;\n\ntype widget;\n\nopen {\n       external c_createGtkWidgetFromXWindow: Sdl2.Window.nativeWindow => widget =\n         \"revery_createGtkWidgetFromXWindow\";\n       external c_gtkWidgetDestroy: widget => unit = \"revery_gtkWidgetDestroy\";\n       external c_gtkWidgetGetDepth: widget => int =\n         \"revery_gtkWidgetGetDepth\";\n\n       module WindowHashable = {\n         type t = Sdl2.Window.t;\n         let equal = (win1, win2) =>\n           Sdl2.Window.getId(win1) == Sdl2.Window.getId(win2);\n         let hash = Sdl2.Window.getId;\n       };\n\n       module WidgetResult = {\n         type t = widget;\n         let weight = c_gtkWidgetGetDepth;\n       };\n\n       module WindowWidgetCache = Lru.M.Make(WindowHashable, WidgetResult);\n\n       /* Unfortunately it's not really possible to have a corresponding\n          Gtk window as part of the state since the creation of the window\n          requires some initialization that doesn't occur until *after*\n          window creation. Conversely, we also don't want to create a\n          bunch of GtkWidgets for one window, so this table maps Sdl windows\n          to GtkWidgets.\n          */\n       let windowWidgetCache = WindowWidgetCache.create(~initialSize=8, 64);\n     };\n\nexternal eventsPending: unit => bool = \"revery_gtkEventsPending\";\nexternal mainIteration: unit => bool = \"revery_gtkMainIteration\";\n\nmodule Widget = {\n  type t = widget;\n\n  let ofSdlWindow = sdlWindow =>\n    switch (WindowWidgetCache.find(sdlWindow, windowWidgetCache)) {\n    | Some(widget) =>\n      WindowWidgetCache.promote(sdlWindow, windowWidgetCache);\n      widget;\n    | None =>\n      let gtkWidget =\n        sdlWindow\n        |> Sdl2.Window.getNativeWindow\n        |> c_createGtkWidgetFromXWindow;\n      WindowWidgetCache.add(sdlWindow, gtkWidget, windowWidgetCache);\n      WindowWidgetCache.trim(windowWidgetCache);\n      Gc.finalise(c_gtkWidgetDestroy, gtkWidget);\n      gtkWidget;\n    };\n\n  let depth = c_gtkWidgetGetDepth;\n\n  external getPath: widget => string = \"revery_gtkWidgetGetPath\";\n  external showAll: widget => unit = \"revery_gtkWidgetShowAll\";\n  external setOpacity: (widget, float) => unit = \"revery_gtkWidgetSetOpacity\";\n  external getOpacity: widget => float = \"revery_gtkWidgetGetOpacity\";\n};\n\n[%%endif];\n"
  },
  {
    "path": "src/Native/Icon.re",
    "content": "type t;\n\ntype progress =\n  | Indeterminate\n  | Determinate(float);\n\nexternal get: unit => t = \"revery_getIconHandle\";\nexternal _setProgress: (Sdl2.Window.nativeWindow, t, progress) => unit =\n  \"revery_setIconProgress\";\n\nexternal _hideProgress: (Sdl2.Window.nativeWindow, t) => unit =\n  \"revery_hideIconProgress\";\n\nlet setProgress = (w: Sdl2.Window.t, ih, progress) =>\n  _setProgress(w |> Sdl2.Window.getNativeWindow, ih, progress);\n\nlet hideProgress = (w: Sdl2.Window.t, ih) =>\n  _hideProgress(w |> Sdl2.Window.getNativeWindow, ih);\n"
  },
  {
    "path": "src/Native/Initialization.re",
    "content": "external initApp: unit => unit = \"revery_initializeApp\";\nexternal uninitApp: unit => unit = \"revery_uninitializeApp\";\n\nexternal _initWindow: Sdl2.Window.nativeWindow => unit =\n  \"revery_initializeWindow\";\nlet initWindow = (w: Sdl2.Window.t) =>\n  _initWindow(w |> Sdl2.Window.getNativeWindow);\n"
  },
  {
    "path": "src/Native/Input.re",
    "content": "%import\n\"config.h\";\n\nmodule Button = {\n  type t;\n\n  open {\n         external c_create: string => t = \"revery_buttonCreate\";\n         external c_setColor: (t, float, float, float, float) => unit =\n           \"revery_buttonSetColor\";\n       };\n\n  %if\n  defined(USE_COCOA);\n\n  let hash = NSObject.hash;\n  let equal = NSObject.equal;\n  let toString = NSObject.toString;\n\n  [%%else];\n\n  let hash = _ => 1;\n  let equal = (===);\n  let toString = _ => \"UNIMPLEMENTED\";\n\n  [%%endif];\n\n  module CallbackTbl =\n    Hashtbl.Make({\n      type nonrec t = t;\n      let equal = equal;\n      let hash = hash;\n    });\n\n  let callbackTbl = CallbackTbl.create(32);\n\n  let callbackForButton = button => {\n    let callback = CallbackTbl.find_opt(callbackTbl, button);\n    switch (callback) {\n    | Some(cb) => cb()\n    | None => ()\n    };\n  };\n\n  Callback.register(\"revery_callbackForButton\", callbackForButton);\n\n  let setColor = (~red, ~green, ~blue, ~alpha, button) =>\n    c_setColor(button, red, green, blue, alpha);\n\n  %if\n  defined(USE_COCOA);\n\n  let create = (~title, ~onClick) => {\n    let button = c_create(title);\n    CallbackTbl.replace(callbackTbl, button, onClick);\n    Gc.finalise(NSObject.release, button);\n    button;\n  };\n\n  let setFrame = (~x, ~y, ~width, ~height, button) =>\n    NSView.setFrame(button, x, y, width, height);\n\n  let getDefaultWidth = NSView.getDefaultWidth;\n  let getDefaultHeight = NSView.getDefaultHeight;\n\n  let remove = NSView.remove;\n  let displayIn = NSView.displayIn;\n\n  [%%else];\n\n  let create = (~title, ~onClick) => {\n    let button = c_create(title);\n    CallbackTbl.replace(callbackTbl, button, onClick);\n    button;\n  };\n\n  let setFrame = (~x as _, ~y as _, ~width as _, ~height as _, _) => ();\n\n  let getDefaultWidth = _ => 0;\n  let getDefaultHeight = _ => 0;\n\n  let remove = _ => ();\n  let displayIn = (_, _) => ();\n\n  [%%endif];\n};\n\nexternal openEmojiPanel: unit => unit = \"revery_openEmojiPanel\";\n"
  },
  {
    "path": "src/Native/Input.rei",
    "content": "module Button: {\n  type t;\n\n  let hash: t => int;\n  let equal: (t, t) => bool;\n  let toString: t => string;\n\n  let create: (~title: string, ~onClick: unit => unit) => t;\n\n  let setFrame: (~x: int, ~y: int, ~width: int, ~height: int, t) => unit;\n\n  let getDefaultWidth: t => int;\n  let getDefaultHeight: t => int;\n\n  let setColor:\n    (~red: float, ~green: float, ~blue: float, ~alpha: float, t) => unit;\n\n  let displayIn: (t, Sdl2.Window.t) => unit;\n  let remove: t => unit;\n};\n\nlet openEmojiPanel: unit => unit;\n"
  },
  {
    "path": "src/Native/Locale.re",
    "content": "external getUser: unit => string = \"revery_getUserLocale\";\n"
  },
  {
    "path": "src/Native/Menu.re",
    "content": "%import\n\"config.h\";\n\ntype t;\n\nmodule KeyEquivalent = {\n  type t = {\n    str: string,\n    alt: bool,\n    shift: bool,\n    ctrl: bool,\n  };\n\n  let strToKey = str =>\n    switch (str) {\n    | \"Space\"\n    | \"space\" => \" \"\n    | \"ESC\"\n    | \"esc\"\n    | \"Escape\"\n    | \"escape\" => 0x1b |> Char.chr |> String.make(1)\n    | \"TAB\"\n    | \"Tab\"\n    | \"tab\" => \"\\t\"\n    | key => key\n    };\n\n  let ofString = str => {\n    str: strToKey(str),\n    alt: false,\n    shift: false,\n    ctrl: false,\n  };\n\n  let enableAlt = (t, truth) => {...t, alt: truth};\n  let enableShift = (t, truth) => {...t, shift: truth};\n  let enableCtrl = (t, truth) => {...t, ctrl: truth};\n};\n\nmodule Item = {\n  type menu = t;\n  type t;\n\n  %if\n  defined(USE_COCOA);\n\n  let hash = NSObject.hash;\n  let equal = NSObject.equal;\n  let toString = NSObject.toString;\n\n  [%%else];\n\n  let hash = _ => 1;\n  let equal = (==);\n  let toString = _ => \"UNIMPLEMENTED\";\n\n  [%%endif];\n\n  open {\n         external c_create: (string, option(KeyEquivalent.t)) => t =\n           \"revery_menuItemCreate\";\n         external c_getSubmenu: t => option(menu) =\n           \"revery_menuItemGetSubmenu\";\n       };\n\n  module CallbackTbl =\n    Hashtbl.Make({\n      type nonrec t = t;\n      let equal = equal;\n      let hash = hash;\n    });\n\n  let callbackTbl = CallbackTbl.create(32);\n\n  let callbackForMenuItem = (fromKeyPress, item) => {\n    let callback = CallbackTbl.find_opt(callbackTbl, item);\n    switch (callback) {\n    | Some(cb) => cb(~fromKeyPress, ())\n    | None => ()\n    };\n  };\n\n  Callback.register(\"revery_callbackForMenuItem\", callbackForMenuItem);\n\n  external createSeparator: unit => t = \"revery_menuItemCreateSeparator\";\n  external setEnabled: (t, bool) => unit = \"revery_menuItemSetEnabled\";\n  external setVisible: (t, bool) => unit = \"revery_menuItemSetVisible\";\n\n  %if\n  defined(USE_COCOA);\n\n  let create = (~title, ~onClick, ~keyEquivalent=?, ()) => {\n    let menu = c_create(title, keyEquivalent);\n    CallbackTbl.replace(callbackTbl, menu, onClick);\n    Gc.finalise(NSObject.release, menu);\n    menu;\n  };\n\n  let getSubmenu = item => {\n    let submenu = c_getSubmenu(item);\n    Option.iter(submenu => Gc.finalise(NSObject.release, submenu), submenu);\n    submenu;\n  };\n\n  [%%else];\n\n  let create = (~title, ~onClick, ~keyEquivalent=?, ()) => {\n    let menu = c_create(title, keyEquivalent);\n    CallbackTbl.replace(callbackTbl, menu, onClick);\n    menu;\n  };\n\n  let getSubmenu = c_getSubmenu;\n\n  [%%endif];\n};\n\nopen {\n       external c_addSubmenu: (t, t) => unit = \"revery_menuAddSubmenu\";\n       external c_removeSubmenu: (t, t) => unit = \"revery_menuRemoveSubmenu\";\n       external c_insertSubmenuAt: (t, t, int) => unit =\n         \"revery_menuInsertSubmenuAt\";\n       external c_getMenuBarHandle: unit => t = \"revery_getMenuBarHandle\";\n       external c_create: string => t = \"revery_menuCreate\";\n       external c_nth: (t, int) => option(Item.t) = \"revery_menuNth\";\n       external c_displayIn: (t, Sdl2.Window.nativeWindow, int, int) => unit =\n         \"revery_menuDisplayIn\";\n     };\n\nexternal addItem: (t, Item.t) => unit = \"revery_menuAddItem\";\nexternal insertItemAt: (t, Item.t, int) => unit = \"revery_menuInsertItemAt\";\nexternal removeItem: (t, Item.t) => unit = \"revery_menuRemoveItem\";\nexternal clear: t => unit = \"revery_menuClear\";\nlet addSubmenu = (~parent, ~child) => c_addSubmenu(parent, child);\nlet removeSubmenu = (~parent, ~child) => c_removeSubmenu(parent, child);\nlet insertSubmenuAt = (~parent, ~child, ~idx) =>\n  c_insertSubmenuAt(parent, child, idx);\n\nlet displayIn = (~x, ~y, menu, window) =>\n  c_displayIn(menu, window |> Sdl2.Window.getNativeWindow, x, y);\n\n%if\ndefined(USE_COCOA);\n\nlet toString = NSObject.toString;\n\nlet getMenuBarHandle = () => {\n  let handle = c_getMenuBarHandle();\n  Gc.finalise(NSObject.release, handle);\n  handle;\n};\n\nlet create = title => {\n  let menu = c_create(title);\n  Gc.finalise(NSObject.release, menu);\n  menu;\n};\n\nlet nth = (menu, idx) => {\n  let item = c_nth(menu, idx);\n  Option.iter(item => Gc.finalise(NSObject.release, item), item);\n  item;\n};\n\n[%%else];\n\nlet toString = _ => \"UNIMPLEMENTED\";\nlet getMenuBarHandle = c_getMenuBarHandle;\nlet create = c_create;\nlet nth = c_nth;\n\n[%%endif];\n"
  },
  {
    "path": "src/Native/Menu.rei",
    "content": "type t;\n\nmodule KeyEquivalent: {\n  type t;\n\n  let ofString: string => t;\n\n  let enableAlt: (t, bool) => t;\n  let enableShift: (t, bool) => t;\n  let enableCtrl: (t, bool) => t;\n};\n\nmodule Item: {\n  type menu = t;\n  type t;\n\n  let hash: t => int;\n  let equal: (t, t) => bool;\n  let toString: t => string;\n\n  let create:\n    (\n      ~title: string,\n      ~onClick: (~fromKeyPress: bool, unit) => unit,\n      ~keyEquivalent: KeyEquivalent.t=?,\n      unit\n    ) =>\n    t;\n  let createSeparator: unit => t;\n  let setEnabled: (t, bool) => unit;\n  let setVisible: (t, bool) => unit;\n\n  let getSubmenu: t => option(menu);\n};\n\nlet getMenuBarHandle: unit => t;\nlet create: string => t;\nlet toString: t => string;\nlet nth: (t, int) => option(Item.t);\nlet addItem: (t, Item.t) => unit;\nlet insertItemAt: (t, Item.t, int) => unit;\nlet removeItem: (t, Item.t) => unit;\nlet addSubmenu: (~parent: t, ~child: t) => unit;\nlet removeSubmenu: (~parent: t, ~child: t) => unit;\nlet insertSubmenuAt: (~parent: t, ~child: t, ~idx: int) => unit;\nlet clear: t => unit;\nlet displayIn: (~x: int, ~y: int, t, Sdl2.Window.t) => unit;\n"
  },
  {
    "path": "src/Native/NSObject.re",
    "content": "%import\n\"config.h\";\n\n%if\ndefined(USE_COCOA) || defined(USE_UIKIT);\n\ntype t('a) = 'a;\n\n/* equal\n   Uses the NSObject isEqual selector */\nexternal equal: (t('a), t('b)) => bool = \"revery_NSObject_equal\";\n\n/* hash\n   Uses the NSObject hash selector */\nexternal hash: t('a) => int = \"revery_NSObject_hash\";\n\n/* toString\n   Uses the NSObject description selector */\nexternal toString: t('a) => string = \"revery_NSObject_toString\";\n\n/* className\n   Uses the NSStringFromClass function */\nexternal className: t('a) => string = \"revery_NSObject_className\";\n\n/* release\n   Uses the NSObject release selector */\nexternal release: t('a) => unit = \"revery_NSObject_release\";\n\n[%%endif];\n"
  },
  {
    "path": "src/Native/NSView.re",
    "content": "%import\n\"config.h\";\n\n%if\ndefined(USE_COCOA);\n\ntype t('a) = 'a;\n\nopen {\n       external c_displayIn: (t('a), Sdl2.Window.nativeWindow) => unit =\n         \"revery_NSView_displayIn\";\n     };\n\nlet displayIn = (nsView, sdlWindow) =>\n  c_displayIn(nsView, Sdl2.Window.getNativeWindow(sdlWindow));\nexternal remove: t('a) => unit = \"revery_NSView_remove\";\n\n/* Frame functions\n   These modify/read the values of the NSView's frame (a CGRect) */\n\n// Position\nexternal setFrame: (t('a), int, int, int, int) => unit =\n  \"revery_NSView_setFrame\";\n\n// Default size\nexternal getDefaultWidth: t('a) => int = \"revery_NSView_getDefaultWidth\";\nexternal getDefaultHeight: t('a) => int = \"revery_NSView_getDefaultHeight\";\n\n[%%endif];\n"
  },
  {
    "path": "src/Native/Notification.re",
    "content": "type t = {\n  title: string,\n  body: string,\n  onClick: unit => unit,\n  mute: bool,\n};\n\nlet create =\n    (~title: string, ~body: string, ~onClick=() => (), ~mute=false, ()) => {\n  title,\n  body,\n  onClick,\n  mute,\n};\n\nexternal dispatch: t => unit = \"revery_dispatchNotification\";\nexternal scheduleFromNow: (int, t) => unit =\n  \"revery_scheduleNotificationFromNow\";\n"
  },
  {
    "path": "src/Native/ReveryCocoa.h",
    "content": "void revery_alert_cocoa(void *pWin, const char *szMessage);\nchar **revery_open_files_cocoa(const char *startDir, const char *fileTypes[],\n                               int fileTypesSize, int allowMultiple,\n                               int canChooseFiles, int canChooseDirectories,\n                               int showHidden, const char* buttonText,\n                               const char* title);\n\n/* Notification functions */\nvoid revery_dispatchNotification_cocoa(const char *title, const char *body,\n                                       long onClickFunc, int mute);\nvoid revery_scheduleNotificationFromNow_cocoa(const char *title,\n        const char *body,\n        long onClickFunc, int mute,\n        int seconds);\nvoid *revery_getIconHandle_cocoa();\n\n/* Icon progress bar functions */\nvoid revery_setIconProgress_cocoa(void* dt, double progress);\nvoid revery_setIconProgressIndeterminate_cocoa(void *dt);\nvoid revery_hideIconProgress_cocoa(void* ip);\n\n/* Open functions */\nint revery_openURL_cocoa(const char *url_string);\nint revery_openFile_cocoa(const char *path_string);\n\n/* Locale functions */\nchar *revery_getUserLocale_cocoa();\n\n/* Menu functions */\nvoid *revery_getMenuBarHandle_cocoa();\nvoid *revery_menuCreate_cocoa(const char *title);\nvoid *revery_menuItemCreate_cocoa(const char *title, void *keyEquivalent);\nvoid *revery_menuNth_cocoa(void *nsMenu, int idx);\nvoid revery_menuAddItem_cocoa(void *nsMenu, void *nsMenuItem);\nvoid *revery_menuItemGetSubmenu_cocoa(void *nsMenuItem);\nvoid revery_menuAddSubmenu_cocoa(void *parent, void *child);\nvoid revery_menuRemoveSubmenu_cocoa(void *parent, void *child);\nvoid revery_menuRemoveItem_cocoa(void *nsMenu, void *nsMenuItem);\nvoid revery_menuInsertItemAt_cocoa(void *nsMenu, void *nsMenuItem, int idx);\nvoid revery_menuInsertSubmenuAt_cocoa(void *parent, void *child, int idx);\nvoid revery_menuClear_cocoa(void *nsMenu);\nvoid *revery_menuItemCreateSeparator_cocoa();\nvoid revery_menuItemSetEnabled_cocoa(void *menuItem, int truth);\nvoid revery_menuItemSetVisible_cocoa(void *menuItem, int truth);\nvoid revery_menuDisplayIn_cocoa(void *nsMenu, void *nsWindow, int x, int y);\n\n/* Input functions */\nvoid *revery_buttonCreate_cocoa(const char *title);\nvoid revery_buttonSetColor_cocoa(void *nsButton, double red, double green, double blue, double alpha);\nvoid revery_openEmojiPanel_cocoa(void);\n\n/* Window functions */\nvoid revery_windowSetUnsavedWork_cocoa(void *memory, int truth);\n"
  },
  {
    "path": "src/Native/ReveryGtk.h",
    "content": "void revery_alert_gtk(void *pWin, const char *szMessage);\nchar** revery_open_files_gtk(const char* startDir, const char* fileTypes[],\n                             int fileTypesSize, int allowMultiple,\n                             int canChooseFiles, int canChooseDirectories,\n                             int showHidden, const char* buttonText,\n                             const char* title);\nint revery_openURL_gtk(const char *url_string);\nint revery_openFile_gtk(const char *path_string);\n"
  },
  {
    "path": "src/Native/ReveryLinux.h",
    "content": "#pragma once\n\n/* Environment functions */\nvoid getOperatingSystemVersion_linux(int *kernel, int *major, int *minor, int *patch);\n"
  },
  {
    "path": "src/Native/ReveryMac.h",
    "content": "#pragma once\n\n/* Environment functions */\nvoid getOperatingSystemVersion_mac(int *major, int *minor, int *bugfix);\n"
  },
  {
    "path": "src/Native/ReveryNSObject.c",
    "content": "#ifdef __APPLE__\n#import <Cocoa/Cocoa.h>\n#import <objc/objc-runtime.h>\n\n#include <caml/alloc.h>\n#include <caml/callback.h>\n#include <caml/memory.h>\n#include <caml/mlvalues.h>\n\n#include \"utilities.h\"\n\nCAMLprim value revery_NSObject_equal(value vNSObjA, value vNSObjB) {\n    CAMLparam2(vNSObjA, vNSObjB);\n    NSObject *nsObjA = (NSObject *)revery_unwrapPointer(vNSObjA);\n    NSObject *nsObjB = (NSObject *)revery_unwrapPointer(vNSObjB);\n\n    BOOL equal = [nsObjA isEqual:nsObjB];\n\n    CAMLreturn(Val_bool(equal));\n}\n\nCAMLprim value revery_NSObject_hash(value vNSObj) {\n    CAMLparam1(vNSObj);\n    NSObject *nsObj = (NSObject *)revery_unwrapPointer(vNSObj);\n\n    NSUInteger hash = [nsObj hash];\n\n    CAMLreturn(Val_int(hash));\n}\n\nCAMLprim value revery_NSObject_toString(value vNSObj) {\n    CAMLparam1(vNSObj);\n    CAMLlocal1(vDescription);\n\n    NSObject *nsObj = (NSObject *)revery_unwrapPointer(vNSObj);\n    NSString *nsDescription = [nsObj description];\n\n    vDescription = caml_copy_string([nsDescription UTF8String]);\n\n    CAMLreturn(vDescription);\n}\n\nCAMLprim value revery_NSObject_className(value vNSObj) {\n    CAMLparam1(vNSObj);\n    CAMLlocal1(vClassName);\n\n    NSObject *nsObj = (NSObject *)revery_unwrapPointer(vNSObj);\n    NSString *nsClassName = NSStringFromClass([nsObj class]);\n    vClassName = caml_copy_string([nsClassName UTF8String]);\n\n    [nsClassName release];\n    CAMLreturn(vClassName);\n}\n\nCAMLprim value revery_NSObject_release(value vNSObj) {\n    CAMLparam1(vNSObj);\n\n    NSObject *nsObj = (NSObject *)revery_unwrapPointer(vNSObj);\n    [nsObj release];\n\n    CAMLreturn(Val_unit);\n}\n\n#endif\n"
  },
  {
    "path": "src/Native/ReveryWin32.h",
    "content": "void revery_alert_win32(void *pWin, const char *szMessage);\nvoid *revery_getIconHandle_win32();\nvoid revery_setIconProgress_win32(void *win, void *ih, float progress);\nvoid revery_setIconProgressIndeterminate_win32(void *win, void *ih);\nvoid revery_hideIconProgress_win32(void *win, void *ih);\nint revery_openURL_win32(const char *url_string);\nchar *revery_getUserLocale_win32();\nchar **revery_open_files_win32(const char *startDir, int canChooseFiles,\n                               int canChooseDirectories, const char *title);\n"
  },
  {
    "path": "src/Native/ReveryWindows.h",
    "content": "#pragma once\n\n/* Environment functions */\nvoid getOperatingSystemVersion_windows(int *major, int *minor, int *build);\n"
  },
  {
    "path": "src/Native/Revery_Native.c",
    "content": "#include <stdio.h>\n\n#include <caml/alloc.h>\n#include <caml/callback.h>\n#include <caml/memory.h>\n#include <caml/mlvalues.h>\n#include <string.h>\n\n#include \"config.h\"\n#ifdef USE_WIN32\n#include \"ReveryWin32.h\"\n#include <combaseapi.h>\n#include <windows.h>\n#elif USE_COCOA\n#include \"ReveryCocoa.h\"\n#import \"ReveryAppDelegate.h\"\n#elif USE_GTK\n#include \"ReveryGtk.h\"\n#include <gtk/gtk.h>\n#endif\n#include \"utilities.h\"\n\nCAMLprim value revery_initializeApp() {\n#ifdef USE_COCOA\n    SDLAppDelegate *sdlDelegate = [NSApp delegate];\n    ReveryAppDelegate *delegate = [ReveryAppDelegate newWithSDLDelegate:sdlDelegate];\n    [NSApp setDelegate:delegate];\n#elif USE_WIN32\n    HRESULT hr = CoInitialize(NULL);\n    if (hr != S_OK) {\n        fprintf(stderr, \"WARNING: COM initialization failed.\");\n    }\n#elif USE_GTK\n    gtk_init(0, NULL);\n#endif\n    return Val_unit;\n}\n\nCAMLprim value revery_uninitializeApp() {\n#ifdef USE_WIN32\n    CoUninitialize();\n#endif\n    return Val_unit;\n}\n\n\nCAMLprim value revery_initializeWindow(value vWin) {\n    CAMLparam1(vWin);\n    void *win = (void *)revery_unwrapPointer(vWin);\n#ifdef USE_WIN32\n    /* This flag often gets unset when the window decoration is removed.\n       This Chromium comment is the source of this fix:\n       https://chromium.googlesource.com/chromium/src.git/+/46.0.2478.0/chrome/browser/ui/views/apps/chrome_native_app_window_views_win.cc#71\n    */\n    HWND window = (HWND)win;\n    int current_style = GetWindowLong(window, GWL_STYLE);\n    SetWindowLong(window, GWL_STYLE, current_style | WS_CAPTION);\n#else\n    UNUSED(win);\n#endif\n    CAMLreturn(Val_unit);\n}\n"
  },
  {
    "path": "src/Native/Revery_Native.re",
    "content": "module Dialog = Dialog;\nmodule Environment = Environment;\nmodule Icon = Icon;\nmodule Notification = Notification;\nmodule Shell = Shell;\nmodule Locale = Locale;\nmodule Gtk = Gtk;\nmodule Menu = Menu;\nmodule Input = Input;\nmodule Window = Window;\n\ninclude Initialization;\n"
  },
  {
    "path": "src/Native/Shell.re",
    "content": "external openURL: string => bool = \"revery_openURL\";\nexternal openFile: string => bool = \"revery_openFile\";\n"
  },
  {
    "path": "src/Native/Window.re",
    "content": "open {\n       type t = Sdl2.Window.nativeWindow;\n\n       external c_setUnsavedWork: (t, bool) => unit =\n         \"revery_windowSetUnsavedWork\";\n     };\n\nlet setUnsavedWork = (window, truth) =>\n  c_setUnsavedWork(window |> Sdl2.Window.getNativeWindow, truth);\n"
  },
  {
    "path": "src/Native/caml_values.h",
    "content": "#ifndef CAMLVALUES_H\n#define CAMLVALUES_H\n\n#define Val_none Val_int(0)\n\nstatic inline value Val_some(value v) {\n    (void)Val_some;\n    CAMLparam1(v);\n    CAMLlocal1(some);\n    some = caml_alloc(1, 0);\n    Store_field(some, 0, v);\n    CAMLreturn(some);\n}\n\n#define Some_val(v) Field(v, 0)\n#endif"
  },
  {
    "path": "src/Native/cocoa/ReveryAppDelegate.c",
    "content": "#include \"config.h\"\n#ifdef USE_COCOA\n#import \"ReveryAppDelegate.h\"\n#import <Cocoa/Cocoa.h>\n\n#import \"utilities.h\"\n\n#include \"ReveryAppDelegate_func.h\"\n\n// Implementation of ReveryAppDelegate\n@implementation ReveryAppDelegate\n\n/* init - initializes the AppDelegate\n  Assigns a mutable dictionary to notificationActions\n*/\n- (id)initWithSDLDelegate:(SDLAppDelegate *)sdlDelegate {\n    self = [super init];\n    if (self) {\n        _notificationActions = [NSMutableDictionary new];\n        _sdlDelegate = sdlDelegate;\n    }\n\n    NSString *valueToSave = @\"true\";\n    [[NSUserDefaults standardUserDefaults] setObject:valueToSave forKey:@\"ApplePressAndHoldEnabled\"];\n    [[NSUserDefaults standardUserDefaults] synchronize];\n\n    return self;\n}\n\n+(id)newWithSDLDelegate:(SDLAppDelegate *)sdlDelegate {\n    return [[ReveryAppDelegate alloc] initWithSDLDelegate:sdlDelegate];\n}\n\n\n/* applicationDidFinishLaunching\n  Assigns self as the notification center delegate\n*/\n- (void)applicationDidFinishLaunching:(NSNotification *)notification {\n    [_sdlDelegate applicationDidFinishLaunching:notification];\n}\n\n/* didActivateNotification\n  Gets the long from the NSDictionary from the notification's identifier and calls it\n*/\n- (void)userNotificationCenter:(NSUserNotificationCenter *)center\n    didActivateNotification:(NSUserNotification *)notification {\n    UNUSED(center);\n    NSNumber *num = _notificationActions[[notification identifier]];\n    long ocamlFunc = [num longValue];\n    revery_caml_call(ocamlFunc);\n}\n\n/* shouldPresentNotification\n  Always presents the notification\n*/\n- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center\n    shouldPresentNotification:(NSUserNotification *)notification {\n    UNUSED(center);\n    UNUSED(notification);\n    return YES;\n}\n\n/* openFile\n  We call into the CAML function `revery_dispatchFileOpen`\n  Unfortunately because of namespacing issues with `alloc`,\n  we have to put the function in a separate file. Both OCaml\n  and Objective-C have `alloc` selector/functions, which\n  causes either build errors or runtime errors, neither of\n  which are preferable :).\n*/\n- (BOOL)application:(NSApplication *)sender\n    openFile:(NSString *)filename {\n    UNUSED(sender);\n\n    revery_appDelegate_openFile([filename UTF8String]);\n    return YES;\n}\n@end\n\n#endif\n"
  },
  {
    "path": "src/Native/cocoa/ReveryAppDelegate.h",
    "content": "#include \"config.h\"\n#ifdef USE_COCOA\n#import <Cocoa/Cocoa.h>\n#import \"SDLAppDelegate.h\"\n\n/* ReveryAppDelegate\n\n  Contains all of the delegation code for the NSApplication, as well as the\n    delegation for the NSUserNotificationCenter\n*/\n@interface ReveryAppDelegate\n    : NSObject <NSApplicationDelegate, NSUserNotificationCenterDelegate>\n      /* [notificationActions] - a mutable dictionary\n        maps NSStrings (notification identifiers) -> NSNumbers\n          (longs which represent OCaml callbacks)\n      */\n  @property(nonatomic, strong) NSMutableDictionary *notificationActions;\n@property(nonatomic, strong) SDLAppDelegate *sdlDelegate;\n\n-(id)initWithSDLDelegate:(SDLAppDelegate *)sdlDelegate;\n+(id)newWithSDLDelegate:(SDLAppDelegate *)sdlDelegate;\n@end\n#endif\n"
  },
  {
    "path": "src/Native/cocoa/ReveryAppDelegate_func.c",
    "content": "#include \"config.h\"\n#ifdef USE_COCOA\n#include \"ReveryAppDelegate_func.h\"\n\n#include <caml/callback.h>\n#include <caml/memory.h>\n#include <caml/mlvalues.h>\n#include <caml/threads.h>\n#include <caml/alloc.h>\n\n#include \"caml_values.h\"\n\n#include \"utilities.h\"\n\n#import <Cocoa/Cocoa.h>\n\nCAMLprim value _revery_appDelegate_openFile(const char *path) {\n    CAMLparam0();\n    CAMLlocal1(vPath);\n\n    static const value *dispatchFileOpen = NULL;\n    if (dispatchFileOpen == NULL) {\n        dispatchFileOpen = caml_named_value(\"revery_dispatchFileOpen\");\n    }\n    // Call only if the value was gotten\n    if (dispatchFileOpen != NULL) {\n        vPath = caml_copy_string(path);\n        caml_callback(*dispatchFileOpen, vPath);\n    }\n    CAMLreturn(Val_unit);\n}\n\nvoid revery_appDelegate_openFile(const char *path) {\n    _revery_appDelegate_openFile(path);\n}\n\n#endif\n"
  },
  {
    "path": "src/Native/cocoa/ReveryAppDelegate_func.h",
    "content": "#include \"config.h\"\n#ifdef USE_COCOA\n#ifndef ReveryAppDelegate_func_h\n#define ReveryAppDelegate_func_h\n\nvoid revery_appDelegate_openFile(const char *path);\n\n#endif\n#endif\n"
  },
  {
    "path": "src/Native/cocoa/ReveryButtonTarget.c",
    "content": "#include \"config.h\"\n#ifdef USE_COCOA\n#import \"ReveryButtonTarget.h\"\n#import <Cocoa/Cocoa.h>\n\n#include <caml/alloc.h>\n#include <caml/callback.h>\n#include <caml/memory.h>\n#include <caml/mlvalues.h>\n\n#import \"utilities.h\"\n\nstatic const camlValue *callbackForButton;\n\n@implementation ReveryButtonTarget\n\n-(id)init {\n    self = [super init];\n    if (self) {\n        if (callbackForButton == NULL) {\n            callbackForButton = caml_named_value(\"revery_callbackForButton\");\n        }\n    }\n    return self;\n}\n\n-(void)onButtonClick:(NSButton *)sender {\n    CAMLparam0();\n    CAMLlocal1(vButton);\n\n    if (callbackForButton != NULL) {\n        vButton = revery_wrapPointer(sender);\n        camlValue args[] = {vButton};\n        revery_caml_call_n(*callbackForButton, 1, args);\n    } else {\n        NSLog(@\"Unable to acquire button callback!\");\n    }\n\n    CAMLreturn0;\n}\n\n@end\n#endif\n"
  },
  {
    "path": "src/Native/cocoa/ReveryButtonTarget.h",
    "content": "#include \"config.h\"\n#ifdef USE_COCOA\n#import <Cocoa/Cocoa.h>\n\n@interface ReveryButtonTarget : NSObject\n\n-(void)onButtonClick:(NSButton *)sender;\n\n@end\n#endif\n"
  },
  {
    "path": "src/Native/cocoa/ReveryMenuItemTarget.c",
    "content": "#include \"config.h\"\n#ifdef USE_COCOA\n#import \"ReveryMenuItemTarget.h\"\n#import <Cocoa/Cocoa.h>\n\n#include <caml/alloc.h>\n#include <caml/callback.h>\n#include <caml/memory.h>\n#include <caml/mlvalues.h>\n\n#import \"utilities.h\"\n\nstatic const camlValue *callbackForMenuItem;\n\n@implementation ReveryMenuItemTarget\n\n-(id)init {\n    self = [super init];\n    if (self) {\n        if (callbackForMenuItem == NULL) {\n            callbackForMenuItem = caml_named_value(\"revery_callbackForMenuItem\");\n        }\n    }\n    return self;\n}\n\n-(void)onMenuItemClick:(NSMenuItem *)sender {\n    CAMLparam0();\n    CAMLlocal1(vMenuItem);\n\n    // Whether the menu item 'click' was due to a shortcut key\n    int fDueToKeyPress = 0;\n\n    switch (NSApp.currentEvent.type) {\n    case NSEventTypeKeyDown:\n    case NSEventTypeKeyUp:\n        fDueToKeyPress = 1;\n        break;\n    default:\n        fDueToKeyPress = 0;\n        break;\n    }\n\n    if (callbackForMenuItem != NULL) {\n        vMenuItem = revery_wrapPointer(sender);\n        camlValue args[] = {Val_int(fDueToKeyPress), vMenuItem};\n        revery_caml_call_n(*callbackForMenuItem, 2, args);\n    } else {\n        NSLog(@\"Unable to acquire menu item callback!\");\n    }\n\n    CAMLreturn0;\n}\n\n@end\n#endif\n"
  },
  {
    "path": "src/Native/cocoa/ReveryMenuItemTarget.h",
    "content": "#include \"config.h\"\n#ifdef USE_COCOA\n#import <Cocoa/Cocoa.h>\n\n@interface ReveryMenuItemTarget : NSObject\n\n-(void)onMenuItemClick:(NSMenuItem *)sender;\n\n@end\n#endif\n"
  },
  {
    "path": "src/Native/cocoa/ReveryNSView.c",
    "content": "#include \"config.h\"\n#ifdef USE_COCOA\n#import <Cocoa/Cocoa.h>\n#import \"ReveryNSViewCoords.h\"\n\n#include <caml/alloc.h>\n#include <caml/callback.h>\n#include <caml/memory.h>\n#include <caml/mlvalues.h>\n\n#include \"utilities.h\"\n\nCAMLprim value revery_NSView_remove(value vNSView) {\n    CAMLparam1(vNSView);\n    NSView *nsView = (NSView *)revery_unwrapPointer(vNSView);\n\n    [nsView removeFromSuperview];\n\n    CAMLreturn(Val_unit);\n}\n\nCAMLprim value revery_NSView_setFrame(value vNSView, value vX, value vY, value vWidth, value vHeight) {\n    CAMLparam5(vNSView, vX, vY, vWidth, vHeight);\n    NSView *nsView = (NSView *)revery_unwrapPointer(vNSView);\n    int x = Int_val(vX);\n    int y = Int_val(vY);\n    int width = Int_val(vWidth);\n    int height = Int_val(vHeight);\n\n    CGRect frame = [nsView frame];\n    frame.size.width = (CGFloat)width;\n    frame.size.height = (CGFloat)height;\n\n    [nsView setReveryX:(CGFloat)x];\n    [nsView setReveryY:(CGFloat)y];\n\n    [nsView updateFrame:frame];\n\n    CAMLreturn(Val_unit);\n}\n\nCAMLprim value revery_NSView_displayIn(value vNSView, value vNSWindow) {\n    CAMLparam2(vNSWindow, vNSView);\n    NSWindow *nsWindow = (NSWindow *)revery_unwrapPointer(vNSWindow);\n    NSView *nsView = (NSView *)revery_unwrapPointer(vNSView);\n\n    NSView *contentView = [nsWindow contentView];\n    CGRect frame = [nsView frame];\n    [contentView addSubview:nsView];\n    [nsView updateFrame: frame];\n\n    CAMLreturn(Val_unit);\n}\n\nCAMLprim value revery_NSView_getDefaultWidth(value vNSView) {\n    CAMLparam1(vNSView);\n    CAMLlocal1(vDefaultWidth);\n    NSView *nsView = (NSView *)revery_unwrapPointer(vNSView);\n\n    CGSize defaultSize = [nsView fittingSize];\n    vDefaultWidth = Val_int((int)defaultSize.width);\n\n    CAMLreturn(vDefaultWidth);\n}\n\nCAMLprim value revery_NSView_getDefaultHeight(value vNSView) {\n    CAMLparam1(vNSView);\n    CAMLlocal1(vDefaultHeight);\n    NSView *nsView = (NSView *)revery_unwrapPointer(vNSView);\n\n    CGSize defaultSize = [nsView fittingSize];\n    vDefaultHeight = Val_int((int)defaultSize.height);\n\n    CAMLreturn(vDefaultHeight);\n}\n\n#endif\n"
  },
  {
    "path": "src/Native/cocoa/ReveryNSViewCoords.c",
    "content": "#include \"config.h\"\n#ifdef USE_COCOA\n#import <Cocoa/Cocoa.h>\n#import <objc/runtime.h>\n#import \"ReveryNSViewCoords.h\"\n#include \"utilities.h\"\n\n@implementation NSView (ReveryNSViewCoords)\n\nCATEGORY_PROPERTY_GET_SET_DOUBLE(reveryX, setReveryX:);\nCATEGORY_PROPERTY_GET_SET_DOUBLE(reveryY, setReveryY:);\n\n- (void)updateFrame:(CGRect) frame {\n    NSView *superview = [self superview];\n    if (superview == NULL) {\n        return;\n    }\n\n    CGRect superFrame = [superview frame];\n\n    frame.origin.x = [self reveryX];\n    frame.origin.y = superFrame.size.height - frame.size.height - [self reveryY];\n\n    [self setFrame:frame];\n}\n\n@end\n\n#endif\n"
  },
  {
    "path": "src/Native/cocoa/ReveryNSViewCoords.h",
    "content": "#include \"config.h\"\n#ifdef USE_COCOA\n#import <Cocoa/Cocoa.h>\n\n/* ReveryNSViewCoords\n    Apple broke from the crowd and defines NSView coordinates to be\n    relative to the bottom left rather than the top. This is a category\n    to \"extend\" the NSView class (and all subclasses) so that we can store\n    \"normal\" coordinates and update the frame when the NSView has a parent.\n*/\n@interface NSView (ReveryNSViewCoords)\n\n@property double reveryX;\n@property double reveryY;\n\n- (void)updateFrame:(CGRect) frame;\n\n@end\n\n#endif\n\n"
  },
  {
    "path": "src/Native/cocoa/ReveryProgressBar.c",
    "content": "#include \"config.h\"\n#ifdef USE_COCOA\n#import <Cocoa/Cocoa.h>\n#import \"ReveryProgressBar.h\"\n\n@implementation ReveryProgressBar\n\n- (void)drawRect:(NSRect)dirtyRect {\n    (void)dirtyRect;\n    NSRect rect = NSInsetRect([self bounds], 1.0, 1.0);\n    CGFloat radius = rect.size.height / 2;\n    NSBezierPath* bezier_path = [NSBezierPath bezierPathWithRoundedRect:rect\n                                              xRadius:radius\n                                              yRadius:radius];\n    [bezier_path setLineWidth:2.0];\n    [[NSColor grayColor] set];\n    [bezier_path stroke];\n\n    rect = NSInsetRect(rect, 2.0, 2.0);\n    radius = rect.size.height / 2;\n    bezier_path = [NSBezierPath bezierPathWithRoundedRect:rect\n                                xRadius:radius\n                                yRadius:radius];\n    [bezier_path setLineWidth:1.0];\n    [bezier_path addClip];\n\n    rect.size.width =\n        floor(rect.size.width * ([self doubleValue] / [self maxValue]));\n    if (self.indeterminate) {\n        [[NSColor colorWithSRGBRed:1 green:0.84 blue:.04 alpha:1] set];\n    } else {\n        [[NSColor colorWithSRGBRed:0.39 green:0.82 blue:1 alpha:1] set];\n    }\n    NSRectFill(rect);\n}\n\n@end\n#endif"
  },
  {
    "path": "src/Native/cocoa/ReveryProgressBar.h",
    "content": "@interface ReveryProgressBar : NSProgressIndicator\n@end"
  },
  {
    "path": "src/Native/cocoa/SDLAppDelegate.h",
    "content": "#include \"config.h\"\n#ifdef USE_COCOA\n#import <Cocoa/Cocoa.h>\n\n@interface SDLAppDelegate\n    : NSObject <NSApplicationDelegate>\n@end\n\n#endif"
  },
  {
    "path": "src/Native/config/discover.re",
    "content": "open Configurator.V1;\nopen C_define;\nopen Flags;\n\ntype os =\n  | Android\n  | IOS\n  | Linux\n  | Mac\n  | Windows;\n\nlet detect_system_header = {|\n  #if __APPLE__\n    #include <TargetConditionals.h>\n    #if TARGET_OS_IPHONE\n      #define PLATFORM_NAME \"ios\"\n    #else\n      #define PLATFORM_NAME \"mac\"\n    #endif\n  #elif __linux__\n    #if __ANDROID__\n      #define PLATFORM_NAME \"android\"\n    #else\n      #define PLATFORM_NAME \"linux\"\n    #endif\n  #elif WIN32\n    #define PLATFORM_NAME \"windows\"\n  #endif\n|};\n\nlet get_os = t => {\n  let header = {\n    let file = Filename.temp_file(\"discover\", \"os.h\");\n    let fd = open_out(file);\n    output_string(fd, detect_system_header);\n    close_out(fd);\n    file;\n  };\n  let platform =\n    C_define.import(t, ~includes=[header], [(\"PLATFORM_NAME\", String)]);\n  switch (platform) {\n  | [(_, String(\"android\"))] => Android\n  | [(_, String(\"ios\"))] => IOS\n  | [(_, String(\"linux\"))] => Linux\n  | [(_, String(\"mac\"))] => Mac\n  | [(_, String(\"windows\"))] => Windows\n  | _ => failwith(\"Unknown operating system\")\n  };\n};\n\ntype feature =\n  | COCOA\n  | GTK\n  | UIKIT\n  | WIN32;\n\nlet gen_config_header = (conf, features) => {\n  let includes = value =>\n    List.exists((==)(value), features)\n      ? Value.Int(1) : Value.Switch(false);\n  let os = {\n    let os = get_os(conf);\n    switch (os) {\n    | Android => \"android\"\n    | IOS => \"ios\"\n    | Linux => \"linux\"\n    | Mac => \"mac\"\n    | Windows => \"windows\"\n    };\n  };\n  let is_os = os => get_os(conf) == os ? Value.Int(1) : Value.Switch(false);\n\n  gen_header_file(\n    conf,\n    [\n      (\"PLATFORM_NAME\", Value.String(os)),\n      (\"USE_COCOA\", includes(COCOA)),\n      (\"USE_GTK\", includes(GTK)),\n      (\"USE_UIKIT\", includes(UIKIT)),\n      (\"USE_WIN32\", includes(WIN32)),\n      (\"IS_IOS\", is_os(IOS)),\n      (\"IS_MACOS\", is_os(Mac)),\n      (\"IS_ANDROID\", is_os(Android)),\n      (\"IS_LINUX\", is_os(Linux)),\n      (\"IS_WINDOWS\", is_os(Windows)),\n    ],\n  );\n};\n\ntype config = {\n  features: list(feature),\n  libs: list(string),\n  cflags: list(string),\n  flags: list(string),\n};\n\nlet ccopt = s => [\"-ccopt\", s];\nlet cclib = s => [\"-cclib\", s];\n\nlet get_ios_config = () => {\n  features: [UIKIT],\n  cflags: [\"-I\", \".\", \"-x\", \"objective-c\"],\n  libs: [],\n  flags: [],\n};\nlet get_mac_config = () => {\n  features: [COCOA],\n  cflags: [\"-I\", \".\", \"-x\", \"objective-c\", \"-Wno-deprecated-declarations\"],\n  libs: [],\n  flags: [] @ cclib(\"-ObjC\"),\n};\n\nlet get_linux_config = c => {\n  let default = {features: [], libs: [], cflags: [], flags: []};\n  switch (Pkg_config.get(c)) {\n  | None => default\n  | Some(pc) =>\n    switch (Pkg_config.query(pc, ~package=\"gtk+-3.0\")) {\n    | None => default\n    | Some(conf) => {\n        features: [GTK],\n        libs: conf.libs,\n        cflags: conf.cflags,\n        flags: [],\n      }\n    }\n  };\n};\n\nlet get_win32_config = () => {\n  features: [WIN32],\n  cflags: [],\n  libs: [],\n  flags: [] @ cclib(\"-luuid\") @ cclib(\"-lole32\") @ cclib(\"-lcomdlg32\"),\n};\n\nmain(~name=\"discover\", t => {\n  let os = get_os(t);\n  let conf =\n    switch (os) {\n    | Android\n    | Linux => get_linux_config(t)\n    | IOS => get_ios_config()\n    | Mac => get_mac_config()\n    | Windows => get_win32_config()\n    };\n  gen_config_header(~fname=\"config.h\", t, conf.features);\n  write_sexp(\"flags.sexp\", conf.flags);\n  write_sexp(\"c_flags.sexp\", conf.cflags);\n  write_sexp(\"c_library_flags.sexp\", conf.libs);\n});\n"
  },
  {
    "path": "src/Native/config/dune",
    "content": "(executable\n (name discover)\n (libraries dune.configurator))\n"
  },
  {
    "path": "src/Native/dialog.c",
    "content": "#include <stdio.h>\n\n#include <caml/alloc.h>\n#include <caml/callback.h>\n#include <caml/memory.h>\n#include <caml/mlvalues.h>\n#include <caml/threads.h>\n\n#include \"caml_values.h\"\n\n#include <string.h>\n\n#include \"config.h\"\n#ifdef USE_WIN32\n#include \"ReveryWin32.h\"\n#elif USE_COCOA\n#include \"ReveryCocoa.h\"\n#elif USE_GTK\n#include \"ReveryGtk.h\"\n#endif\n#include \"utilities.h\"\n\nCAMLprim value revery_alertSupported() {\n#if defined(USE_WIN32) || defined(USE_COCOA) || defined(USE_GTK)\n    return Val_true;\n#else\n    return Val_false;\n#endif\n}\n\nCAMLprim value revery_alert(value vWindow, value vMessage) {\n    CAMLparam2(vWindow, vMessage);\n    const char *szMessage = String_val(vMessage);\n    void *pWin = (void *)revery_unwrapPointer(vWindow);\n\n#ifdef USE_WIN32\n    revery_alert_win32(pWin, szMessage);\n#elif USE_COCOA\n    revery_alert_cocoa(pWin, szMessage);\n#elif USE_GTK\n    revery_alert_gtk(pWin, szMessage);\n#else\n    printf(\"WARNING - Not implemented: alert\");\n    UNUSED(szMessage);\n    UNUSED(pWin);\n#endif\n    return Val_unit;\n}\n\nCAMLprim value revery_alertOpenFiles_native(\n    value vStartDirectory, value vFileTypes, value vAllowMultiple,\n    value vCanChooseFiles, value vCanChooseDirectories, value vShowHidden,\n    value vButtonText, value vTitle, value vUnit) {\n    CAMLparam5(vStartDirectory, vFileTypes, vAllowMultiple, vCanChooseFiles,\n               vCanChooseDirectories);\n    CAMLxparam3(vButtonText, vTitle, vUnit);\n\n    const char *startDirectory;\n    const char *buttonText;\n\n    // Initialize an array of filetypes\n    const char **fileTypes = NULL;\n    int fileTypesSize = 0;\n\n    // title from OCaml -> C\n    const char *title;\n    if (vTitle != Val_none)\n        title = String_val(Some_val(vTitle));\n    else\n        title = NULL;\n\n    int allowMultiple = Bool_val(vAllowMultiple);\n    int canChooseFiles = Bool_val(vCanChooseFiles);\n    int canChooseDirectories = Bool_val(vCanChooseDirectories);\n    int showHidden = Bool_val(vShowHidden);\n\n    if (vFileTypes != Val_none) {\n        CAMLlocal1(camlArr);\n        camlArr = Some_val(vFileTypes);\n        fileTypesSize = Wosize_val(camlArr);\n\n        // Allocate space for an array\n        fileTypes = (const char **)malloc(sizeof(*fileTypes) * fileTypesSize);\n\n        // Populate the array with the CAML array;\n        for (int i = 0; i < fileTypesSize; i++) {\n            const char *str = String_val(Field(camlArr, i));\n            fileTypes[i] = str;\n        }\n    }\n\n    if (vStartDirectory != Val_none) {\n        startDirectory = String_val(Some_val(vStartDirectory));\n    } else {\n        startDirectory = NULL;\n    }\n\n    if (vButtonText != Val_none) {\n        buttonText = String_val(Some_val(vButtonText));\n    } else {\n        buttonText = NULL;\n    }\n\n    char **fileList = NULL;\n\n#ifdef USE_WIN32\n    fileList = revery_open_files_win32(startDirectory, canChooseFiles,\n                                       canChooseDirectories, title);\n    (void)fileTypesSize;\n    (void)allowMultiple;\n    (void)showHidden;\n    (void)buttonText;\n#elif USE_COCOA\n    caml_release_runtime_system();\n    fileList = revery_open_files_cocoa(\n                   startDirectory, fileTypes, fileTypesSize, allowMultiple, canChooseFiles,\n                   canChooseDirectories, showHidden, buttonText, title);\n    caml_acquire_runtime_system();\n#elif USE_GTK\n    fileList = revery_open_files_gtk(\n                   startDirectory, fileTypes, fileTypesSize, allowMultiple, canChooseFiles,\n                   canChooseDirectories, showHidden, buttonText, title);\n#else\n    (void)showHidden;\n    (void)canChooseDirectories;\n    (void)canChooseFiles;\n    (void)allowMultiple;\n    (void)title;\n    (void)buttonText;\n    (void)startDirectory;\n    (void)fileList;\n#endif\n\n    if (fileList) {\n        CAMLlocal1(camlArr);\n        int len = -1;\n        while (fileList[++len] != NULL) {\n        }\n        camlArr = caml_alloc(len, 0);\n\n        for (int i = 0; i < len; i++) {\n            Store_field(camlArr, i, caml_copy_string(fileList[i]));\n            free(fileList[i]);\n        }\n\n        free(fileList);\n\n        CAMLreturn(Val_some(camlArr));\n    } else {\n        CAMLreturn(Val_none);\n    }\n}\n\nCAMLprim value revery_alertOpenFiles_bytecode(value *argv, int argn) {\n    (void)argn;\n    return revery_alertOpenFiles_native(argv[0], argv[1], argv[2], argv[3],\n                                        argv[4], argv[5], argv[6], argv[7],\n                                        argv[8]);\n}\n"
  },
  {
    "path": "src/Native/dialog.js",
    "content": "// Provides: revery_alertSupported\nfunction revery_alertSupported() {\n    return true;\n}\n\n// Provides: revery_alert\nfunction revery_alert(win, msg) {\n    joo_global_object.alert(msg);\n}\n"
  },
  {
    "path": "src/Native/dialog_cocoa.c",
    "content": "#include \"config.h\"\n#ifdef USE_COCOA\n#include <stdio.h>\n\n#import <Cocoa/Cocoa.h>\n#import \"ReveryProgressBar.h\"\n\n\nvoid revery_alert_cocoa(void *pWin, const char *szMessage) {\n    (void)pWin;\n\n    NSAlert *alert = [[NSAlert alloc] init];\n    NSString *message = [NSString stringWithUTF8String:szMessage];\n    [alert addButtonWithTitle:@\"Ok\"];\n    [alert setMessageText:@\"Alert\"];\n    [alert setInformativeText:message];\n    [alert runModal];\n    [alert release];\n    [message release];\n}\n\nconst char **revery_open_files_cocoa(\n    const char *startDir, const char *fileTypes[], int fileTypesSize,\n    int allowMultiple, int canChooseFiles, int canChooseDirectories,\n    int showHidden, const char *buttonText, const char *title) {\n\n    // Grab the key window, so we can restore focus after dialog..\n    NSWindow *keyWindow = [NSApp keyWindow];\n    /* Creates an empty NSArray of filetypes (NSString's)\n        If [fileTypes] is not null, copy the C-strings to NSString's to the\n       NSArray\n    */\n    NSArray *nsFileTypes = NULL;\n    if (fileTypes) {\n        NSMutableArray *tmpArr =\n            [[NSMutableArray alloc] initWithCapacity:fileTypesSize];\n        for (int i = 0; i < fileTypesSize; i++) {\n            // Convert char* -> NSString\n            NSString *str = [NSString stringWithCString:fileTypes[i]\n                                      encoding:NSUTF8StringEncoding];\n            [tmpArr addObject:str];\n        }\n        nsFileTypes = tmpArr;\n    }\n    // The actual dialog itself\n    NSOpenPanel *panel = [NSOpenPanel openPanel];\n    // We can directly set these because it will either be 0 or 1\n    [panel setAllowsMultipleSelection:allowMultiple];\n    [panel setCanChooseFiles:canChooseFiles];\n    [panel setCanChooseDirectories:canChooseDirectories];\n    [panel setShowsHiddenFiles:showHidden];\n\n    if (startDir) {\n        // If [startDir] is not NULL, convert it to an NSString...\n        NSString *urlString =\n            [NSString stringWithCString:startDir encoding:NSUTF8StringEncoding];\n        // ...and then to an NSURL\n        NSURL *url = [NSURL fileURLWithPath:urlString];\n        [panel setDirectoryURL:url];\n    }\n\n    if (title) {\n        [panel setMessage:[NSString stringWithCString:title\n                           encoding:NSUTF8StringEncoding]];\n        [panel setTitle:[NSString stringWithCString:title\n                         encoding:NSUTF8StringEncoding]];\n    }\n\n    if (buttonText)\n        [panel setPrompt:[NSString stringWithCString:buttonText\n                          encoding:NSUTF8StringEncoding]];\n\n    if (nsFileTypes) [panel setAllowedFileTypes:nsFileTypes];\n\n    // Run the actual panel/modal\n    NSInteger result = [panel runModal];\n\n    [panel release];\n    [nsFileTypes release];\n\n    // If a file(s) was selected...\n    if (result == NSModalResponseOK) {\n        // ...get the list of URLs\n        NSArray *urls = [panel URLs];\n        /* Create a C-array with the size + 1 of the NSArray\n            We NULL terminate it so we can get the size in the main function\n        */\n        int size = [urls count];\n        int actualSize = 0;\n        const char **ret = malloc((size + 1) * sizeof(char *));\n        // Copy the NSArray to the C-array\n        for (int i = 0; i < size; i++) {\n            NSString *tmp = [[urls objectAtIndex:i] path];\n            const char *sz= [tmp cStringUsingEncoding:NSUTF8StringEncoding];\n            // According to the Objective-C docs, the returned string\n            // is only guaranteed to be valid for the lifetime of\n            // the [NSString]:\n            // https://developer.apple.com/documentation/foundation/nsstring/1408489-cstringusingencoding?language=objc\n            // So we need to make a copy here...\n            if (sz != NULL) {\n                ret[actualSize] = strdup(sz);\n                actualSize++;\n            }\n        }\n        [urls release];\n        ret[actualSize] = NULL;\n        [keyWindow makeKeyWindow];\n        return ret;\n    } else {\n        // ...else return NULL\n        [keyWindow makeKeyWindow];\n        return NULL;\n    }\n}\n#endif\n"
  },
  {
    "path": "src/Native/dialog_gtk.c",
    "content": "#include \"config.h\"\n#ifdef USE_GTK\n#include <gtk/gtk.h>\n#include <string.h>\n\n// The callback to g_signal_connect MUST be an `activate` function\nstatic void activate(GtkApplication *app, const char *user_data) {\n    (void)app;\n    GtkWidget *dialog;\n\n    GtkDialogFlags flags = GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT;\n    dialog = gtk_message_dialog_new(NULL, flags, GTK_MESSAGE_INFO,\n                                    GTK_BUTTONS_CLOSE, \"%s\", user_data);\n    gtk_dialog_run(GTK_DIALOG(dialog));\n    gtk_widget_destroy(dialog);\n}\n\nvoid revery_alert_gtk(void *pWin, const char *szMessage) {\n    /*\n     * TODO:\n     * 1. figure out how to convert the pointer from an X11 window handle\n     * to a GTK window, see (for inspiration):\n     * https://gist.github.com/mmozeiko/2401933b1fa89e5d5bd238b33eab0465\n     *\n     * 2. Get reference to revery application, is there an existing\n     * gtk application reference when a glfw window is created that can be\n     * reused?\n     */\n    (void)pWin;\n    GtkApplication *app;\n    app = gtk_application_new(\"org.gtk.revery\", G_APPLICATION_FLAGS_NONE);\n    g_signal_connect(app, \"activate\", G_CALLBACK(activate),\n                     (gpointer)szMessage);\n    /* argv the final argument to run can be set to NULL in which case argc\n     * should be set to 0 */\n    g_application_run(G_APPLICATION(app), 0, NULL);\n    g_object_unref(app);\n}\n\nstruct FileChooserOptions {\n    const char *startDir;\n    const char **fileTypes;\n    int fileTypesSize;\n    int allowMultiple;\n    int canChooseFiles;\n    int canChooseDirectories;\n    int showHidden;\n    const char *buttonText;\n    const char *title;\n    const char **result;\n};\n\nvoid activate_filechooser(GtkApplication *app, struct FileChooserOptions *options) {\n    (void)app;\n    GtkFileChooserAction action = options->canChooseDirectories ? GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER : GTK_FILE_CHOOSER_ACTION_OPEN;\n    gint result;\n    const char *okButtonText = (options->buttonText ? options->buttonText : \"Open\");\n    const char *dialogTitle = (options->title ? options->title : \"Open File(s) and/or Folder(s)\");\n\n    GtkWidget *dialog = gtk_file_chooser_dialog_new(\n                            dialogTitle, NULL, action, \"Cancel\", GTK_RESPONSE_CANCEL, okButtonText,\n                            GTK_RESPONSE_ACCEPT, NULL);\n    GtkFileChooser *chooser = GTK_FILE_CHOOSER(dialog);\n\n    gtk_file_chooser_set_show_hidden(chooser, options->showHidden);\n    gtk_file_chooser_set_select_multiple(chooser, options->allowMultiple);\n\n    if (options->fileTypes) {\n        char *wildcard = \"*.\";\n        char *name = \"\";\n        GtkFileFilter *filter = gtk_file_filter_new();\n        for (int i = 0; i < options->fileTypesSize; i++) {\n            const char *fileType = options->fileTypes[i];\n            char *pattern = malloc(strlen(wildcard) + strlen(fileType) + 1);\n            strcpy(pattern, wildcard);\n            strcat(pattern, fileType);\n            name = g_strjoin(\" \", name, pattern, NULL);\n            gtk_file_filter_add_pattern(filter, pattern);\n        }\n        gtk_file_filter_set_name(filter, name);\n        gtk_file_chooser_add_filter(chooser, filter);\n    }\n\n    result = gtk_dialog_run(GTK_DIALOG(dialog));\n\n    if (result == GTK_RESPONSE_ACCEPT) {\n        GSList *filenames = gtk_file_chooser_get_filenames(chooser);\n        int size = g_slist_length(filenames);\n        options->result = malloc((size + 1) * sizeof(char *));\n        for (int i = 0; i < size; i++) {\n            options->result[i] = (char *) g_slist_nth_data(filenames, i);\n        }\n        options->result[size] = NULL;\n    } else {\n        options->result = NULL;\n    }\n\n    gtk_widget_destroy(dialog);\n}\n\nconst char **revery_open_files_gtk(const char *startDir, const char *fileTypes[],\n                                   int fileTypesSize, int allowMultiple,\n                                   int canChooseFiles, int canChooseDirectories,\n                                   int showHidden, const char *buttonText,\n                                   const char *title) {\n    struct FileChooserOptions options = {\n        startDir,\n        fileTypes,\n        fileTypesSize,\n        allowMultiple,\n        canChooseFiles,\n        canChooseDirectories,\n        showHidden,\n        buttonText,\n        title,\n        NULL\n    };\n\n    GtkApplication *app;\n    app = gtk_application_new(\"org.gtk.revery\", G_APPLICATION_FLAGS_NONE);\n    g_signal_connect(app, \"activate\", G_CALLBACK(activate_filechooser),\n                     (gpointer)&options);\n    /* argv the final argument to run can be set to NULL in which case argc\n     * should be set to 0 */\n    g_application_run(G_APPLICATION(app), 0, NULL);\n\n\n    g_object_unref(app);\n    return options.result;\n}\n#endif\n"
  },
  {
    "path": "src/Native/dialog_win32.c",
    "content": "#include \"config.h\"\n#ifdef USE_WIN32\n\n#include <stdio.h>\n\n#include <Shlobj.h>\n#include <Shobjidl.h>\n#include <Windows.h>\n#include <winuser.h>\n\nvoid revery_alert_win32(void *pWin, const char *szMessage) {\n    HWND hwnd = (HWND)pWin;\n\n    MessageBox(hwnd, szMessage, \"Alert\", MB_ICONWARNING | MB_OK);\n}\n\nconst char **revery_open_files_win32(const char *startDir, int canChooseFiles,\n                                     int canChooseDirectories,\n                                     const char *title) {\n    (void)startDir;\n    (void)canChooseFiles;\n\n    // Default return - null\n    const char **ret = malloc(1 * sizeof(char *));\n    ret[0] = NULL;\n\n    // Right now, we split - either choose a directory, _or_ a file\n    if (canChooseDirectories) {\n        HRESULT hr = CoInitialize(NULL);\n        if (SUCCEEDED(hr)) {\n            TCHAR szDir[MAX_PATH];\n            BROWSEINFO bInfo;\n            bInfo.hwndOwner = NULL;\n            bInfo.pidlRoot = NULL;\n            bInfo.pszDisplayName = szDir;\n            bInfo.lpszTitle = title;\n            bInfo.ulFlags = BIF_USENEWUI | BIF_NEWDIALOGSTYLE;\n            bInfo.lpfn = NULL;\n            bInfo.lParam = 0;\n            bInfo.iImage = -1;\n\n            LPITEMIDLIST lpItem = SHBrowseForFolder(&bInfo);\n            if (lpItem != NULL) {\n\n                if (SHGetPathFromIDList(lpItem, szDir)) {\n                    // free the 'default' return\n                    free(ret);\n\n                    ret = malloc(2 * sizeof(char *));\n                    ret[0] = strdup(szDir);\n                    ret[1] = NULL;\n                }\n            }\n        }\n    } else {\n        // TODO: Use the allowed files array\n        char *filter = \"All Files (*.*)\\0*.*\\0\";\n        OPENFILENAME ofn;\n        char fileName[MAX_PATH] = \"\";\n        ZeroMemory(&ofn, sizeof(ofn));\n        ofn.lStructSize = sizeof(OPENFILENAME);\n        ofn.hwndOwner = NULL;\n        ofn.lpstrFilter = filter;\n        ofn.lpstrFile = fileName;\n        ofn.nMaxFile = MAX_PATH;\n        ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;\n        ofn.lpstrDefExt = \"\";\n        if (GetOpenFileName(&ofn)) {\n            // free the 'default' return\n            free(ret);\n            ret = malloc(2 * sizeof(char *));\n            ret[0] = strdup(fileName);\n            ret[1] = NULL;\n        }\n    }\n    return ret;\n}\n#endif\n"
  },
  {
    "path": "src/Native/dune",
    "content": "(library\n (name Revery_Native)\n (public_name Revery.Native)\n (preprocess\n  (pps lwt_ppx ppx_optcomp))\n (preprocessor_deps config.h)\n (library_flags\n  (:include flags.sexp))\n (js_of_ocaml\n  (javascript_files dialog.js))\n (foreign_stubs\n  (language c)\n  (names Revery_Native dialog dialog_cocoa dialog_win32 dialog_gtk\n    notification notification_cocoa environment environment_mac\n    environment_linux environment_windows icon icon_cocoa icon_win32 shell\n    shell_cocoa shell_gtk shell_win32 locale locale_cocoa locale_win32 menu\n    menu_cocoa input input_cocoa window window_cocoa utilities ReveryGtk\n    ReveryGtk_Widget ReveryAppDelegate ReveryAppDelegate_func ReveryNSObject\n    ReveryNSView ReveryNSViewCoords ReveryMenuItemTarget ReveryButtonTarget\n    ReveryProgressBar)\n  (flags\n   :standard\n   -Wall\n   -Wextra\n   -Werror\n   (:include c_flags.sexp)))\n (c_library_flags\n  (:include c_library_flags.sexp))\n (libraries sdl2 lru))\n\n(copy_files cocoa/*)\n\n(copy_files gtk/*)\n\n(rule\n (targets config.h flags.sexp c_flags.sexp c_library_flags.sexp)\n (deps\n  (:discover config/discover.exe))\n (action\n  (run %{discover})))\n"
  },
  {
    "path": "src/Native/environment.c",
    "content": "#include <caml/alloc.h>\n#include <caml/callback.h>\n#include <caml/memory.h>\n#include <caml/mlvalues.h>\n#include <stdio.h>\n#include <string.h>\n\n#include <caml/alloc.h>\n#include <caml/callback.h>\n#include <caml/memory.h>\n#include <caml/mlvalues.h>\n\n#include \"config.h\"\n#ifdef IS_MACOS\n#import \"ReveryMac.h\"\n#elif IS_LINUX\n#include \"ReveryLinux.h\"\n#elif IS_WINDOWS\n#include \"ReveryWindows.h\"\n#endif\n\nCAMLprim value revery_getOperatingSystem() {\n    CAMLparam0();\n    CAMLlocal1(vOS);\n#ifdef IS_ANDROID\n    vOS = Val_int(1);\n#elif IS_IOS\n    vOS = Val_int(2);\n#elif IS_LINUX\n    int kernel, major, minor, patch;\n    getOperatingSystemVersion_linux(&kernel, &major, &minor, &patch);\n    vOS = caml_alloc(4, 1);\n    Store_field(vOS, 0, Val_int(kernel));\n    Store_field(vOS, 1, Val_int(major));\n    Store_field(vOS, 2, Val_int(minor));\n    Store_field(vOS, 3, Val_int(patch));\n#elif IS_WINDOWS\n    int major, minor, build;\n    getOperatingSystemVersion_windows(&major, &minor, &build);\n    vOS = caml_alloc(3, 2);\n    Store_field(vOS, 0, Val_int(major));\n    Store_field(vOS, 1, Val_int(minor));\n    Store_field(vOS, 2, Val_int(build));\n#elif IS_MACOS\n    int major, minor, bugfix;\n    getOperatingSystemVersion_mac(&major, &minor, &bugfix);\n    vOS = caml_alloc(3, 0);\n    Store_field(vOS, 0, Val_int(major));\n    Store_field(vOS, 1, Val_int(minor));\n    Store_field(vOS, 2, Val_int(bugfix));\n#else\n    vOS = Val_int(0);\n#endif\n\n    CAMLreturn(vOS);\n}\n"
  },
  {
    "path": "src/Native/environment_linux.c",
    "content": "#include \"config.h\"\n#ifdef IS_LINUX\n#include <sys/utsname.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <ctype.h>\n\nvoid getOperatingSystemVersion_linux(int *kernel, int *major, int *minor, int *patch) {\n    long ver[16];\n    struct utsname unameInfo;\n    uname(&unameInfo);\n\n    char *cursor = unameInfo.release;\n    int i = 0;\n    while (*cursor) {\n        if (isdigit(*cursor)) {\n            ver[i] = strtol(cursor, &cursor, 10);\n            i++;\n        } else {\n            cursor++;\n        }\n    }\n\n    *kernel = (int)ver[0];\n    *major = (int)ver[1];\n    *minor = (int)ver[2];\n    *patch = (int)ver[3];\n}\n\n#endif\n"
  },
  {
    "path": "src/Native/environment_mac.c",
    "content": "#include \"config.h\"\n#ifdef IS_MACOS\n#import <Foundation/Foundation.h>\n\nvoid getOperatingSystemVersion_mac(int *major, int *minor, int *bugfix) {\n    NSOperatingSystemVersion nsOSVersion = [[NSProcessInfo processInfo] operatingSystemVersion];\n    *major = nsOSVersion.majorVersion;\n    *minor = nsOSVersion.minorVersion;\n    *bugfix = nsOSVersion.patchVersion;\n}\n\n#endif\n"
  },
  {
    "path": "src/Native/environment_windows.c",
    "content": "#include \"config.h\"\n#ifdef IS_WINDOWS\n#include <windows.h>\n#include <stdio.h>\n\nvoid getOperatingSystemVersion_windows(int *major, int *minor, int *build) {\n    OSVERSIONINFOW osVersion;\n    ZeroMemory(&osVersion, sizeof(OSVERSIONINFOW));\n    osVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);\n\n    NTSTATUS (WINAPI *rtlGetVersion)(PRTL_OSVERSIONINFOW lpVersionInformation) = NULL;\n    HINSTANCE hNtDll = LoadLibrary(\"ntdll.dll\");\n\n    if (hNtDll != NULL) {\n        rtlGetVersion = (NTSTATUS (WINAPI *)(PRTL_OSVERSIONINFOW))GetProcAddress(hNtDll, \"RtlGetVersion\");\n\n        if (rtlGetVersion != NULL) {\n            rtlGetVersion(&osVersion);\n        }\n\n        FreeLibrary(hNtDll);\n    }\n\n    if (rtlGetVersion == NULL) {\n        GetVersionEx((OSVERSIONINFO*)&osVersion);\n    }\n\n    *major = (int)osVersion.dwMajorVersion;\n    *minor = (int)osVersion.dwMinorVersion;\n    *build = (int)osVersion.dwBuildNumber;\n}\n\n#endif\n"
  },
  {
    "path": "src/Native/gtk/ReveryGtk.c",
    "content": "#include \"config.h\"\n#ifdef USE_GTK\n\n#include <caml/alloc.h>\n#include <caml/callback.h>\n#include <caml/memory.h>\n#include <caml/mlvalues.h>\n\n#include <gtk/gtk.h>\n\nCAMLprim value revery_gtkEventsPending() {\n    CAMLparam0();\n    gboolean gEventsPending = gtk_events_pending();\n    CAMLreturn(Val_bool(gEventsPending));\n}\n\nCAMLprim value revery_gtkMainIteration() {\n    CAMLparam0();\n    gboolean gMainIteration = gtk_main_iteration();\n    CAMLreturn(Val_bool(gMainIteration));\n}\n\n#endif\n"
  },
  {
    "path": "src/Native/gtk/ReveryGtk_Widget.c",
    "content": "\n#include \"config.h\"\n#ifdef USE_GTK\n\n#include <caml/alloc.h>\n#include <caml/callback.h>\n#include <caml/memory.h>\n#include <caml/mlvalues.h>\n\n#include <gtk/gtk.h>\n#include <gdk/gdkx.h>\n#include <X11/Xlib.h>\n\n#include \"utilities.h\"\n\nstatic void revery_gtkRealize(GtkWidget *gWidget, gpointer data) {\n    gtk_widget_set_window(gWidget, (GdkWindow *)data);\n}\n\nCAMLprim value revery_createGtkWidgetFromXWindow(value vXWindow) {\n    CAMLparam1(vXWindow);\n\n    Window xWindow = (Window)vXWindow;\n\n    GdkDisplay *gDisplay = gdk_display_get_default();\n    XMapRaised(GDK_DISPLAY_XDISPLAY(gDisplay), xWindow);\n\n    GdkWindow *gWindow =\n        gdk_x11_window_foreign_new_for_display(gDisplay, xWindow);\n\n    GtkWidget *gWidget = gtk_widget_new(GTK_TYPE_WINDOW, NULL);\n    g_signal_connect(gWidget, \"realize\", G_CALLBACK(revery_gtkRealize), gWindow);\n    gtk_widget_set_has_window(gWidget, TRUE);\n    gtk_widget_realize(gWidget);\n\n    value camlWidget = revery_wrapPointer(gWidget);\n\n    CAMLreturn(camlWidget);\n}\n\nCAMLprim value revery_gtkWidgetShowAll(value vWidget) {\n    CAMLparam1(vWidget);\n\n    GtkWidget *gWidget = (GtkWidget *)revery_unwrapPointer(vWidget);\n    gtk_widget_show_all(gWidget);\n\n    CAMLreturn(Val_unit);\n}\n\nCAMLprim value revery_gtkWidgetGetPath(value vWidget) {\n    CAMLparam1(vWidget);\n    CAMLlocal1(vPathStr);\n\n    GtkWidget *gWidget = (GtkWidget *)revery_unwrapPointer(vWidget);\n\n    GtkWidgetPath *gWidgetPath = gtk_widget_get_path(gWidget);\n    char *pathStr = gtk_widget_path_to_string(gWidgetPath);\n\n    vPathStr = caml_copy_string(pathStr);\n    free(pathStr);\n\n    CAMLreturn(vPathStr);\n}\n\nCAMLprim value revery_gtkWidgetSetOpacity(value vWidget, value vOpacity) {\n    CAMLparam2(vWidget, vOpacity);\n\n    GtkWidget *gWidget = (GtkWidget *)revery_unwrapPointer(vWidget);\n    double opacity = Double_val(vOpacity);\n\n    gtk_widget_set_opacity(gWidget, opacity);\n\n    CAMLreturn(Val_unit);\n}\n\nCAMLprim value revery_gtkWidgetDestroy(value vWidget) {\n    CAMLparam1(vWidget);\n\n    GtkWidget *gWidget = (GtkWidget *)revery_unwrapPointer(vWidget);\n\n    gtk_widget_destroy(gWidget);\n\n    CAMLreturn(Val_unit);\n}\n\nCAMLprim value revery_gtkWidgetGetOpacity(value vWidget) {\n    CAMLparam1(vWidget);\n\n    GtkWidget *gWidget = (GtkWidget *)revery_unwrapPointer(vWidget);\n\n    double opacity = gtk_widget_get_opacity(gWidget);\n\n    CAMLreturn(caml_copy_double(opacity));\n}\n\nCAMLprim value revery_gtkWidgetGetDepth(value vWidget) {\n    CAMLparam1(vWidget);\n\n    GtkWidget *gWidget = (GtkWidget *)revery_unwrapPointer(vWidget);\n\n    int depth;\n    for (depth = 0; gWidget != NULL; depth++) {\n        gWidget = gtk_widget_get_parent(gWidget);\n    }\n\n    CAMLreturn(Val_int(depth));\n}\n\n#endif\n"
  },
  {
    "path": "src/Native/icon.c",
    "content": "#include <stdio.h>\n\n#include <caml/alloc.h>\n#include <caml/callback.h>\n#include <caml/memory.h>\n#include <caml/mlvalues.h>\n#include <string.h>\n\n#include \"caml_values.h\"\n\n#include \"config.h\"\n#ifdef USE_WIN32\n#include \"ReveryWin32.h\"\n#elif USE_COCOA\n#include \"ReveryCocoa.h\"\n#elif USE_GTK\n#include \"ReveryGtk.h\"\n#endif\n#include \"utilities.h\"\n\nCAMLprim value revery_getIconHandle() {\n    CAMLparam0();\n    void *ret;\n#ifdef USE_COCOA\n    ret = revery_getIconHandle_cocoa();\n#elif USE_WIN32\n    ret = revery_getIconHandle_win32();\n#else\n    fprintf(stderr, \"WARNING: %s is not implemented on this platform.\", __func__);\n    ret = NULL;\n#endif\n    value vIconHandle = revery_wrapPointer(ret);\n    CAMLreturn(vIconHandle);\n}\n\nCAMLprim value revery_setIconProgress(value vWin, value vIconHandle,\n                                      value vProgress) {\n    CAMLparam3(vWin, vIconHandle, vProgress);\n    void *win = (void *)revery_unwrapPointer(vWin);\n    void *ih = (void *)revery_unwrapPointer(vIconHandle);\n    /* vProgress is an OCaml variant of type Revery_Native.Icon.progress\n      It can be either:\n        - Indeterminate -- has no type arguments\n        - Determinate -- has 1 type argument of float\n    */\n    // If vProgress \"is long\", it has no type arguments, and is therefore indeterminate\n    if (Is_long(vProgress)) {\n#ifdef USE_COCOA\n        (void)win;\n        revery_setIconProgressIndeterminate_cocoa(ih);\n#elif USE_WIN32\n        revery_setIconProgressIndeterminate_win32(win, ih);\n#else\n        fprintf(stderr, \"WARNING: %s is not implemented on this platform.\", __func__);\n#endif\n    } else if (Is_block(vProgress)) {  // If vProgress \"is block\", it has a type argument and must be determinate\n        float progress = Double_val(Field(vProgress, 0));\n#ifdef USE_COCOA\n        (void)win;\n        revery_setIconProgress_cocoa(ih, progress);\n#elif USE_WIN32\n        revery_setIconProgress_win32(win, ih, progress);\n#else\n        fprintf(stderr, \"WARNING: %s is not implemented on this platform.\", __func__);\n        (void)win;\n        (void)ih;\n        (void)progress;\n#endif\n    }\n\n    CAMLreturn(Val_unit);\n}\n\nCAMLprim value revery_hideIconProgress(value vWin, value vIconHandle) {\n    CAMLparam2(vWin, vIconHandle);\n    void *win = (void *)revery_unwrapPointer(vWin);\n    void *ih = (void *)revery_unwrapPointer(vIconHandle);\n\n#ifdef USE_COCOA\n    (void)win;\n    revery_hideIconProgress_cocoa(ih);\n#elif USE_WIN32\n    revery_hideIconProgress_win32(win, ih);\n#else\n    (void)win;\n    (void)ih;\n#endif\n\n    CAMLreturn(Val_unit);\n}\n"
  },
  {
    "path": "src/Native/icon_cocoa.c",
    "content": "#include \"config.h\"\n#ifdef USE_COCOA\n#include <stdio.h>\n\n#import <Cocoa/Cocoa.h>\n#import \"ReveryProgressBar.h\"\n\nvoid *revery_getIconHandle_cocoa() {\n    NSDockTile *dock_tile = [NSApp dockTile];\n    return dock_tile;\n}\n\nNSProgressIndicator *get_progress_indicator(NSDockTile *dock_tile) {\n    // Determine if there is already a progress indicator attached to the dock icon\n    int first_render = !dock_tile.contentView ||\n                       [[dock_tile.contentView subviews] count] == 0 ||\n                       ![[[dock_tile.contentView subviews] lastObject]\n                                                           isKindOfClass:[NSProgressIndicator class]];\n\n    NSProgressIndicator *progress_indicator;\n\n    if (first_render) {\n        NSRect frame = NSMakeRect(0.0f, 0.0f, dock_tile.size.width, 15.0f);\n        progress_indicator = [[ReveryProgressBar alloc] initWithFrame:frame];\n        [progress_indicator setBezeled:YES];\n        [progress_indicator setMinValue:0];\n        [progress_indicator setMaxValue:1];\n        [progress_indicator setHidden:NO];\n        [dock_tile.contentView addSubview:progress_indicator];\n    } else {\n        progress_indicator = (NSProgressIndicator *)[[[dock_tile contentView] subviews] lastObject];\n    }\n    return progress_indicator;\n}\n\nvoid revery_setIconProgress_cocoa(void *dt, double progress) {\n    NSDockTile *dock_tile = (NSDockTile *)dt;\n\n    NSProgressIndicator *progress_indicator = get_progress_indicator(dock_tile);\n    [progress_indicator setIndeterminate:NO];\n    [progress_indicator setDoubleValue:progress];\n    [progress_indicator setHidden:NO];\n    [dock_tile display];\n}\n\nvoid revery_setIconProgressIndeterminate_cocoa(void *dt) {\n    NSDockTile *dock_tile = (NSDockTile *)dt;\n\n    NSProgressIndicator *progress_indicator = get_progress_indicator(dock_tile);\n    [progress_indicator setIndeterminate:YES];\n    [progress_indicator setDoubleValue:1];\n    [progress_indicator setHidden:NO];\n    [progress_indicator setUsesThreadedAnimation:YES];\n    [progress_indicator startAnimation:progress_indicator];\n    [dock_tile display];\n}\n\nvoid revery_hideIconProgress_cocoa(void *dt) {\n    NSDockTile *dock_tile = (NSDockTile *)dt;\n\n    NSProgressIndicator *progress_indicator = get_progress_indicator(dock_tile);\n    [progress_indicator setHidden:YES];\n    [progress_indicator release];\n    [[NSApp dockTile] display];\n}\n\n#endif"
  },
  {
    "path": "src/Native/icon_win32.c",
    "content": "#include \"config.h\"\n#ifdef USE_WIN32\n\n#include <windows.h>\n#include <shobjidl.h>\n#include <combaseapi.h>\n#include <stdio.h>\n\nvoid *revery_getIconHandle_win32() {\n    ITaskbarList3 *tbl;\n    CoCreateInstance(&CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER,\n                     &IID_ITaskbarList3, (void**)&tbl);\n    return (void *)tbl;\n}\n\nvoid revery_setIconProgress_win32(void *win, void *ih, float progress) {\n    HWND window = (HWND)win;\n    ITaskbarList3 *iconHandle = (ITaskbarList3 *)ih;\n    iconHandle->lpVtbl->SetProgressState(iconHandle, window, TBPF_NORMAL);\n    iconHandle->lpVtbl->SetProgressValue(iconHandle, window, progress * 100, 100);\n}\n\nvoid revery_setIconProgressIndeterminate_win32(void *win, void *ih) {\n    HWND window = (HWND)win;\n    ITaskbarList3 *iconHandle = (ITaskbarList3 *)ih;\n    iconHandle->lpVtbl->SetProgressState(iconHandle, window, TBPF_INDETERMINATE);\n}\n\nvoid revery_hideIconProgress_win32(void *win, void *ih) {\n    HWND window = (HWND)win;\n    ITaskbarList3 *iconHandle = (ITaskbarList3 *)ih;\n    iconHandle->lpVtbl->SetProgressState(iconHandle, window, TBPF_NOPROGRESS);\n}\n\n#endif\n"
  },
  {
    "path": "src/Native/input.c",
    "content": "#include <caml/alloc.h>\n#include <caml/callback.h>\n#include <caml/memory.h>\n#include <caml/mlvalues.h>\n#include <string.h>\n\n#include \"caml_values.h\"\n#include \"menu.h\"\n\n#include \"config.h\"\n#ifdef USE_WIN32\n#include \"ReveryWin32.h\"\n#elif USE_COCOA\n#include \"ReveryCocoa.h\"\n#import <Cocoa/Cocoa.h>\n#elif USE_GTK\n#include \"ReveryGtk.h\"\n#endif\n#include \"utilities.h\"\n\nCAMLprim value revery_buttonCreate(value vTitle) {\n    CAMLparam1(vTitle);\n    CAMLlocal1(vButton);\n\n    const char *title = String_val(vTitle);\n    void *button;\n#ifdef USE_COCOA\n    button = revery_buttonCreate_cocoa(title);\n    [(NSObject *)button retain];\n#else\n    UNUSED(title);\n    button = NULL;\n#endif\n\n    vButton = revery_wrapPointer(button);\n    CAMLreturn(vButton);\n}\n\nCAMLprim value revery_buttonSetColor(value vButton, value vRed, value vGreen, value vBlue, value vAlpha) {\n    CAMLparam5(vButton, vRed, vGreen, vBlue, vAlpha);\n\n    void *button = revery_unwrapPointer(vButton);\n    double red = Double_val(vRed);\n    double green = Double_val(vGreen);\n    double blue = Double_val(vBlue);\n    double alpha = Double_val(vAlpha);\n#ifdef USE_COCOA\n    revery_buttonSetColor_cocoa(button, red, green, blue, alpha);\n#else\n    UNUSED(button);\n    UNUSED(red);\n    UNUSED(green);\n    UNUSED(blue);\n    UNUSED(alpha);\n#endif\n\n    CAMLreturn(Val_unit);\n}\n\nCAMLprim value revery_openEmojiPanel() {\n    CAMLparam0();\n\n#ifdef USE_COCOA\n    revery_openEmojiPanel_cocoa();\n#endif\n\n    CAMLreturn(Val_unit);\n}\n"
  },
  {
    "path": "src/Native/input_cocoa.c",
    "content": "#include \"config.h\"\n#ifdef USE_COCOA\n#include <stdio.h>\n#include \"menu.h\"\n\n#import <Cocoa/Cocoa.h>\n#import \"ReveryButtonTarget.h\"\n#import \"ReveryNSViewCoords.h\"\n\nstatic ReveryButtonTarget *buttonTarget;\n\nNSButton *revery_buttonCreate_cocoa(const char *title) {\n    if (buttonTarget == NULL) {\n        buttonTarget = [[ReveryButtonTarget alloc] init];\n    }\n\n    NSString *nsTitle = [NSString stringWithUTF8String:title];\n    NSButton *nsButton =\n        [NSButton buttonWithTitle:nsTitle target:buttonTarget action:@selector(onButtonClick:)];\n\n    [nsButton setReveryX:0.0];\n    [nsButton setReveryY:0.0];\n\n    return nsButton;\n}\n\nvoid revery_buttonSetColor_cocoa(NSButton *nsButton, double red, double green, double blue, double alpha) {\n    NSColor *color = [NSColor colorWithCalibratedRed:red green:green blue:blue alpha:alpha];\n\n    NSMutableAttributedString *styledTitle = [[NSMutableAttributedString alloc] initWithAttributedString:[nsButton attributedTitle]];\n    NSRange titleRange = NSMakeRange(0, [styledTitle length]);\n    [styledTitle addAttribute:NSForegroundColorAttributeName value:color range:titleRange];\n\n    [nsButton setAttributedTitle:styledTitle];\n}\n\nvoid revery_openEmojiPanel_cocoa() {\n    [NSApp orderFrontCharacterPalette:nil];\n}\n\n#endif\n"
  },
  {
    "path": "src/Native/locale.c",
    "content": "#include <stdio.h>\n\n#include <caml/alloc.h>\n#include <caml/callback.h>\n#include <caml/memory.h>\n#include <caml/mlvalues.h>\n#include <string.h>\n\n#include \"caml_values.h\"\n\n#include \"config.h\"\n#ifdef USE_WIN32\n#include \"ReveryWin32.h\"\n#elif USE_COCOA\n#include \"ReveryCocoa.h\"\n#elif USE_GTK\n#include \"ReveryGtk.h\"\n#endif\n\n#if defined(__linux__) || defined(__APPLE__)\n#include <locale.h>\n#endif\n\nCAMLprim value revery_getUserLocale() {\n    CAMLparam0();\n    CAMLlocal1(camlRet);\n    char *ret;\n    int shouldFree;\n#ifdef USE_COCOA\n    ret = revery_getUserLocale_cocoa();\n    shouldFree = 0;\n#elif USE_WIN32\n    ret = revery_getUserLocale_win32();\n    shouldFree = 1;\n#else\n    setlocale(LC_CTYPE, \"\");\n    ret = setlocale(LC_CTYPE, NULL);\n    shouldFree = 0;\n#endif\n    camlRet = caml_copy_string(ret);\n    if (shouldFree) {\n        free(ret);\n    }\n    CAMLreturn(camlRet);\n}\n"
  },
  {
    "path": "src/Native/locale_cocoa.c",
    "content": "#include \"config.h\"\n#ifdef USE_COCOA\n#include <stdio.h>\n\n#import <Cocoa/Cocoa.h>\n\nchar *revery_getUserLocale_cocoa() {\n    NSLocale *nsLocale = [NSLocale currentLocale];\n    return (char *)[[nsLocale localeIdentifier] UTF8String];\n}\n#endif\n"
  },
  {
    "path": "src/Native/locale_win32.c",
    "content": "#ifdef _WIN32\n\n#include \"win32_target.h\"\n\n#include <windows.h>\n#include <stdlib.h>\n\nchar *revery_getUserLocale_win32() {\n    WCHAR locale[LOCALE_NAME_MAX_LENGTH];\n    char *localeStr = malloc(LOCALE_NAME_MAX_LENGTH * sizeof(char));\n\n    GetUserDefaultLocaleName(locale, LOCALE_NAME_MAX_LENGTH);\n\n    wcstombs(localeStr, locale, sizeof(localeStr));\n    return localeStr;\n}\n\n#endif\n"
  },
  {
    "path": "src/Native/menu.c",
    "content": "#include <caml/alloc.h>\n#include <caml/callback.h>\n#include <caml/memory.h>\n#include <caml/mlvalues.h>\n#include <string.h>\n\n#include \"caml_values.h\"\n#include \"menu.h\"\n\n#include \"config.h\"\n#ifdef USE_WIN32\n#include \"ReveryWin32.h\"\n#elif USE_COCOA\n#include \"ReveryCocoa.h\"\n#import <Cocoa/Cocoa.h>\n#elif USE_GTK\n#include \"ReveryGtk.h\"\n#endif\n#include \"utilities.h\"\n\nCAMLprim value revery_getMenuBarHandle() {\n    CAMLparam0();\n    CAMLlocal1(vMenuBarHandle);\n\n    void *handle;\n#ifdef USE_COCOA\n    handle = revery_getMenuBarHandle_cocoa();\n    [(NSObject *)handle retain];\n#else\n    handle = NULL;\n#endif\n\n    vMenuBarHandle = revery_wrapPointer(handle);\n    CAMLreturn(vMenuBarHandle);\n}\n\nCAMLprim value revery_menuCreate(value vTitle) {\n    CAMLparam1(vTitle);\n    CAMLlocal1(vMenu);\n\n    const char *title = String_val(vTitle);\n    void *menu;\n#ifdef USE_COCOA\n    menu = revery_menuCreate_cocoa(title);\n    [(NSObject *)menu retain];\n#else\n    UNUSED(title);\n    menu = NULL;\n#endif\n\n    vMenu = revery_wrapPointer(menu);\n    CAMLreturn(vMenu);\n}\n\nvoid convertCamlKeyEquivalent(value vKeyEquivalent, struct KeyEquivalent *keyEquivalent) {\n    keyEquivalent->str = String_val(Field(vKeyEquivalent, 0));\n    keyEquivalent->alt = Bool_val(Field(vKeyEquivalent, 1));\n    keyEquivalent->shift = Bool_val(Field(vKeyEquivalent, 2));\n    keyEquivalent->ctrl = Bool_val(Field(vKeyEquivalent, 3));\n}\n\nCAMLprim value revery_menuItemCreate(value vTitle, value vKeyEquivalent) {\n    CAMLparam2(vTitle, vKeyEquivalent);\n    CAMLlocal1(vMenuItem);\n    struct KeyEquivalent keyEquivalent;\n    if (vKeyEquivalent != Val_none) {\n        convertCamlKeyEquivalent(Some_val(vKeyEquivalent), &keyEquivalent);\n    }\n\n    const char *title = String_val(vTitle);\n    void *menuItem;\n#ifdef USE_COCOA\n    menuItem = revery_menuItemCreate_cocoa(title, vKeyEquivalent == Val_none ? NULL : &keyEquivalent);\n    [(NSObject *)menuItem retain];\n#else\n    UNUSED(title);\n    menuItem = NULL;\n#endif\n\n    vMenuItem = revery_wrapPointer(menuItem);\n    CAMLreturn(vMenuItem);\n}\n\nCAMLprim value revery_menuNth(value vMenu, value vIdx) {\n    CAMLparam2(vMenu, vIdx);\n    CAMLlocal1(vMenuItem);\n\n    void *menu = revery_unwrapPointer(vMenu);\n    int idx = Int_val(vIdx);\n    void *menuItem;\n#ifdef USE_COCOA\n    menuItem = revery_menuNth_cocoa(menu, idx);\n    if (menuItem != NULL) {\n        [(NSObject *)menuItem retain];\n    }\n#else\n    UNUSED(menu);\n    UNUSED(idx);\n    menuItem = NULL;\n#endif\n\n    vMenuItem = revery_wrapOptionalPointer(menuItem);\n    CAMLreturn(vMenuItem);\n}\n\nCAMLprim value revery_menuAddItem(value vMenu, value vMenuItem) {\n    CAMLparam2(vMenu, vMenuItem);\n\n    void *menu = revery_unwrapPointer(vMenu);\n    void *menuItem = revery_unwrapPointer(vMenuItem);\n#ifdef USE_COCOA\n    revery_menuAddItem_cocoa(menu, menuItem);\n#else\n    UNUSED(menu);\n    UNUSED(menuItem);\n#endif\n\n    CAMLreturn(Val_unit);\n}\n\nCAMLprim value revery_menuItemGetSubmenu(value vMenuItem) {\n    CAMLparam1(vMenuItem);\n    CAMLlocal1(vSubmenu);\n\n    void *menuItem = revery_unwrapPointer(vMenuItem);\n    void *submenu;\n#ifdef USE_COCOA\n    submenu = revery_menuItemGetSubmenu_cocoa(menuItem);\n    if (submenu != NULL) {\n        [(NSObject *)submenu retain];\n    }\n#else\n    UNUSED(menuItem);\n    submenu = NULL;\n#endif\n\n    vSubmenu = revery_wrapOptionalPointer(submenu);\n    CAMLreturn(vSubmenu);\n}\n\nCAMLprim value revery_menuRemoveItem(value vMenu, value vMenuItem) {\n    CAMLparam2(vMenu, vMenuItem);\n\n    void *menu = revery_unwrapPointer(vMenu);\n    void *menuItem = revery_unwrapPointer(vMenuItem);\n#ifdef USE_COCOA\n    revery_menuRemoveItem_cocoa(menu, menuItem);\n#else\n    UNUSED(menu);\n    UNUSED(menuItem);\n#endif\n\n    CAMLreturn(Val_unit);\n}\n\nCAMLprim value revery_menuAddSubmenu(value vParent, value vChild) {\n    CAMLparam2(vParent, vChild);\n\n    void *parent = revery_unwrapPointer(vParent);\n    void *child = revery_unwrapPointer(vChild);\n#ifdef USE_COCOA\n    revery_menuAddSubmenu_cocoa(parent, child);\n#else\n    UNUSED(parent);\n    UNUSED(child);\n#endif\n\n    CAMLreturn(Val_unit);\n}\n\nCAMLprim value revery_menuRemoveSubmenu(value vParent, value vChild) {\n    CAMLparam2(vParent, vChild);\n\n    void *parent = revery_unwrapPointer(vParent);\n    void *child = revery_unwrapPointer(vChild);\n#ifdef USE_COCOA\n    revery_menuRemoveSubmenu_cocoa(parent, child);\n#else\n    UNUSED(parent);\n    UNUSED(child);\n#endif\n\n    CAMLreturn(Val_unit);\n}\n\nCAMLprim value revery_menuInsertItemAt(value vMenu, value vMenuItem, value vIdx) {\n    CAMLparam3(vMenu, vMenuItem, vIdx);\n\n    void *menu = revery_unwrapPointer(vMenu);\n    void *menuItem = revery_unwrapPointer(vMenuItem);\n    int idx = Int_val(vIdx);\n\n#ifdef USE_COCOA\n    revery_menuInsertItemAt_cocoa(menu, menuItem, idx);\n#else\n    UNUSED(menu);\n    UNUSED(menuItem);\n    UNUSED(idx);\n#endif\n\n    CAMLreturn(Val_unit);\n}\n\nCAMLprim value revery_menuInsertSubmenuAt(value vParent, value vChild, value vIdx) {\n    CAMLparam3(vParent, vChild, vIdx);\n\n    void *parent = revery_unwrapPointer(vParent);\n    void *child = revery_unwrapPointer(vChild);\n    int idx = Int_val(vIdx);\n\n#ifdef USE_COCOA\n    revery_menuInsertSubmenuAt_cocoa(parent, child, idx);\n#else\n    UNUSED(parent);\n    UNUSED(child);\n    UNUSED(idx);\n#endif\n\n    CAMLreturn(Val_unit);\n}\n\nCAMLprim value revery_menuClear(value vMenu) {\n    CAMLparam1(vMenu);\n\n    void *menu = revery_unwrapPointer(vMenu);\n#ifdef USE_COCOA\n    revery_menuClear_cocoa(menu);\n#else\n    UNUSED(menu);\n#endif\n\n    CAMLreturn(Val_unit);\n}\n\nCAMLprim value revery_menuDisplayIn(value vMenu, value vWindow, value vX, value vY) {\n    CAMLparam4(vMenu, vWindow, vX, vY);\n\n    void *menu = revery_unwrapPointer(vMenu);\n    void *window = revery_unwrapPointer(vWindow);\n    int x = Int_val(vX);\n    int y = Int_val(vY);\n#ifdef USE_COCOA\n    revery_menuDisplayIn_cocoa(menu, window, x, y);\n#else\n    UNUSED(menu);\n    UNUSED(window);\n    UNUSED(x);\n    UNUSED(y);\n#endif\n\n    CAMLreturn(Val_unit);\n}\n\nCAMLprim value revery_menuItemCreateSeparator() {\n    CAMLparam0();\n    CAMLlocal1(vSeparator);\n\n    void *separator;\n#ifdef USE_COCOA\n    separator = revery_menuItemCreateSeparator_cocoa();\n#else\n    separator = NULL;\n#endif\n    vSeparator = revery_wrapPointer(separator);\n\n    CAMLreturn(vSeparator);\n}\n\nCAMLprim value revery_menuItemSetEnabled(value vMenuItem, value vTruth) {\n    CAMLparam2(vMenuItem, vTruth);\n\n    void *menuItem = revery_unwrapPointer(vMenuItem);\n    int truth = Bool_val(vTruth);\n\n#ifdef USE_COCOA\n    revery_menuItemSetEnabled_cocoa(menuItem, truth);\n#else\n    UNUSED(menuItem);\n    UNUSED(truth);\n#endif\n\n    CAMLreturn(Val_unit);\n}\n\nCAMLprim value revery_menuItemSetVisible(value vMenuItem, value vTruth) {\n    CAMLparam2(vMenuItem, vTruth);\n\n    void *menuItem = revery_unwrapPointer(vMenuItem);\n    int truth = Bool_val(vTruth);\n\n#ifdef USE_COCOA\n    revery_menuItemSetVisible_cocoa(menuItem, truth);\n#else\n    UNUSED(menuItem);\n    UNUSED(truth);\n#endif\n\n    CAMLreturn(Val_unit);\n}\n"
  },
  {
    "path": "src/Native/menu.h",
    "content": "#pragma once\n\nstruct KeyEquivalent {\n    const char *str;\n    int alt;\n    int shift;\n    int ctrl;\n};\n"
  },
  {
    "path": "src/Native/menu_cocoa.c",
    "content": "#include \"config.h\"\n#ifdef USE_COCOA\n#include <stdio.h>\n#include \"menu.h\"\n\n#import <Cocoa/Cocoa.h>\n#import \"ReveryMenuItemTarget.h\"\n\nstatic ReveryMenuItemTarget *menuItemTarget;\n\nNSMenu *revery_getMenuBarHandle_cocoa() {\n    return [[NSApplication sharedApplication] mainMenu];\n}\n\nNSMenu *revery_menuCreate_cocoa(const char *title) {\n    NSString *nsTitle = [NSString stringWithUTF8String:title];\n    NSMenu *nsMenu = [[NSMenu alloc] initWithTitle:nsTitle];\n    [nsMenu setAutoenablesItems:NO];\n    return nsMenu;\n}\n\nNSMenuItem *revery_menuItemCreate_cocoa(const char *title, struct KeyEquivalent *keyEquivalent) {\n    if (menuItemTarget == NULL) {\n        menuItemTarget = [[ReveryMenuItemTarget alloc] init];\n    }\n\n    NSString *nsTitle = [NSString stringWithUTF8String:title];\n    NSMenuItem *nsMenuItem =\n        [[NSMenuItem alloc] initWithTitle:nsTitle action:@selector(onMenuItemClick:) keyEquivalent:@\"\"];\n\n    [nsMenuItem setTarget:menuItemTarget];\n\n    if (keyEquivalent != NULL) {\n        NSEventModifierFlags modifierFlags = NSCommandKeyMask;\n        [nsMenuItem setKeyEquivalent:[NSString stringWithUTF8String:keyEquivalent->str]];\n        if (keyEquivalent->alt) {\n            modifierFlags |= NSEventModifierFlagOption;\n        }\n        if (keyEquivalent->shift) {\n            modifierFlags |= NSEventModifierFlagShift;\n        }\n        if (keyEquivalent->ctrl) {\n            modifierFlags |= NSEventModifierFlagControl;\n        }\n        [nsMenuItem setKeyEquivalentModifierMask:modifierFlags];\n    }\n\n    return nsMenuItem;\n}\n\nNSMenuItem *revery_menuNth_cocoa(NSMenu *nsMenu, int idx) {\n    if (idx > [nsMenu numberOfItems]) {\n        return NULL;\n    }\n\n    return [nsMenu itemAtIndex:idx];\n}\n\nvoid revery_menuAddItem_cocoa(NSMenu *nsMenu, NSMenuItem *nsMenuItem) {\n    [nsMenu addItem:nsMenuItem];\n}\n\nvoid *revery_menuItemGetSubmenu_cocoa(NSMenuItem *nsMenuItem) {\n    if (![nsMenuItem hasSubmenu]) {\n        return NULL;\n    }\n\n    return [nsMenuItem submenu];\n}\n\nvoid revery_menuAddSubmenu_cocoa(NSMenu *parent, NSMenu *child) {\n    NSMenuItem *nsMenuItem =\n        [[NSMenuItem alloc] initWithTitle:[child title] action:NULL keyEquivalent:@\"\"];\n    [parent addItem:nsMenuItem];\n    [parent setSubmenu:child forItem:nsMenuItem];\n}\n\nvoid revery_menuRemoveSubmenu_cocoa(NSMenu *parent, NSMenu *child) {\n    NSMutableArray *itemsToRemove = [NSMutableArray array];\n    for (NSMenuItem *item in [parent itemArray]) {\n        if ([child isEqual:[item submenu]]) {\n            [itemsToRemove addObject:item];\n        }\n    }\n\n    for (NSMenuItem *item in itemsToRemove) {\n        [parent removeItem:item];\n    }\n}\n\nvoid revery_menuRemoveItem_cocoa(NSMenu *nsMenu, NSMenuItem *nsMenuItem) {\n    [nsMenu removeItem:nsMenuItem];\n}\n\nvoid revery_menuInsertItemAt_cocoa(NSMenu *nsMenu, NSMenuItem *nsMenuItem, int idx) {\n    [nsMenu insertItem:nsMenuItem atIndex:idx];\n}\n\nvoid revery_menuInsertSubmenuAt_cocoa(NSMenu *parent, NSMenu *child, int idx) {\n    NSMenuItem *nsMenuItem =\n        [[NSMenuItem alloc] initWithTitle:[child title] action:NULL keyEquivalent:@\"\"];\n    [parent insertItem:nsMenuItem atIndex:idx];\n    [parent setSubmenu:child forItem:nsMenuItem];\n}\n\nvoid revery_menuClear_cocoa(NSMenu *nsMenu) {\n    [nsMenu removeAllItems];\n}\n\nvoid revery_menuDisplayIn_cocoa(NSMenu *nsMenu, NSWindow *nsWindow, int x, int y) {\n    NSView *nsView = [nsWindow contentView];\n    CGRect frame = [nsView frame];\n\n    float adjY = frame.size.height - (float)y;\n\n    CGPoint point;\n    point.x = (float)x;\n    point.y = adjY;\n\n    [nsMenu popUpMenuPositioningItem:nil atLocation:point inView:nsView];\n}\n\nNSMenuItem *revery_menuItemCreateSeparator_cocoa() {\n    return [NSMenuItem separatorItem];\n}\n\nvoid revery_menuItemSetEnabled_cocoa(NSMenuItem *nsMenuItem, int truth) {\n    [nsMenuItem setEnabled:truth];\n}\n\nvoid revery_menuItemSetVisible_cocoa(NSMenuItem *nsMenuItem, int truth) {\n    [nsMenuItem setHidden:!truth];\n}\n#endif\n"
  },
  {
    "path": "src/Native/notification.c",
    "content": "#include <stdio.h>\n\n#include <caml/alloc.h>\n#include <caml/callback.h>\n#include <caml/memory.h>\n#include <caml/mlvalues.h>\n\n#include \"caml_values.h\"\n\n#include \"config.h\"\n#ifdef USE_WIN32\n#include \"ReveryWin32.h\"\n#elif USE_COCOA\n#include \"ReveryCocoa.h\"\n#elif USE_GTK\n#include \"ReveryGtk.h\"\n#endif\n\n#include \"utilities.h\"\n\nCAMLprim value revery_dispatchNotification(value vNotificationT) {\n    CAMLparam1(vNotificationT);\n\n    const char *title;\n    const char *body;\n    int mute;\n\n    title = String_val(Field(vNotificationT, 0));\n    body = String_val(Field(vNotificationT, 1));\n    value onClickCaml = Field(vNotificationT, 2);\n    mute = Int_val(Field(vNotificationT, 3));\n#ifdef USE_COCOA\n    revery_dispatchNotification_cocoa(title, body, onClickCaml, mute);\n    UNUSED(title);\n    UNUSED(body);\n    UNUSED(mute);\n    UNUSED(onClickCaml);\n#else\n    UNUSED(title);\n    UNUSED(body);\n    UNUSED(mute);\n    UNUSED(onClickCaml);\n#endif\n    CAMLreturn(Val_unit);\n}\n\nCAMLprim value revery_scheduleNotificationFromNow(value vSeconds, value vNotificationT) {\n    CAMLparam2(vNotificationT, vSeconds);\n\n    const char *title;\n    const char *body;\n    int mute;\n    int seconds;\n    value onClickCaml;\n\n    title = String_val(Field(vNotificationT, 0));\n    body = String_val(Field(vNotificationT, 1));\n    onClickCaml = Field(vNotificationT, 2);\n    mute = Int_val(Field(vNotificationT, 3));\n    seconds = Int_val(vSeconds);\n#ifdef USE_COCOA\n    revery_scheduleNotificationFromNow_cocoa(title, body, onClickCaml, mute, seconds);\n    UNUSED(title);\n    UNUSED(body);\n    UNUSED(mute);\n    UNUSED(onClickCaml);\n#else\n    UNUSED(title);\n    UNUSED(body);\n    UNUSED(mute);\n    UNUSED(seconds);\n    UNUSED(onClickCaml);\n#endif\n    CAMLreturn(Val_unit);\n}\n"
  },
  {
    "path": "src/Native/notification_cocoa.c",
    "content": "#include \"config.h\"\n#ifdef USE_COCOA\n#import \"cocoa/ReveryAppDelegate.h\"\n#import <Cocoa/Cocoa.h>\n\n#include \"utilities.h\"\n\nvoid revery_dispatchNotification_cocoa(const char *title, const char *body,\n                                       long onClickFunc, int mute) {\n    NSUserNotification *notification = [[NSUserNotification alloc] autorelease];\n    NSUserNotificationCenter *notificationCenter =\n        [NSUserNotificationCenter defaultUserNotificationCenter];\n    ReveryAppDelegate *delegate = (ReveryAppDelegate *)[NSApp delegate];\n    NSString *nsTitle =\n        [NSString stringWithCString:title encoding:NSUTF8StringEncoding];\n    NSString *nsBody =\n        [NSString stringWithCString:body encoding:NSUTF8StringEncoding];\n\n    NSString *identifier =\n        [NSString stringWithFormat:@\"%@:notification:%@\",\n                  [[NSBundle mainBundle] bundleIdentifier],\n                  [[[NSUUID alloc] init] UUIDString]];\n    [notification setIdentifier:identifier];\n    [notification setTitle:nsTitle];\n    [notification setInformativeText:nsBody];\n    [notification setSoundName:mute ? NULL : NSUserNotificationDefaultSoundName];\n\n    delegate.notificationActions[identifier] =\n        [NSNumber numberWithLong:onClickFunc];\n\n    [notificationCenter deliverNotification:notification];\n}\n\nvoid revery_scheduleNotificationFromNow_cocoa(const char *title, const char *body,\n        long onClickFunc, int mute, int seconds) {\n    NSUserNotification *notification = [[NSUserNotification alloc] autorelease];\n    NSUserNotificationCenter *notificationCenter =\n        [NSUserNotificationCenter defaultUserNotificationCenter];\n    ReveryAppDelegate *delegate = (ReveryAppDelegate *)[NSApp delegate];\n    NSString *nsTitle =\n        [NSString stringWithCString:title encoding:NSUTF8StringEncoding];\n    NSString *nsBody =\n        [NSString stringWithCString:body encoding:NSUTF8StringEncoding];\n\n    NSString *identifier =\n        [NSString stringWithFormat:@\"%@:notification:%@\",\n                  [[NSBundle mainBundle] bundleIdentifier],\n                  [[[NSUUID alloc] init] UUIDString]];\n    [notification setIdentifier:identifier];\n    [notification setTitle:nsTitle];\n    [notification setInformativeText:nsBody];\n    [notification setSoundName:mute ? NULL : NSUserNotificationDefaultSoundName];\n    NSDate *deliveryDate = [[NSDate alloc] initWithTimeIntervalSinceNow:seconds];\n    [notification setDeliveryDate:deliveryDate];\n\n    delegate.notificationActions[identifier] =\n        [NSNumber numberWithLong:onClickFunc];\n\n    [notificationCenter scheduleNotification:notification];\n}\n\n#endif"
  },
  {
    "path": "src/Native/shell.c",
    "content": "#include <stdio.h>\n\n#include <caml/alloc.h>\n#include <caml/callback.h>\n#include <caml/memory.h>\n#include <caml/mlvalues.h>\n\n#include \"caml_values.h\"\n\n#include \"config.h\"\n#ifdef USE_WIN32\n#include \"ReveryWin32.h\"\n#elif USE_COCOA\n#include \"ReveryCocoa.h\"\n#elif USE_GTK\n#include \"ReveryGtk.h\"\n#endif\n#include \"utilities.h\"\n\nCAMLprim value revery_openURL(value vURL) {\n    CAMLparam1(vURL);\n\n    const char *url_string = String_val(vURL);\n    int success = 0;\n#ifdef USE_COCOA\n    success = revery_openURL_cocoa(url_string);\n#elif USE_GTK\n    success = revery_openURL_gtk(url_string);\n#elif USE_WIN32\n    success = revery_openURL_win32(url_string);\n#else\n    fprintf(stderr, \"WARNING: %s is not implemented on this platform.\\n\", __func__);\n    success = 0;\n    UNUSED(url_string);\n#endif\n    CAMLreturn(Val_bool(success));\n}\n\nCAMLprim value revery_openFile(value vPath) {\n    CAMLparam1(vPath);\n\n    const char *path_string = String_val(vPath);\n    int success = 0;\n#ifdef USE_COCOA\n    success = revery_openFile_cocoa(path_string);\n#elif USE_GTK\n    success = revery_openFile_gtk(path_string);\n#elif USE_WIN32\n    // The Win32 implementation of the URL opener also works for file paths\n    success = revery_openURL_win32(path_string);\n#else\n    fprintf(stderr, \"WARNING: %s is not implemented on this platform.\\n\", __func__);\n    success = 0;\n    UNUSED(path_string);\n#endif\n    CAMLreturn(Val_bool(success));\n}\n"
  },
  {
    "path": "src/Native/shell_cocoa.c",
    "content": "#include \"config.h\"\n#ifdef USE_COCOA\n#import <Cocoa/Cocoa.h>\n\nint revery_openURL_cocoa(const char *url_string) {\n    NSString *nsString = [NSString stringWithCString:url_string encoding:NSUTF8StringEncoding];\n    NSString *nsEncodedString = [nsString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]];\n    NSURL *nsURL = [NSURL URLWithString:nsEncodedString];\n\n    int success = (int)[[NSWorkspace sharedWorkspace] openURL:nsURL];\n\n    [nsEncodedString release];\n    [nsString release];\n    [nsURL release];\n\n    return success;\n}\n\nint revery_openFile_cocoa(const char *path_string) {\n    NSString *nsString = [NSString stringWithCString:path_string encoding:NSUTF8StringEncoding];\n    NSURL *nsURL = [NSURL fileURLWithPath:nsString];\n\n    int success = (int)[[NSWorkspace sharedWorkspace] openURL:nsURL];\n\n    [nsString release];\n    [nsURL release];\n\n    return success;\n}\n\n#endif\n"
  },
  {
    "path": "src/Native/shell_gtk.c",
    "content": "#include \"config.h\"\n#ifdef USE_GTK\n#include <gtk/gtk.h>\n\n/*  gtk_show_uri is technically deprecated, but it has the most support\n and we aren't attaching to a window or screen */\nint revery_openURL_gtk(const char *url_string) {\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wdeprecated-declarations\"\n    return (int)gtk_show_uri(\n               NULL, // We don't have a parent screen (and we don't need one)\n               url_string,\n               gtk_get_current_event_time(),\n               NULL\n           );\n#pragma GCC diagnostic pop\n}\n\nint revery_openFile_gtk(const char *path_string) {\n    GString *url = g_string_new(path_string);\n    url = g_string_prepend(url, \"file://\");\n    return revery_openURL_gtk(g_string_free(url, FALSE));\n}\n\n#endif\n"
  },
  {
    "path": "src/Native/shell_win32.c",
    "content": "#include \"config.h\"\n#ifdef USE_WIN32\n\n#include <windows.h>\n#include <shellapi.h>\n#include <stdint.h>\n\nint revery_openURL_win32(const char *url_string) {\n    /* We have to do this pointer magic because HINSTANCE is a\n    legacy type that is meant to be converted.\n    See https://docs.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shellexecutea#return-value\n    */\n    return (int)(intptr_t)ShellExecute(\n               NULL,\n               \"open\",\n               url_string,\n               NULL,\n               NULL,\n               SW_SHOW\n           );\n}\n\n#endif\n"
  },
  {
    "path": "src/Native/utilities.c",
    "content": "#include <caml/callback.h>\n#include <caml/memory.h>\n#include <caml/mlvalues.h>\n#include <caml/threads.h>\n#include <caml/alloc.h>\n\n#include \"caml_values.h\"\n#include \"utilities.h\"\n\n/* Sourced from Brisk's `BriskCocoa`\n    Thank you @wokalski!\n*/\nvoid revery_caml_call_n(camlValue f, int argCount, camlValue *args) {\n    caml_c_thread_register();\n    // With the change to remove the acquire/release runtime calls in:\n    // https://github.com/revery-ui/revery/pull/1072\n    // it seems that this acquire/release pair is no longer required\n    // (...and will crash).\n\n    // caml_acquire_runtime_system();\n    caml_callbackN(f, argCount, args);\n    //caml_release_runtime_system();\n}\n\nvoid revery_caml_call(camlValue f) {\n    value args[] = {Val_unit};\n    revery_caml_call_n(f, 1, args);\n}\n\n/* Create an OCaml value encapsulating the pointer p */\nvalue revery_wrapPointer(void *p) {\n    value v = caml_alloc(1, Abstract_tag);\n    *((void **) Data_abstract_val(v)) = p;\n    return v;\n}\n\n/* Extract the pointer encapsulated in the given OCaml value */\nvoid *revery_unwrapPointer(value v) {\n    return *((void **) Data_abstract_val(v));\n}\n\n/* Create an OCaml value encapsulating the pointer p\n    None when p == NULL, Some(p) when p != NULL\n*/\nCAMLprim value revery_wrapOptionalPointer(void *p) {\n    CAMLparam0();\n    if (p != NULL) {\n        value v = revery_wrapPointer(p);\n        CAMLreturn(Val_some(v));\n    } else {\n        CAMLreturn(Val_none);\n    }\n}\n"
  },
  {
    "path": "src/Native/utilities.h",
    "content": "#pragma once\n\n/* This was taken from OCaml's config.h.\n  It lets us use the value type without\n  importing the offending header file\n  that causes Obj-C collisions */\n#if SIZEOF_PTR == SIZEOF_LONG\n/* Standard models: ILP32 or I32LP64 */\ntypedef long intnat;\ntypedef unsigned long uintnat;\n#define ARCH_INTNAT_PRINTF_FORMAT \"l\"\n#elif SIZEOF_PTR == SIZEOF_INT\n/* Hypothetical IP32L64 model */\ntypedef int intnat;\ntypedef unsigned int uintnat;\n#define ARCH_INTNAT_PRINTF_FORMAT \"\"\n#elif SIZEOF_PTR == 8\n/* Win64 model: IL32P64 */\ntypedef int64_t intnat;\ntypedef uint64_t uintnat;\n#define ARCH_INTNAT_PRINTF_FORMAT ARCH_INT64_PRINTF_FORMAT\n#else\n#error \"No integer type available to represent pointers\"\n#endif\n\ntypedef intnat camlValue;\n\nvoid revery_caml_call_n(camlValue f, int numArgs, camlValue* args);\nvoid revery_caml_call(camlValue f);\ncamlValue revery_wrapPointer(void *data);\nvoid *revery_unwrapPointer(camlValue data);\ncamlValue revery_wrapOptionalPointer(void *data);\n\n#define UNUSED(x) ((void)(x))\n\n#ifdef USE_COCOA\n#define CATEGORY_PROPERTY_GET(type, property) - (type) property { return objc_getAssociatedObject(self, @selector(property)); }\n#define CATEGORY_PROPERTY_SET(type, property, setter) - (void) setter (type) property { objc_setAssociatedObject(self, @selector(property), property, OBJC_ASSOCIATION_RETAIN_NONATOMIC); }\n#define CATEGORY_PROPERTY_GET_SET(type, property, setter) CATEGORY_PROPERTY_GET(type, property) CATEGORY_PROPERTY_SET(type, property, setter)\n\n#define CATEGORY_PROPERTY_GET_NSNUMBER_PRIMITIVE(type, property, valueSelector) - (type) property { return [objc_getAssociatedObject(self, @selector(property)) valueSelector]; }\n#define CATEGORY_PROPERTY_SET_NSNUMBER_PRIMITIVE(type, property, setter, numberSelector) - (void) setter (type) property { objc_setAssociatedObject(self, @selector(property), [NSNumber numberSelector: property], OBJC_ASSOCIATION_RETAIN_NONATOMIC); }\n\n#define CATEGORY_PROPERTY_GET_DOUBLE(property) CATEGORY_PROPERTY_GET_NSNUMBER_PRIMITIVE(double, property, doubleValue)\n#define CATEGORY_PROPERTY_SET_DOUBLE(property, setter) CATEGORY_PROPERTY_SET_NSNUMBER_PRIMITIVE(double, property, setter, numberWithDouble)\n#define CATEGORY_PROPERTY_GET_SET_DOUBLE(property, setter) CATEGORY_PROPERTY_GET_DOUBLE(property) CATEGORY_PROPERTY_SET_DOUBLE(property, setter)\n#endif\n\n"
  },
  {
    "path": "src/Native/win32_target.h",
    "content": "#define WINVER 0x0601\n#define _WIN32_WINNT 0x0601\n"
  },
  {
    "path": "src/Native/window.c",
    "content": "#include <caml/alloc.h>\n#include <caml/callback.h>\n#include <caml/memory.h>\n#include <caml/mlvalues.h>\n#include <string.h>\n\n#include \"caml_values.h\"\n\n#include \"config.h\"\n#ifdef USE_WIN32\n#include \"ReveryWin32.h\"\n#elif USE_COCOA\n#include \"ReveryCocoa.h\"\n#import <Cocoa/Cocoa.h>\n#elif USE_GTK\n#include \"ReveryGtk.h\"\n#endif\n#include \"utilities.h\"\n\nCAMLprim value revery_windowSetUnsavedWork(value vWindow, value vTruth) {\n    CAMLparam2(vWindow, vTruth);\n\n    void *window = revery_unwrapPointer(vWindow);\n    int truth = Bool_val(vTruth);\n#ifdef USE_COCOA\n    revery_windowSetUnsavedWork_cocoa(window, truth);\n#else\n    UNUSED(window);\n    UNUSED(truth);\n#endif\n\n    CAMLreturn(Val_unit);\n}\n"
  },
  {
    "path": "src/Native/window_cocoa.c",
    "content": "#include \"config.h\"\n#ifdef USE_COCOA\n\n#import <Cocoa/Cocoa.h>\n\nvoid revery_windowSetUnsavedWork_cocoa(NSWindow *window, int truth) {\n    [window setDocumentEdited:truth];\n}\n\n#endif\n"
  },
  {
    "path": "src/Platform.re",
    "content": "/**\n * Platform.re\n *\n */\nmodule Window = Revery_Core.Window;\n\nmodule Dialog = {\n  let alert = (window: Window.t, message: string) =>\n    Sdl2.MessageBox.showSimple(\n      Information,\n      \"Revery\",\n      message,\n      Some(Window.getSdlWindow(window)),\n    );\n};\n"
  },
  {
    "path": "src/Revery.re",
    "content": "/*\n * Include Revery_Core in the top level 'Revery' module\n * Otherwise it's confusing when to open 'Revery' vs 'Revery.Core'\n */\ninclude Revery_Core;\n\nmodule Font = Revery_Font;\nmodule Draw = Revery_Draw;\nmodule Math = Revery_Math;\nmodule Native = Revery_Native;\n\nmodule UI = {\n  include Revery_UI;\n  include Revery_UI_Primitives;\n\n  /*\n   * Include Components such that consumers access it via:\n   * Revery.UI.Components\n   */\n  module Components = Revery_UI_Components;\n  module Hooks = Revery_UI_Hooks;\n};\n\nmodule Platform = {\n  include Platform;\n};\n\nmodule Debug = {\n  let enable = Revery_Draw.DebugDraw.enable;\n  let disable = Revery_Draw.DebugDraw.disable;\n  let isEnabled = Revery_Draw.DebugDraw.isEnabled;\n};\n\nmodule Utility = {\n  include Revery_Utility;\n};\n"
  },
  {
    "path": "src/UI/Animation.re",
    "content": "open Revery_Core;\nopen Revery_Math;\n\nmodule NormalizedTime = {\n  type t = float;\n  let fromFloat = t => Float.max(0., Float.min(1., t));\n};\n\ntype t('a) = Time.t => ('a, state)\n\nand state =\n  | Delayed\n  | Running\n  | Complete(Time.t); // Elapsed time\n\nlet const = (constant, _time) => (constant, Complete(Time.zero));\n\n/**\n * `time` is assumed to start at 0\n */\nlet animate = (duration, time) => {\n  let normalizedTime = Time.(toFloatSeconds(time / duration));\n\n  if (normalizedTime < 0.) {\n    (0., Delayed);\n  } else if (normalizedTime > 1.) {\n    (1., Complete(duration));\n  } else {\n    (normalizedTime, Running);\n  };\n};\n\nlet delay = (delay, animate, time) =>\n  if (delay > time) {\n    (fst(animate(Time.zero)), Delayed);\n  } else {\n    switch (animate(Time.(time - delay))) {\n    | (value, Complete(elapsed)) => (\n        value,\n        Complete(Time.(elapsed + delay)),\n      )\n    | result => result\n    };\n  };\n\nlet repeat = (animate, time) =>\n  switch (animate(time)) {\n  | (_, Complete(elapsed)) =>\n    let remainder = elapsed == Time.zero ? Time.zero : Time.(time mod elapsed);\n    animate(remainder);\n\n  | result => result\n  };\n\nlet alternatingRepeat = (animate, time) =>\n  switch (animate(time)) {\n  | (_, Complete(elapsed)) =>\n    let iteration = int_of_float(floor(Time.toFloatSeconds(time)));\n    let shouldReverse = iteration mod 2 != 0; // if not divisble by 2\n    let remainder = elapsed == Time.zero ? Time.zero : Time.(time mod elapsed);\n    animate(shouldReverse ? Time.(elapsed - remainder) : remainder);\n\n  | result => result\n  };\n\nlet map = (f, animate, time) =>\n  switch (animate(time)) {\n  | (t, state) => (f(t), state)\n  };\n\nlet ease = (easing, animate) => map(easing, animate);\n\nlet tween = (start, finish, animate) =>\n  map(interpolate(start, finish), animate);\n\nlet andThen = (current, ~next, time) =>\n  switch (current(time)) {\n  | (_, Complete(elapsed)) => next(Time.(time - elapsed))\n  | result => result\n  };\n\nlet zip = ((a, b), time) =>\n  switch (a(time), b(time)) {\n  | ((aValue, Delayed), (bValue, Delayed)) => ((aValue, bValue), Delayed)\n\n  | ((aValue, Complete(aElapsed)), (bValue, Complete(bElapsed))) => (\n      (aValue, bValue),\n      Complete(Time.max(aElapsed, bElapsed)),\n    )\n\n  | ((aValue, _), (bValue, _)) => ((aValue, bValue), Running)\n  };\n\nlet apply = (time, animate) => animate(time);\n\nlet valueAt = (time, animate) => fst(animate(time));\n\nlet stateAt = (time, animate) => snd(animate(time));\n"
  },
  {
    "path": "src/UI/Animation.rei",
    "content": "open Revery_Core;\n\n/**\n * Normalized time in the interval [0., 1.]\n */\nmodule NormalizedTime: {\n  type t = pri float;\n  let fromFloat: float => t;\n};\n\n/**\n * `t('value)` is the type of an animated value where `'value` represents the type of the value.\n */\ntype t('value);\n\n/**\n * `state` is the state of the animation.\n *\n *   `Delayed` means it has yet to start\n *   `Running` means it has started and not yet finished.\n *   `Complete(elapsed)` means it has completed in `elapsed` time.\n */\ntype state =\n  | Delayed\n  | Running\n  | Complete(Time.t); // Elapsed time\n\n/**\n * `const(value)` will always be completed and produce `value`\n *\n * Examples:\n *   const(3) |> valueAt(Time.zero) == 3,\n *   const(3) |> valueAt(Time.seconds(-42)) == 3,\n *   const(3) |> valueAt(Time.seconds(42)) == 3,\n *   const(3) |> stateAt(Time.seconds(-42)) == Complete(Time.zero),\n *   const(3) |> stateAt(Time.seconds(42)) == Complete(Time.zero),\n *   const(\"rhino\") |> valueAt(Time.zero) == \"rhino\",\n */\nlet const: 'a => t('a);\n\n/**\n * `animate(time)` maps the interval [Time.zero, time] to the interval [0., 1.]\n *\n * Applied with a time value < `Time.zero` will produce `(0., Delayed)`.\n * Applied with a time value > `time` will produce `(1., Complete(time))`.\n * Applied with a time value > `Time.zero` and < `time` will produce\n * `(value, Running(value))` where `value` is in the interval [0., 1.].\n *\n * Examples:\n *   (animate(Time.seconds(5)) |> valueAt(Time.zero) :> float) == 0.0,\n *   (animate(Time.seconds(5)) |> valueAt(Time.seconds(-42)) :> float) == 0.0,\n *   (animate(Time.seconds(5)) |> valueAt(Time.seconds(1)) :> float) == 0.2,\n *   (animate(Time.seconds(5)) |> valueAt(Time.seconds(42)) :> float) == 1.0,\n *   animate(Time.seconds(5)) |> stateAt(Time.zero) == Running,\n *   animate(Time.seconds(5)) |> stateAt(Time.seconds(-42)) == Delayed,\n *   animate(Time.seconds(5)) |> stateAt(Time.seconds(1)) == Running,\n *   animate(Time.seconds(5)) |> stateAt(Time.seconds(42)) == Complete(Time.seconds(5)),\n */\nlet animate: Time.t => t(NormalizedTime.t);\n\n/**\n * `delay(time)` will effectively extend the the reuslt of applying `Time.zero`\n *\n * Examples:\n *   (animate(Time.seconds(1)) |> delay(Time.seconds(1)) |> valueAt(Time.seconds(-2)) :> float) == 0.0,\n *   (animate(Time.seconds(1)) |> delay(Time.seconds(1)) |> valueAt(Time.ms(750)) :> float) == 0.0,\n *   (animate(Time.seconds(1)) |> delay(Time.seconds(1)) |> valueAt(Time.ms(1500)) :> float) == 0.5,\n *   (animate(Time.seconds(1)) |> delay(Time.seconds(1)) |> valueAt(Time.ms(4660)) :> float) == 1.0,\n *   animate(Time.seconds(1)) |> delay(Time.seconds(1)) |> stateAt(Time.ms(-2)) == Delayed,\n *   animate(Time.seconds(1)) |> delay(Time.seconds(1)) |> stateAt(Time.ms(750)) == Delayed,\n *   animate(Time.seconds(1)) |> delay(Time.seconds(1)) |> stateAt(Time.ms(1500)) == Running,\n *   animate(Time.seconds(1)) |> delay(Time.seconds(1)) |> stateAt(Time.ms(4660)) == Complete(Time.seconds(2)),\n */\nlet delay: (Time.t, t('a)) => t('a);\n\n/**\n * `repeat` will repeat the animation ad infinitum\n *\n * Note that it will not repat \"backwards\", that is, in negative time.\n *\n * Examples:\n *   (animate(Time.seconds(1)) |> repeat |> valueAt(Time.seconds(-2)) :> float) == 0.0,\n *   (animate(Time.seconds(1)) |> repeat |> valueAt(Time.ms(750)) :> float) == 0.75,\n *   (animate(Time.seconds(1)) |> repeat |> valueAt(Time.ms(1750)) :> float) == 0.75,\n *   (animate(Time.seconds(1)) |> repeat |> valueAt(Time.ms(3660)) :> float) =~. 0.66,\n *   (animate(Time.seconds(1)) |> repeat |> valueAt(Time.ms(4660)) :> float) =~. 0.66,\n *   animate(Time.seconds(1)) |> repeat |> stateAt(Time.seconds(-2)) == Delayed,\n *   animate(Time.seconds(1)) |> repeat |> stateAt(Time.ms(4660)) == Running,\n */\nlet repeat: t('a) => t('a);\n\n/**\n * `alternatingRepeat` will repeat the animation back and forth ad infinitum\n *\n * Note that it will not repat \"backwards\", that is, in negative time.\n *\n * Examples:\n *   (animate(Time.seconds(1)) |> alternatingRepeat |> valueAt(Time.seconds(-2.)) :> float) == 0.0,\n *   (animate(Time.seconds(1)) |> alternatingRepeat |> valueAt(Time.ms(750)) :> float) == 0.75,\n *   (animate(Time.seconds(1)) |> alternatingRepeat |> valueAt(Time.ms(1750)) :> float) == 0.25,\n *   (animate(Time.seconds(1)) |> alternatingRepeat |> valueAt(Time.ms(3660)) :> float) =~. 0.34,\n *   (animate(Time.seconds(1)) |> alternatingRepeat |> valueAt(Time.ms(4660)) :> float) =~. 0.66,\n *   animate(Time.seconds(1)) |> alternatingRepeat |> stateAt(Time.seconds(-2)) == Delayed,\n *   animate(Time.seconds(1)) |> alternatingRepeat |> stateAt(Time.ms(4660)) == Running,\n */\nlet alternatingRepeat: t('a) => t('a);\n\n/**\n * `ease(start, end)` will transform the animated value according to the given easing function.\n *\n * Note that easing only works in normalized time space, and therefore requires\n * an animation in normalized time even if the easing functions themselves don't.\n *\n * Examples:\n *   (animate(Time.seconds(1)) |> ease(Easing.quadratic) |> valueAt(Time.seconds(-2)) :> float) == 0.0,\n *   (animate(Time.seconds(1)) |> ease(Easing.quadratic) |> valueAt(Time.ms(250)) :> float) == 0.0625,\n *   (animate(Time.seconds(1)) |> ease(Easing.quadratic) |> valueAt(Time.ms(500)) :> float) == 0.25,\n *   (animate(Time.seconds(1)) |> ease(Easing.quadratic) |> valueAt(Time.ms(750)) :> float) =~. 0.5625,\n *   (animate(Time.seconds(1)) |> ease(Easing.quadratic) |> valueAt(Time.ms(4660)) :> float) =~. 1.0,\n */\nlet ease: (Easing.t, t(NormalizedTime.t)) => t(NormalizedTime.t);\n\n/**\n * `tween(start, end)` maps normalized time to the interval [start, end]\n *\n * Examples:\n *   animate(Time.seconds(1)) |> tween(2., 5.) |> valueAt(Time.zero) == 2.0,\n *   animate(Time.seconds(1)) |> tween(2., 5.) |> valueAt(Time.ms(500)) == 3.5,\n *   animate(Time.seconds(1)) |> tween(2., 5.) |> valueAt(Time.seconds(-42)) == 2.0,\n *   animate(Time.seconds(1)) |> tween(2., 5.) |> valueAt(Time.seconds(42)) == 5.0,\n */\nlet tween: (float, float, t(NormalizedTime.t)) => t(float);\n\n/**\n * `map(f)` maps the animated value according to the given function `f`\n *\n * Examples:\n *   animate(Time.seconds(1)) |> map(n => string_of_float(n: NormalizedTime.t :> float)) |> valueAt(Time.ms(330)) == \"0.33\",\n */\nlet map: ('a => 'b, t('a)) => t('b);\n\n/**\n * `andThen(a, ~next:b)` runs `a` and `b` in sequence.\n *\n * Examples:\n *   (animate(Time.seconds(1)) |> andThen(~next=animate(Time.seconds(2))) |> valueAt(Time.seconds(1)) :> float) == 1.0,\n *   (animate(Time.seconds(1)) |> andThen(~next=animate(Time.seconds(2))) |> valueAt(Time.seconds(2)) :> float) == 0.5,\n */\nlet andThen: (t('a), ~next: t('a)) => t('a);\n\n/**\n * `zip((a, b))` runs `a` and `b` in parallel and returns their values as a tuple.\n *\n * Examples:\n *   (zip((animate(Time.seconds(1)), animate(Time.seconds(2)))) |> valueAt(Time.seconds(1)) |> fst :> float) == 1.0,\n *   (zip((animate(Time.seconds(1)), animate(Time.seconds(2)))) |> valueAt(Time.seconds(1)) |> snd :> float) == 0.5,\n */\nlet zip: ((t('a), t('b))) => t(('a, 'b));\n\n/**\n * `apply(time)` returns the vale and state of the animation at the given `time`\n *\n * Examples:\n *   (animate(Time.seconds(1)) |> apply(Time.ms(330)) |> fst :> float) == 0.33,\n *   animate(Time.seconds(1)) |> apply(Time.ms(330)) |> snd == Running,\n */\nlet apply: (Time.t, t('a)) => ('a, state);\n\n/**\n * `valueAt(time)` returns the value of the animation at the given `time`\n *\n * Examples:\n *   (animate(Time.seconds(1)) |> valueAt(Time.ms(330)) :> float) == 0.33,\n */\nlet valueAt: (Time.t, t('a)) => 'a;\n\n/**\n * `stateAt(time)` returns the state of the animation at the given `time`\n *\n * Examples:\n *   animate(Time.seconds(1)) |> stateAt(Time.ms(330)) == Running,\n */\nlet stateAt: (Time.t, t('a)) => state;\n"
  },
  {
    "path": "src/UI/CanvasNode.re",
    "content": "module Layout = Layout;\nmodule LayoutTypes = Layout.LayoutTypes;\nmodule CanvasContext = Revery_Draw.CanvasContext;\n\nopen ViewNode;\n\n/*\n * CanvasNode\n *\n * Very simple node that just takes in a `render` callback\n * and calls it during draw with a canvas context -\n * enabling the use of arbitrary canvas functions.\n */\nclass canvasNode (()) = {\n  as _this;\n  val mutable render: option((CanvasContext.t, Dimensions.t) => unit) = None;\n  inherit (class viewNode)() as _super;\n  pub! draw = (parentContext: NodeDrawContext.t) => {\n    _super#draw(parentContext);\n\n    let world = _this#getWorldTransform();\n    let dimensions = _this#measurements();\n    let canvas = parentContext.canvas;\n\n    switch (render) {\n    | Some(render) =>\n      let _: int = CanvasContext.save(canvas);\n\n      CanvasContext.setMatrix(canvas, world);\n\n      Overflow.render(canvas, LayoutTypes.Hidden, dimensions, () =>\n        render(canvas, dimensions)\n      );\n\n      CanvasContext.restore(canvas);\n\n    | None => ()\n    };\n  };\n  pub setRender = r => render = r;\n};\n"
  },
  {
    "path": "src/UI/Container.re",
    "content": "type state = {\n  rendered: React.RenderedElement.t(React.reveryNode, React.reveryNode),\n  previousElement: React.element(React.reveryNode),\n};\n\ntype t = {\n  node: React.reveryNode,\n  state: option(state),\n};\n\nlet create: React.reveryNode => t = n => {node: n, state: None};\n\nlet update: (t, React.element(React.reveryNode)) => t =\n  ({node, state}, element) => {\n    let newRendered =\n      switch (state) {\n      | None =>\n        let updates =\n          React.RenderedElement.render(\n            {\n              node,\n              insertNode: React.insertNode,\n              deleteNode: React.deleteNode,\n              moveNode: React.moveNode,\n            },\n            element,\n          );\n        React.RenderedElement.executeHostViewUpdates(updates) |> ignore;\n        updates |> React.RenderedElement.executePendingEffects;\n      | Some(s) =>\n        let nextElement =\n          React.RenderedElement.update(\n            ~previousElement=s.previousElement,\n            ~renderedElement=s.rendered,\n            element,\n          );\n        let nextElement =\n          React.RenderedElement.flushPendingUpdates(nextElement);\n\n        React.RenderedElement.executeHostViewUpdates(nextElement) |> ignore;\n\n        React.RenderedElement.executePendingEffects(nextElement);\n      };\n\n    let ret: t = {\n      node,\n      state: Some({rendered: newRendered, previousElement: element}),\n    };\n    ret;\n  };\n"
  },
  {
    "path": "src/UI/Dimensions.re",
    "content": "type t = {\n  top: int,\n  left: int,\n  width: int,\n  height: int,\n};\n\nlet create = (~top, ~left, ~width, ~height, ()) => {\n  top,\n  left,\n  width,\n  height,\n};\n"
  },
  {
    "path": "src/UI/Easing.re",
    "content": "type t = float => float;\n\nlet linear = (t: float) => t;\nlet quadratic = (t: float) => t *. t;\nlet cubic = (t: float) => t *. t *. t;\nlet cubicBezier = Rebez.make;\n// From https://developer.mozilla.org/en-US/docs/Web/CSS/timing-function#Keywords_for_common_cubic-bezier_timing_functions\nlet ease = cubicBezier(0.25, 0.1, 0.25, 1.0);\nlet easeIn = cubicBezier(0.42, 0.0, 1.0, 1.0);\nlet easeOut = cubicBezier(0.0, 0.0, 0.58, 1.0);\nlet easeInOut = cubicBezier(0.42, 0.0, 0.58, 1.0);\n"
  },
  {
    "path": "src/UI/FileDrop.re",
    "content": "open Revery_Core;\n\nopen UiEvents;\nopen NodeEvents;\n\nmodule Log = (val Log.withNamespace(\"Revery.UI.FileDropped\"));\n\nlet internalToExternalEvent = (evt: Events.internalFileDropEvents) =>\n  switch (evt) {\n  | InternalFileDropped(evt) =>\n    FileDropped({\n      mouseX: evt.mouseX,\n      mouseY: evt.mouseY,\n      paths: evt.paths,\n      ctrlKey: Key.Keymod.isControlDown(evt.keymod),\n      altKey: Key.Keymod.isAltDown(evt.keymod),\n      shiftKey: Key.Keymod.isShiftDown(evt.keymod),\n      guiKey: Key.Keymod.isGuiDown(evt.keymod),\n    })\n  };\n\nlet getPositionFromInternalEvent = (evt: Events.internalFileDropEvents) =>\n  switch (evt) {\n  | InternalFileDropped({mouseX, mouseY, _}) => (mouseX, mouseY)\n  };\n\nlet dispatch = (evt: Events.internalFileDropEvents, node: Node.node) => {\n  let eventToSend = internalToExternalEvent(evt);\n  Log.tracef(m =>\n    m(\n      \"Dispatching event from node %i: %s\",\n      node#getInternalId(),\n      NodeEvents.show_event(eventToSend),\n    )\n  );\n\n  if (node#hasRendered()) {\n    let (mouseX, mouseY) = getPositionFromInternalEvent(evt);\n\n    let deepestNode = getTopMostNode(node, mouseX, mouseY);\n\n    switch (deepestNode) {\n    | Some(node) => bubble(node, eventToSend)\n    | None => ()\n    };\n  };\n};\n"
  },
  {
    "path": "src/UI/Focus.re",
    "content": "open Revery_Core;\nopen NodeEvents;\n\ntype active = {\n  handler: event => unit,\n  id: int,\n};\ntype focused = ref(option(active));\nlet focused: focused = ref(None);\n\nmodule Log = (val Log.withNamespace(\"Revery.UI.Focus\"));\n\n/* Should happen when user clicks anywhere where no focusable node exists */\nlet loseFocus = () => {\n  Log.trace(\"loseFocus()\");\n\n  switch (focused^) {\n  | Some({handler, _}) =>\n    handler(Blur);\n    focused := None;\n  | None => ()\n  };\n  // If there is an active window, with text input active, turn off text input\n};\n\nlet isFocused = (node: Node.node) =>\n  switch (focused^) {\n  | Some({id, _}) => node#getInternalId() == id\n  | None => false\n  };\n\n/* TODO perform checks if a node can be focused ? */\nlet focus = (node: Node.node) =>\n  if (!isFocused(node)) {\n    loseFocus();\n\n    Log.trace(\"focus()\");\n    node#handleEvent(Focus);\n    focused := Some({handler: node#handleEvent, id: node#getInternalId()});\n  };\n\nlet dispatch = event =>\n  switch (focused^) {\n  | Some({handler, _}) => handler(event)\n  | None => ()\n  };\n"
  },
  {
    "path": "src/UI/Focus.rei",
    "content": "open NodeEvents;\n\nlet loseFocus: unit => unit;\n\nlet isFocused: Node.node => bool;\n\nlet focus: Node.node => unit;\n\nlet dispatch: event => unit;\n"
  },
  {
    "path": "src/UI/HitTest.re",
    "content": "module Window = Revery_Core.Window;\nopen UiEvents;\n\nmodule Log = (val Revery_Core.Log.withNamespace(\"Revery.Ui.HitTest\"));\n\nlet windowCallback = (node, window) =>\n  Some(\n    (sdlWindow, mouseX, mouseY) =>\n      Sdl2.Window.(\n        if (getId(sdlWindow) == getId(Window.getSdlWindow(window))\n            && node#hasRendered()) {\n          let scaleAndZoomFactor = Window.getScaleAndZoom(window);\n          let deepestNode =\n            getTopMostNode(\n              node,\n              float(mouseX) /. scaleAndZoomFactor,\n              float(mouseY) /. scaleAndZoomFactor,\n            );\n          switch (deepestNode) {\n          | Some(node) => node#getMouseBehavior()\n          | None => Normal\n          };\n        } else {\n          Normal;\n        }\n      ),\n  );\n"
  },
  {
    "path": "src/UI/ImageNode.re",
    "content": "module Draw = Revery_Draw;\n\nmodule Layout = Layout;\nmodule LayoutTypes = Layout.LayoutTypes;\n\nopen Node;\n\nlet uiToDrawResizeMode: ImageResizeMode.t => Revery_Draw.ImageResizeMode.t =\n  rm =>\n    switch (rm) {\n    | Stretch => Revery_Draw.ImageResizeMode.Stretch\n    | Repeat => Revery_Draw.ImageResizeMode.Repeat\n    };\n\nclass imageNode (data: option(Skia.Image.t)) = {\n  as _this;\n  val mutable data = data;\n  inherit (class node)() as _super;\n  val mutable _opacity = 1.0;\n  val mutable _resizeMode = ImageResizeMode.Stretch;\n  val mutable _maybeWidth = None;\n  val mutable _maybeHeight = None;\n  val _paint = Skia.Paint.make();\n  pub! draw = (parentContext: NodeDrawContext.t) => {\n    /* Draw background first */\n    _super#draw(parentContext);\n    let dimensions = _this#measurements();\n    let world = _this#getWorldTransform();\n\n    let {canvas, _}: NodeDrawContext.t = parentContext;\n\n    Skia.Paint.setAlpha(_paint, _opacity *. parentContext.opacity);\n\n    // TODO find a way to only manage the matrix stack in Node\n    Revery_Draw.CanvasContext.setMatrix(canvas, world);\n\n    switch (data) {\n    | Some(data) =>\n      Draw.CanvasContext.drawImage(\n        ~x=0.,\n        ~y=0.,\n        ~width=float_of_int(dimensions.width),\n        ~height=float_of_int(dimensions.height),\n        ~paint=_paint,\n        data,\n        canvas,\n      )\n    | None => ()\n    };\n  };\n  pub setOpacity = f => _opacity = f;\n  pub setResizeMode = (mode: ImageResizeMode.t) => {\n    _resizeMode = mode;\n  };\n  pub setQuality = quality => {\n    Skia.Paint.setFilterQuality(_paint, quality);\n  };\n  pub! setStyle = style => {\n    // If neither the height and width are defined, then\n    // use the image size as the default height and width.\n\n    let adjustedStyle =\n      Layout.Encoding.(\n        {\n          let noDimensionsSet =\n            style.width == cssUndefined && style.height == cssUndefined;\n\n          Style.{\n            ...style,\n            width:\n              noDimensionsSet\n                ? _maybeWidth |> Option.value(~default=cssUndefined)\n                : style.width,\n            height:\n              noDimensionsSet\n                ? _maybeHeight |> Option.value(~default=cssUndefined)\n                : style.height,\n          };\n        }\n      );\n    _super#setStyle(adjustedStyle);\n  };\n  pub setData = maybeImg => {\n    data = maybeImg;\n    _maybeWidth = maybeImg |> Option.map(Skia.Image.width);\n    _maybeHeight = maybeImg |> Option.map(Skia.Image.height);\n    _this#setStyle(_super#getStyle());\n  };\n};\n"
  },
  {
    "path": "src/UI/ImageResizeMode.re",
    "content": "type t =\n  | Stretch\n  | Repeat;\n"
  },
  {
    "path": "src/UI/Keyboard.re",
    "content": "open Revery_Core;\nopen NodeEvents;\n\nlet internalToExternalEvent =\n    (event: Revery_Core.Events.internalKeyboardEvent) =>\n  switch (event) {\n  | InternalTextEditEvent({text, start, length}) =>\n    TextEdit({text, start, length})\n  | InternalTextInputEvent(e) => TextInput({text: e.text})\n  | InternalKeyUpEvent(e) =>\n    KeyUp({\n      keycode: e.keycode,\n      scancode: e.scancode,\n      repeat: e.repeat,\n      keymod: e.keymod,\n      ctrlKey: Key.Keymod.isControlDown(e.keymod),\n      altKey: Key.Keymod.isAltDown(e.keymod),\n      shiftKey: Key.Keymod.isShiftDown(e.keymod),\n      guiKey: Key.Keymod.isGuiDown(e.keymod),\n    })\n  | InternalKeyDownEvent(e) =>\n    KeyDown({\n      keycode: e.keycode,\n      scancode: e.scancode,\n      repeat: e.repeat,\n      keymod: e.keymod,\n      ctrlKey: Key.Keymod.isControlDown(e.keymod),\n      altKey: Key.Keymod.isAltDown(e.keymod),\n      shiftKey: Key.Keymod.isShiftDown(e.keymod),\n      guiKey: Key.Keymod.isGuiDown(e.keymod),\n    })\n  };\n\nlet dispatch = (event: Revery_Core.Events.internalKeyboardEvent) => {\n  let eventToSend = internalToExternalEvent(event);\n\n  Focus.dispatch(eventToSend);\n};\n"
  },
  {
    "path": "src/UI/LayerNode.re",
    "content": "open Revery_Core;\nopen Revery_Draw;\n\nmodule Layout = Layout;\nmodule LayoutTypes = Layout.LayoutTypes;\n\nopen ViewNode;\n\nclass layerNode (condition: RenderCondition.t) = {\n  as _this;\n  inherit (class viewNode)() as _super;\n  val mutable _backgroundColor: Skia.Color.t = Colors.white |> Color.toSkia;\n  val mutable _lastCondition: option(RenderCondition.t) = None;\n  val mutable _condition: option(RenderCondition.t) = Some(condition);\n  val mutable _maybeCanvas: option(CanvasContext.t) = None;\n  val mutable _lastRenderTime: option(float) = None;\n  // MUTABLE\n  val _inverseWorld = Skia.Matrix.make(); // The inverseWorld is used to 'undo' the world transform to give the interior contents a blank slate\n  val _layerPaint = Skia.Paint.make();\n  pri createOrInitializeLayer =\n      (\n        ~width,\n        ~height,\n        {canvas, dpi, canvasScalingFactor, _}: NodeDrawContext.t,\n      ) => {\n    let adjustedWidth =\n      int_of_float(float(width) *. dpi *. canvasScalingFactor +. 0.5);\n    let adjustedHeight =\n      int_of_float(float(height) *. dpi *. canvasScalingFactor +. 0.5);\n\n    switch (_maybeCanvas) {\n    | None =>\n      _maybeCanvas =\n        CanvasContext.createLayer(\n          ~width=Int32.of_int(adjustedWidth),\n          ~height=Int32.of_int(adjustedHeight),\n          canvas,\n        );\n\n      true;\n    | Some(canvas) =>\n      let currentWidth = CanvasContext.width(canvas);\n      let currentHeight = CanvasContext.height(canvas);\n\n      if (currentWidth < adjustedWidth || currentHeight < adjustedHeight) {\n        _maybeCanvas =\n          CanvasContext.createLayer(\n            ~width=Int32.of_int(adjustedWidth),\n            ~height=Int32.of_int(adjustedHeight),\n            canvas,\n          );\n        true;\n      } else {\n        false;\n      };\n    };\n  };\n  pri debugDraw = (~layerCanvas, width, height, canvas) => {\n    let currentTime = Unix.gettimeofday();\n    switch (_lastRenderTime) {\n    | None => ()\n    | Some(lastTime) =>\n      let diff = currentTime -. lastTime;\n\n      let debugPaint = Skia.Paint.make();\n      if (diff < 0.25) {\n        let color =\n          Colors.red |> Color.multiplyAlpha(0.6 -. diff) |> Color.toSkia;\n        Skia.Paint.setColor(debugPaint, color);\n\n        CanvasContext.drawRectLtwh(\n          ~paint=debugPaint,\n          ~left=0.,\n          ~top=0.,\n          ~width=float(width),\n          ~height=float(height),\n          canvas,\n        );\n      } else {\n        let color = Colors.green |> Color.multiplyAlpha(0.5) |> Color.toSkia;\n        Skia.Paint.setColor(debugPaint, color);\n        CanvasContext.drawRectLtwh(\n          ~paint=debugPaint,\n          ~left=0.,\n          ~top=0.,\n          ~width=float(width),\n          ~height=float(height),\n          canvas,\n        );\n      };\n\n      let textPaint = Skia.Paint.make();\n      Skia.Paint.setColor(textPaint, Colors.white |> Color.toSkia);\n      CanvasContext.drawText(\n        ~paint=textPaint,\n        ~x=0.,\n        ~y=0.,\n        ~text=\"Unique ID: \" ++ string_of_int(_super#getInternalId()),\n        canvas,\n      );\n      CanvasContext.drawText(\n        ~paint=textPaint,\n        ~x=0.,\n        ~y=20.,\n        ~text=\n          Printf.sprintf(\n            \"Layer Dimensions: %dx%d\",\n            CanvasContext.width(layerCanvas),\n            CanvasContext.height(layerCanvas),\n          ),\n        canvas,\n      );\n      CanvasContext.drawText(\n        ~paint=textPaint,\n        ~x=0.,\n        ~y=40.,\n        ~text=Printf.sprintf(\"Element Dimensions: %dx%d\", width, height),\n        canvas,\n      );\n    };\n  };\n  pub! draw =\n       (\n         {canvas, opacity, dpi, canvasScalingFactor, _} as parentContext: NodeDrawContext.t,\n       ) => {\n    let dimensions = _this#measurements();\n    let world = _this#getWorldTransform();\n\n    let wasRecreated =\n      _this#createOrInitializeLayer(\n        ~width=dimensions.width,\n        ~height=dimensions.height,\n        parentContext,\n      );\n\n    let redraw =\n      wasRecreated\n      || RenderCondition.shouldRenderOpt(_lastCondition, _condition);\n\n    let totalScaleFactor = dpi *. canvasScalingFactor;\n    switch (_maybeCanvas) {\n    | None => ()\n    | Some(layerCanvas) =>\n      // Draw the 'inside' of the layer - we only need to do this if\n      // the render condition `shouldRender` is true.\n      if (redraw) {\n        _lastCondition = _condition;\n        _lastRenderTime = Some(Unix.gettimeofday());\n\n        // We need to 'undo' the world transform, to transform the children\n        // back into a coordinate space where [0, 0] is the layer space.\n        let _: bool = Skia.Matrix.invert(world, _inverseWorld);\n\n        // But reapply the root scaling transform...\n        let skiaRoot =\n          Skia.Matrix.makeScale(totalScaleFactor, totalScaleFactor, 0., 0.);\n        Skia.Matrix.postConcat(_inverseWorld, skiaRoot);\n\n        let newContext: NodeDrawContext.t = {\n          ...parentContext,\n          canvas: layerCanvas,\n          opacity: 1.0,\n          zIndex: 0,\n        };\n\n        CanvasContext.clear(~color=_backgroundColor, layerCanvas);\n        CanvasContext.setRootTransform(_inverseWorld, layerCanvas);\n\n        _super#draw(newContext);\n\n        CanvasContext.flush(layerCanvas);\n      };\n\n      // Draw the cached layer. We always have to do this, every frame.\n      let drawRoot =\n        Skia.Matrix.makeScale(\n          1. /. totalScaleFactor,\n          1. /. totalScaleFactor,\n          0.,\n          0.,\n        );\n      Skia.Matrix.postConcat(drawRoot, world);\n      Revery_Draw.CanvasContext.setMatrix(canvas, drawRoot);\n\n      Skia.Paint.setColor(\n        _layerPaint,\n        Colors.white |> Color.multiplyAlpha(opacity) |> Color.toSkia,\n      );\n\n      let clippingRect =\n        Skia.Rect.makeLtrb(\n          0.,\n          0.,\n          float_of_int(dimensions.width) *. dpi *. canvasScalingFactor,\n          float_of_int(dimensions.height) *. dpi *. canvasScalingFactor,\n        );\n\n      let _save: int = Revery_Draw.CanvasContext.save(canvas);\n      let () = Revery_Draw.CanvasContext.clipRect(canvas, clippingRect);\n      // [x] and [y] are 0. because this is accounted for in the world transform\n      CanvasContext.drawLayer(\n        ~paint=_layerPaint,\n        ~layer=layerCanvas,\n        ~x=0.,\n        ~y=0.,\n        canvas,\n      );\n      let () = Revery_Draw.CanvasContext.restore(canvas);\n\n      if (parentContext.debug) {\n        Revery_Draw.CanvasContext.setMatrix(canvas, world);\n        _this#debugDraw(\n          ~layerCanvas,\n          dimensions.width,\n          dimensions.height,\n          canvas,\n        );\n      };\n    };\n  };\n  pub setCondition = (condition: RenderCondition.t) => {\n    _condition = Some(condition);\n  };\n  pub setBackgroundColor = (color: Color.t) => {\n    _backgroundColor = color |> Color.toSkia;\n  };\n};\n"
  },
  {
    "path": "src/UI/Layout.re",
    "content": "open Revery_Core;\n\nopen Flex;\n\nmodule Node = {\n  type context = ref(int);\n  /* Ignored - only needed to create the dummy instance. */\n  let nullContext = {contents: 0};\n};\n\nlet rootContext = {contents: 0};\n\nmodule Encoding = FixedEncoding;\nmodule LayoutTestUtils = LayoutTestUtils.Create(Node, Encoding);\nmodule Layout = Layout.Create(Node, Encoding);\nmodule LayoutPrint = LayoutPrint.Create(Node, Encoding);\nmodule LayoutSupport = Layout.LayoutSupport;\nmodule LayoutTypes = LayoutTypes.Create(Node, Encoding);\n\nlet defaultStyle = LayoutSupport.defaultStyle;\nlet createNode = (children, style) =>\n  LayoutSupport.createNode(\n    ~withChildren=children,\n    ~andStyle=style,\n    rootContext,\n  );\nlet createNodeWithMeasure = (children, style, measure) =>\n  LayoutSupport.createNode(\n    ~withChildren=children,\n    ~andStyle=style,\n    ~andMeasure=measure,\n    rootContext,\n  );\nlet layout = (~force=false, node) =>\n  Performance.bench(\"layout\", () => {\n    let layoutNode = node#toLayoutNode(~force, ());\n    Layout.layoutNode(\n      layoutNode,\n      Encoding.cssUndefined,\n      Encoding.cssUndefined,\n      Ltr,\n    );\n  });\nlet printCssNode = root =>\n  LayoutPrint.printCssNode((\n    root,\n    {printLayout: true, printChildren: true, printStyle: true},\n  ));\n"
  },
  {
    "path": "src/UI/Mouse.re",
    "content": "/* Mouse Input */\nopen Revery_Core;\nopen Revery_Draw;\n\nopen UiEvents;\nopen NodeEvents;\n\nmodule Log = (val Log.withNamespace(\"Revery.UI.Mouse\"));\n\nmodule Cursor = {\n  /* State needed to track on the cursor */\n  type t = {\n    mutable x: float,\n    mutable y: float,\n  };\n\n  let make = () => {x: 0., y: 0.};\n\n  let get = cursor => (cursor.x, cursor.y);\n\n  let set = (~x, ~y, c) => {\n    c.x = x;\n    c.y = y;\n  };\n};\n\nmodule Capture: {\n  let set:\n    (\n      ~onRelease: unit => unit=?,\n      ~onMouseDown: NodeEvents.mouseDownHandler=?,\n      ~onMouseUp: NodeEvents.mouseUpHandler=?,\n      ~onMouseMove: NodeEvents.mouseMoveHandler=?,\n      ~onMouseWheel: NodeEvents.mouseWheelHandler=?,\n      Window.t\n    ) =>\n    unit;\n  let release: unit => unit;\n  let isSet: unit => bool;\n  let dispatch: NodeEvents.event => unit;\n} = {\n  type t = {\n    onMouseDown: mouseDownHandler,\n    onMouseUp: mouseUpHandler,\n    onMouseMove: mouseMoveHandler,\n    onMouseWheel: mouseWheelHandler,\n    dispose: unit => unit,\n  };\n\n  let currentState = ref(None);\n\n  let update = maybeState => {\n    switch (currentState^) {\n    | Some({dispose, _}) => dispose()\n    | None => ()\n    };\n    currentState := maybeState;\n  };\n\n  let set =\n      (\n        ~onRelease=() => (),\n        ~onMouseDown=_evt => (),\n        ~onMouseUp=_evt => (),\n        ~onMouseMove=_evt => (),\n        ~onMouseWheel=_evt => (),\n        window,\n      ) => {\n    ignore(Sdl2.Mouse.capture(true): int);\n    let unsubscribe = Window.onFocusLost(window, () => update(None));\n\n    update(\n      Some({\n        onMouseDown,\n        onMouseUp,\n        onMouseMove,\n        onMouseWheel,\n        dispose: () => {\n          unsubscribe();\n          onRelease();\n        },\n      }),\n    );\n  };\n\n  let release = () => {\n    ignore(Sdl2.Mouse.capture(false): int);\n    update(None);\n  };\n\n  let isSet = () => currentState^ != None;\n\n  let dispatch = event =>\n    switch (currentState^) {\n    | Some(state) =>\n      switch (event) {\n      | MouseDown(params) => state.onMouseDown(params)\n      | MouseUp(params) => state.onMouseUp(params)\n      | MouseMove(params) => state.onMouseMove(params)\n      | MouseWheel(params) => state.onMouseWheel(params)\n      | _ => ()\n      }\n\n    | None => ()\n    };\n};\n\nlet releaseCapture = Capture.release;\nlet setCapture = Capture.set;\n\nlet getPositionFromMouseEvent = (c: Cursor.t, evt: Events.internalMouseEvents) =>\n  switch (evt) {\n  | InternalMouseDown(_) => Cursor.get(c)\n  | InternalMouseMove(e) => (e.mouseX, e.mouseY)\n  | InternalMouseUp(_) => Cursor.get(c)\n  | InternalMouseWheel(e) => (e.mouseX, e.mouseY)\n  | InternalMouseEnter(_) => Cursor.get(c)\n  | InternalMouseLeave(_) => Cursor.get(c)\n  | InternalMouseOver(_) => Cursor.get(c)\n  | InternalMouseOut(_) => Cursor.get(c)\n  };\n\nlet internalToExternalEvent = (c: Cursor.t, evt: Events.internalMouseEvents) =>\n  switch (evt) {\n  | InternalMouseDown(evt) =>\n    MouseDown({\n      mouseX: c.x,\n      mouseY: c.y,\n      button: evt.button,\n      ctrlKey: Key.Keymod.isControlDown(evt.keymod),\n      altKey: Key.Keymod.isAltDown(evt.keymod),\n      shiftKey: Key.Keymod.isShiftDown(evt.keymod),\n      guiKey: Key.Keymod.isGuiDown(evt.keymod),\n    })\n  | InternalMouseUp(evt) =>\n    MouseUp({\n      mouseX: c.x,\n      mouseY: c.y,\n      button: evt.button,\n      ctrlKey: Key.Keymod.isControlDown(evt.keymod),\n      altKey: Key.Keymod.isAltDown(evt.keymod),\n      shiftKey: Key.Keymod.isShiftDown(evt.keymod),\n      guiKey: Key.Keymod.isGuiDown(evt.keymod),\n    })\n  | InternalMouseMove(evt) =>\n    MouseMove({\n      mouseX: evt.mouseX,\n      mouseY: evt.mouseY,\n      ctrlKey: Key.Keymod.isControlDown(evt.keymod),\n      altKey: Key.Keymod.isAltDown(evt.keymod),\n      shiftKey: Key.Keymod.isShiftDown(evt.keymod),\n      guiKey: Key.Keymod.isGuiDown(evt.keymod),\n    })\n  | InternalMouseWheel(evt) =>\n    MouseWheel({\n      deltaX: evt.deltaX,\n      deltaY: evt.deltaY,\n      ctrlKey: Key.Keymod.isControlDown(evt.keymod),\n      altKey: Key.Keymod.isAltDown(evt.keymod),\n      shiftKey: Key.Keymod.isShiftDown(evt.keymod),\n      guiKey: Key.Keymod.isGuiDown(evt.keymod),\n    })\n  | InternalMouseEnter(evt) =>\n    MouseEnter({\n      mouseX: evt.mouseX,\n      mouseY: evt.mouseY,\n      ctrlKey: Key.Keymod.isControlDown(evt.keymod),\n      altKey: Key.Keymod.isAltDown(evt.keymod),\n      shiftKey: Key.Keymod.isShiftDown(evt.keymod),\n      guiKey: Key.Keymod.isGuiDown(evt.keymod),\n    })\n  | InternalMouseLeave(evt) =>\n    MouseLeave({\n      mouseX: evt.mouseX,\n      mouseY: evt.mouseY,\n      ctrlKey: Key.Keymod.isControlDown(evt.keymod),\n      altKey: Key.Keymod.isAltDown(evt.keymod),\n      shiftKey: Key.Keymod.isShiftDown(evt.keymod),\n      guiKey: Key.Keymod.isGuiDown(evt.keymod),\n    })\n  | InternalMouseOver(evt) =>\n    MouseOver({\n      mouseX: evt.mouseX,\n      mouseY: evt.mouseY,\n      ctrlKey: Key.Keymod.isControlDown(evt.keymod),\n      altKey: Key.Keymod.isAltDown(evt.keymod),\n      shiftKey: Key.Keymod.isShiftDown(evt.keymod),\n      guiKey: Key.Keymod.isGuiDown(evt.keymod),\n    })\n  | InternalMouseOut(evt) =>\n    MouseOut({\n      mouseX: evt.mouseX,\n      mouseY: evt.mouseY,\n      ctrlKey: Key.Keymod.isControlDown(evt.keymod),\n      altKey: Key.Keymod.isAltDown(evt.keymod),\n      shiftKey: Key.Keymod.isShiftDown(evt.keymod),\n      guiKey: Key.Keymod.isGuiDown(evt.keymod),\n    })\n  };\n\nlet onCursorChanged: Event.t(MouseCursors.t) = Event.create();\n\nlet isMouseDownEv =\n  fun\n  | MouseDown(_) => true\n  | _ => false;\n\nlet isMouseMoveEv =\n  fun\n  | MouseMove(_) => true\n  | _ => false;\n\nlet isMouseWheelEv =\n  fun\n  | MouseWheel(_) => true\n  | _ => false;\n\nlet storedNodesUnderCursor = ref([]);\n\nlet getMouseMoveEventParams =\n    (cursor: Cursor.t, evt: Events.internalMouseEvents) =>\n  switch (evt) {\n  | InternalMouseMove(evt) => {\n      mouseX: evt.mouseX,\n      mouseY: evt.mouseY,\n      ctrlKey: Key.Keymod.isControlDown(evt.keymod),\n      altKey: Key.Keymod.isAltDown(evt.keymod),\n      shiftKey: Key.Keymod.isShiftDown(evt.keymod),\n      guiKey: Key.Keymod.isGuiDown(evt.keymod),\n    }\n  | InternalMouseDown(evt)\n  | InternalMouseUp(evt) => {\n      mouseX: cursor.x,\n      mouseY: cursor.y,\n      ctrlKey: Key.Keymod.isControlDown(evt.keymod),\n      altKey: Key.Keymod.isAltDown(evt.keymod),\n      shiftKey: Key.Keymod.isShiftDown(evt.keymod),\n      guiKey: Key.Keymod.isGuiDown(evt.keymod),\n    }\n  | InternalMouseWheel(evt) => {\n      mouseX: cursor.x,\n      mouseY: cursor.y,\n      ctrlKey: Key.Keymod.isControlDown(evt.keymod),\n      altKey: Key.Keymod.isAltDown(evt.keymod),\n      shiftKey: Key.Keymod.isShiftDown(evt.keymod),\n      guiKey: Key.Keymod.isGuiDown(evt.keymod),\n    }\n  | InternalMouseEnter(evt)\n  | InternalMouseLeave(evt)\n  | InternalMouseOver(evt)\n  | InternalMouseOut(evt) => {\n      mouseX: cursor.x,\n      mouseY: cursor.y,\n      ctrlKey: Key.Keymod.isControlDown(evt.keymod),\n      altKey: Key.Keymod.isAltDown(evt.keymod),\n      shiftKey: Key.Keymod.isShiftDown(evt.keymod),\n      guiKey: Key.Keymod.isGuiDown(evt.keymod),\n    }\n  };\n\nlet rec sendMouseLeaveEvents = (listOfNodes, evtParams) => {\n  switch (listOfNodes) {\n  | [] => storedNodesUnderCursor := []\n  | [node, ...tail] =>\n    node#handleEvent(MouseLeave(evtParams));\n    sendMouseLeaveEvents(tail, evtParams);\n  };\n};\n\nlet rec sendMouseEnterEvents = (deepestNode, evtParams) => {\n  deepestNode#handleEvent(MouseEnter(evtParams));\n\n  storedNodesUnderCursor := storedNodesUnderCursor^ @ [deepestNode];\n\n  let parent = deepestNode#getParent();\n\n  switch (parent) {\n  | None => ()\n  | Some(parent) => sendMouseEnterEvents(parent, evtParams)\n  };\n};\n\nlet rec handleMouseEnterDiff = (deepestNode, evtParams, ~newNodes=[], ()) => {\n  let nodeExists =\n    List.exists(\n      node => node#getInternalId() == deepestNode#getInternalId(),\n      storedNodesUnderCursor^,\n    );\n\n  if (nodeExists) {\n    /*\n     * Save currentlyStoredNodes up until that point (So need to go through list and find point at which the new and old tree intersect)\n     * MouseEnter/MouseLeave evnets\n     */\n    let rec loopThroughStoredNodesUnderCursor = listOfNodes => {\n      switch (listOfNodes) {\n      | [] => ()\n      | [node, ...tail] =>\n        if (node#getInternalId() != deepestNode#getInternalId()) {\n          node#handleEvent(MouseLeave(evtParams));\n          loopThroughStoredNodesUnderCursor(tail);\n        } else {\n          List.iter(\n            newNode => newNode#handleEvent(MouseEnter(evtParams)),\n            newNodes,\n          );\n          storedNodesUnderCursor := newNodes @ [node, ...tail];\n        }\n      };\n    };\n\n    let handleMouseOverDiff = listOfNodes => {\n      switch (newNodes) {\n      | [] =>\n        if (deepestNode#getInternalId()\n            != List.hd(listOfNodes)#getInternalId()) {\n          bubble(List.hd(listOfNodes), MouseOut(evtParams));\n          bubble(deepestNode, MouseOver(evtParams));\n        }\n      | [node, ..._tail] =>\n        bubble(List.hd(listOfNodes), MouseOut(evtParams));\n        bubble(node, MouseOver(evtParams));\n      };\n    };\n\n    handleMouseOverDiff(storedNodesUnderCursor^);\n    loopThroughStoredNodesUnderCursor(storedNodesUnderCursor^);\n  } else {\n    /*\n     * 1. Get parent if node is not found in currentlyStoredNodes\n     * 2. If no parent, replace currently stored nodes entirely with new tree\n     * 3. If parent found, call function again\n     */\n    let parent = deepestNode#getParent();\n    switch (parent) {\n    | None =>\n      /*\n       * MouseEnter/Leave events\n       */\n      List.iter(\n        node => node#handleEvent(MouseLeave(evtParams)),\n        storedNodesUnderCursor^,\n      );\n\n      List.iter(\n        newNode => newNode#handleEvent(MouseEnter(evtParams)),\n        [deepestNode, ...newNodes],\n      );\n\n      /*\n       * MouseOver/Out Events\n       */\n      bubble(List.hd(storedNodesUnderCursor^), MouseOut(evtParams));\n      switch (newNodes) {\n      | [] => bubble(deepestNode, MouseOver(evtParams))\n      | [node, ..._tail] => bubble(node, MouseOver(evtParams))\n      };\n\n      storedNodesUnderCursor := newNodes @ [deepestNode];\n    | Some(parent) =>\n      handleMouseEnterDiff(\n        parent,\n        evtParams,\n        ~newNodes=newNodes @ [deepestNode],\n        (),\n      )\n    };\n  };\n};\n\nlet dispatch =\n    (cursor: Cursor.t, evt: Events.internalMouseEvents, rootNode: Node.node) => {\n  let eventToSend = internalToExternalEvent(cursor, evt);\n  Log.tracef(m =>\n    m(\n      \"Dispatching event from node %i: %s\",\n      rootNode#getInternalId(),\n      NodeEvents.show_event(eventToSend),\n    )\n  );\n\n  if (rootNode#hasRendered()) {\n    let (mouseX, mouseY) = getPositionFromMouseEvent(cursor, evt);\n\n    if (isMouseDownEv(eventToSend)) {\n      switch (getFirstFocusable(rootNode, mouseX, mouseY)) {\n      | Some(node) => Focus.focus(node)\n      | None => Focus.loseFocus()\n      };\n    };\n\n    if (Capture.isSet()) {\n      Capture.dispatch(eventToSend);\n    } else {\n      let currentTopNode = getTopMostNode(rootNode, mouseX, mouseY);\n\n      let deepestNode =\n        if (isMouseMoveEv(eventToSend)) {\n          let mouseMoveEventParams = getMouseMoveEventParams(cursor, evt);\n\n          switch (currentTopNode) {\n          | None =>\n            // if no node found, call bubbled MouseOut on deepestStoredNode if there's some stored nodes\n            // And recursively send mouseLeave events to storedNodes if they exist\n            switch (storedNodesUnderCursor^) {\n            | [] => ()\n            | [node, ..._] => bubble(node, MouseOut(mouseMoveEventParams))\n            };\n\n            sendMouseLeaveEvents(\n              storedNodesUnderCursor^,\n              mouseMoveEventParams,\n            );\n\n          | Some(deepNode) =>\n            switch (storedNodesUnderCursor^) {\n            | [] =>\n              // If some deepNode is found and there are no storedNodes\n              // Traverse the tree and call MouseEnter on each  node -  https://developer.mozilla.org/en-US/docs/Web/Events/mouseenter\n              // And call bubbled MouseOver on deepNode\n              sendMouseEnterEvents(deepNode, mouseMoveEventParams);\n              bubble(deepNode, MouseOver(mouseMoveEventParams));\n            | [node, ..._] =>\n              // Only handle diff if the deepestStoredNode !==  the deepestFoundNode\n              if (node#getInternalId() != deepNode#getInternalId()) {\n                handleMouseEnterDiff(deepNode, mouseMoveEventParams, ());\n              }\n            }\n          };\n\n          currentTopNode;\n        } else if (isMouseWheelEv(eventToSend)) {\n          // Sometimes, we can get mouse wheel events even if the current item isn't focused.\n          // So let's double check the node under the cursor\n          getTopMostNode(\n            rootNode,\n            mouseX,\n            mouseY,\n          );\n        } else {\n          currentTopNode;\n        };\n\n      switch (deepestNode) {\n      | None => ()\n      | Some(node) =>\n        let bbox = node#getBoundingBox();\n        DebugDraw.setActive(bbox);\n        bubble(node, eventToSend);\n        let cursor = node#getCursorStyle();\n        Event.dispatch(onCursorChanged, cursor);\n      };\n    };\n\n    Cursor.set(~x=mouseX, ~y=mouseY, cursor);\n  };\n};\n"
  },
  {
    "path": "src/UI/Mouse.rei",
    "content": "/* Mouse Input */\nopen Revery_Core;\n\nmodule Cursor: {\n  type t;\n\n  let make: unit => t;\n\n  let get: t => (float, float);\n  let set: (~x: float, ~y: float, t) => unit;\n};\n\nlet setCapture:\n  (\n    ~onRelease: unit => unit=?,\n    ~onMouseDown: NodeEvents.mouseDownHandler=?,\n    ~onMouseUp: NodeEvents.mouseUpHandler=?,\n    ~onMouseMove: NodeEvents.mouseMoveHandler=?,\n    ~onMouseWheel: NodeEvents.mouseWheelHandler=?,\n    Window.t\n  ) =>\n  unit;\n\nlet releaseCapture: unit => unit;\n\nlet onCursorChanged: Event.t(MouseCursors.t);\n\nlet dispatch: (Cursor.t, Events.internalMouseEvents, Node.node) => unit;\n"
  },
  {
    "path": "src/UI/NativeButtonNode.re",
    "content": "module Draw = Revery_Draw;\n\nmodule Button = Revery_Native.Input.Button;\n\nmodule Layout = Layout;\nmodule LayoutTypes = Layout.LayoutTypes;\n\nopen Node;\n\nlet int = int_of_float;\n\nclass nativeButtonNode (title: string, onClick: unit => unit) = {\n  as _this;\n  val button = Button.create(~title, ~onClick);\n  inherit (class node)() as _super;\n  pub! draw = (parentContext: NodeDrawContext.t) => {\n    _super#draw(parentContext);\n    let dimensions = _this#measurements();\n    let worldTransform = _this#getWorldTransform();\n    Button.setFrame(\n      ~x=int(Skia.Matrix.getTranslateX(worldTransform)),\n      ~y=int(Skia.Matrix.getTranslateY(worldTransform)),\n      ~width=dimensions.width,\n      ~height=dimensions.height,\n      button,\n    );\n\n    switch (parentContext.canvas.maybeWindow) {\n    | Some(window) =>\n      let sdlWindow = Revery_Core.Window.getSdlWindow(window);\n      Button.displayIn(button, sdlWindow);\n    | None => ()\n    };\n  };\n  pub! setStyle = style => {\n    open Layout.Encoding;\n    // Make sure that the button is visible if no width/height are set.\n    let adjustedStyle =\n      Style.{\n        ...style,\n        minWidth:\n          style.minWidth == cssUndefined\n            ? button |> Button.getDefaultWidth : style.minWidth,\n        minHeight:\n          style.minHeight == cssUndefined\n            ? button |> Button.getDefaultHeight : style.minHeight,\n      };\n\n    switch (style.color) {\n    | Some(color) =>\n      let (red, green, blue, alpha) = Revery_Core.Color.toRgba(color);\n      Button.setColor(~red, ~green, ~blue, ~alpha, button);\n    | None => ()\n    };\n\n    _super#setStyle(adjustedStyle);\n  };\n  pub! cleanup = () => {\n    Button.remove(button);\n    _super#cleanup();\n  };\n};\n"
  },
  {
    "path": "src/UI/Node.re",
    "content": "module Layout = Layout;\nmodule LayoutTypes = Layout.LayoutTypes;\n\nopen Revery_Math;\n\nmodule Log = (val Revery_Core.Log.withNamespace(\"Revery.UI.Node\"));\n\nmodule ListEx = {\n  let insert = (i, node, list) => {\n    let rec loop = (i, before, after) =>\n      if (i > 0) {\n        switch (after) {\n        | [] => loop(0, before, after)\n        | [head, ...tail] => loop(i - 1, [head, ...before], tail)\n        };\n      } else if (i == 0) {\n        loop(i - 1, before, [node, ...after]);\n      } else {\n        switch (before) {\n        | [] => after\n        | [head, ...tail] => loop(i, tail, [head, ...after])\n        };\n      };\n\n    loop(i, [], list);\n  };\n};\n\nmodule UniqueId =\n  Revery_Core.UniqueId.Make({});\n\ntype callback = unit => unit;\n\nexception NoDataException(string);\nlet getOrThrow: (string, option('a)) => 'a =\n  (msg, opt) =>\n    switch (opt) {\n    | Some(p) => p\n    | None => raise(NoDataException(msg))\n    };\n\ntype cachedNodeState = {\n  transform: Skia.Matrix.t,\n  worldTransform: Skia.Matrix.t,\n  bbox: BoundingBox2d.t,\n  bboxClipped: BoundingBox2d.t,\n  depth: int,\n};\nclass node (()) = {\n  as _this;\n  val mutable _children: list(node) = [];\n  val mutable _style: Style.t = Style.defaultStyle;\n  val mutable _layoutStyle: LayoutTypes.cssStyle = Layout.LayoutSupport.defaultStyle;\n  val mutable _events: NodeEvents.t(node) = NodeEvents.make();\n  val mutable _layoutNode = Layout.createNode([||], Layout.defaultStyle);\n  val mutable _parent: option(node) = None;\n  val _internalId = UniqueId.getUniqueId();\n  val mutable _tabIndex: option(int) = None;\n  val mutable _hasFocus = false;\n  val mutable _cachedNodeState: option(cachedNodeState) = None;\n  val mutable _queuedCallbacks: list(callback) = [];\n  val mutable _lastDimensions: NodeEvents.DimensionsChangedEventParams.t =\n    NodeEvents.DimensionsChangedEventParams.create();\n  val mutable _isLayoutDirty = true;\n  val mutable _forcedMeasurements: option(Dimensions.t) = None;\n  val mutable _hasHadNonZeroBlurRadius = false;\n  val mutable _mouseBehavior = Sdl2.Window.Normal;\n  // !! WARNING !!\n  // These values are not marked as [mutable], but they are mutated\n  // via the C FFI for performance.\n  // They are created once to be re-used across recalculations,\n  // and will be mutated when `[recalculate]` is called.\n  val _worldTransform = Skia.Matrix.make();\n  val _localTransform = Skia.Matrix.make();\n  val _bboxLocal = BoundingBox2d.create(0., 0., 0., 0.);\n  val _bboxWorld = BoundingBox2d.create(0., 0., 0., 0.);\n  val _bboxClipped = BoundingBox2d.create(0., 0., 0., 0.);\n  val _lastBoundingBox: BoundingBox2d.t = BoundingBox2d.create(0., 0., 0., 0.);\n  pub draw = (parentContext: NodeDrawContext.t) => {\n    let style: Style.t = _this#getStyle();\n    let worldTransform = _this#getWorldTransform();\n    let dimensions = _this#measurements();\n\n    let {canvas, _}: NodeDrawContext.t = parentContext;\n\n    Revery_Draw.CanvasContext.setMatrix(canvas, worldTransform);\n\n    Overflow.render(\n      canvas,\n      style.overflow,\n      dimensions,\n      () => {\n        let localContext =\n          NodeDrawContext.createFromParent(parentContext, style.opacity);\n        List.iter(c => c#draw(localContext), _this#getChildren());\n      },\n    );\n  };\n  pub measurements = () => {\n    switch (_forcedMeasurements) {\n    | Some(v) => v\n    | None =>\n      let layout = _layoutNode.layout;\n      Dimensions.create(\n        ~left=layout.left,\n        ~top=layout.top,\n        ~width=layout.width,\n        ~height=layout.height,\n        (),\n      );\n    };\n  };\n  pub forceMeasurements = (dimensions: Dimensions.t) => {\n    _forcedMeasurements = Some(dimensions);\n  };\n  pub getSceneOffsets = () => {\n    let Dimensions.{left, top, _} = _this#measurements();\n    switch (_parent) {\n    | Some(parent) =>\n      let parentOffsets = parent#getSceneOffsets();\n      Offset.{left: left + parentOffsets.left, top: top + parentOffsets.top};\n    | None => Offset.{left, top}\n    };\n  };\n  pub getInternalId = () => _internalId;\n  pub getTabIndex = () => _tabIndex;\n  pub setTabIndex = index => _tabIndex = index;\n  pub markLayoutDirty = () => {\n    _isLayoutDirty\n      ? ()\n      : {\n        switch (_this#getParent()) {\n        | Some(p) => p#markLayoutDirty()\n        | None => ()\n        };\n        _isLayoutDirty = true;\n      };\n  };\n  pub setStyle = style =>\n    if (style != _style) {\n      if (style.boxShadow.blurRadius != 0. || _hasHadNonZeroBlurRadius) {\n        _hasHadNonZeroBlurRadius = true;\n      };\n      _style = style;\n\n      let lastLayoutStyle = _layoutStyle;\n      let newLayoutStyle = Style.toLayoutNode(style);\n      _layoutStyle = newLayoutStyle;\n\n      if (lastLayoutStyle != _layoutStyle) {\n        _this#markLayoutDirty();\n      };\n    };\n  pub getStyle = () => {\n    _style;\n  };\n  pub setEvents = events => _events = events;\n  pub getEvents = () => _events;\n  pub getChildren = () => _children;\n  pub getWorldTransform = () => {\n    let state = _cachedNodeState |> getOrThrow(\"getWorldTransform\");\n    state.worldTransform;\n  };\n  pub getTransform = () => {\n    let state = _cachedNodeState |> getOrThrow(\"getTransform\");\n    state.transform;\n  };\n  pub getBoundingBox = () => {\n    let state = _cachedNodeState |> getOrThrow(\"getBoundingBox\");\n    state.bbox;\n  };\n  pub getBoundingBoxClipped = () => {\n    let state = _cachedNodeState |> getOrThrow(\"getBoundingBoxClipped\");\n    state.bboxClipped;\n  };\n  pub getDepth = () => {\n    let state = _cachedNodeState |> getOrThrow(\"getDepth\");\n    state.depth;\n  };\n  pri _recalculateTransform = () => {\n    let dimensions = _this#measurements();\n    Skia.Matrix.setTranslate(\n      _localTransform,\n      dimensions.left |> float_of_int,\n      dimensions.top |> float_of_int,\n    );\n\n    let transforms = _this#getStyle().transform;\n    switch (transforms) {\n    // Skip a matrix multiplication if there are no transforms\n    | [] => ()\n    | _ =>\n      let animationTransform =\n        Transform.toMat4(\n          float_of_int(dimensions.width) /. 2.,\n          float_of_int(dimensions.height) /. 2.,\n          _this#getStyle().transform,\n        );\n      Skia.Matrix.preConcat(_localTransform, animationTransform);\n    };\n    _localTransform;\n  };\n  pri _recalculateWorldTransform = localTransform => {\n    let xform = localTransform;\n    let world =\n      switch (_parent) {\n      | None =>\n        Skia.Matrix.setIdentity(_worldTransform);\n        _worldTransform;\n      | Some(p) => p#getWorldTransform()\n      };\n    Skia.Matrix.concat(_worldTransform, world, xform);\n    _worldTransform;\n  };\n  pri _recalculateBoundingBox = worldTransform => {\n    let dimensions = _this#measurements();\n    BoundingBox2d.Mutable.set(\n      ~out=_bboxLocal,\n      0.,\n      0.,\n      float_of_int(dimensions.width),\n      float_of_int(dimensions.height),\n    );\n    BoundingBox2d.Mutable.transform(\n      ~out=_bboxWorld,\n      _bboxLocal,\n      worldTransform,\n    );\n    _bboxWorld;\n  };\n  pri _recalculateBoundingBoxClipped = bbox => {\n    switch (_this#getParent()) {\n    | Some(p) =>\n      BoundingBox2d.Mutable.intersect(\n        ~out=_bboxClipped,\n        bbox,\n        p#getBoundingBoxClipped(),\n      );\n      _bboxClipped;\n    | None => bbox\n    };\n  };\n  pri _recalculateDepth = () =>\n    switch (_parent) {\n    | None => 0\n    | Some(p) => p#getDepth() + 1\n    };\n  pub recalculate = () => {\n    _isLayoutDirty = false;\n    let transform = _this#_recalculateTransform();\n    let worldTransform = _this#_recalculateWorldTransform(transform);\n    let bbox = _this#_recalculateBoundingBox(worldTransform);\n    let bboxClipped = _this#_recalculateBoundingBoxClipped(bbox);\n    let depth = _this#_recalculateDepth();\n\n    _cachedNodeState =\n      Some({transform, worldTransform, bbox, bboxClipped, depth});\n\n    List.iter(c => c#recalculate(), _children);\n\n    /* Check if dimensions are different, if so, we need to queue up a dimensions changed event */\n    let newDimensions = _this#measurements();\n\n    let events = _this#getEvents();\n\n    if (_lastDimensions.width != newDimensions.width\n        || _lastDimensions.height != newDimensions.height) {\n      let evt: NodeEvents.DimensionsChangedEventParams.t = {\n        width: newDimensions.width,\n        height: newDimensions.height,\n      };\n      _lastDimensions = evt;\n\n      /*\n       * Defer dispatching the `ref` until AFTER layout has occurred.\n       * A common use-case for using the ref will be getting dimension\n       * and layout information. This won't be available until AFTER\n       * layout.\n       */\n      switch (events.onDimensionsChanged) {\n      | None => ()\n      | Some(cb) => _this#_queueCallback(() => cb(evt))\n      };\n    };\n\n    switch (events.onBoundingBoxChanged) {\n    | None => ()\n    | Some(cb) =>\n      if (!BoundingBox2d.equals(_lastBoundingBox, bbox)) {\n        let (x0, y0, x1, y1) = BoundingBox2d.getBounds(bbox);\n        BoundingBox2d.Mutable.set(~out=_lastBoundingBox, x0, y0, x1, y1);\n        _this#_queueCallback(() => cb(bbox));\n      }\n    };\n  };\n  pub getCursorStyle = () => {\n    switch (_this#getStyle().cursor, _this#getParent()) {\n    | (None, None) => Revery_Core.MouseCursors.arrow\n    | (None, Some(parent)) => parent#getCursorStyle()\n    | (Some(cursorStyle), _) => cursorStyle\n    };\n  };\n  pub hasRendered = () => {\n    switch (_cachedNodeState) {\n    | Some(_) => true\n    | None => false\n    };\n  };\n  pub hitTest = (x: float, y: float) => {\n    let bboxClipped = _this#getBoundingBoxClipped();\n    BoundingBox2d.isPointInside(~x, ~y, bboxClipped);\n  };\n  pub addChild = (child: node, position: int) => {\n    _children = ListEx.insert(position, child, _children);\n    child#_setParent(Some((_this :> node)));\n    _this#markLayoutDirty();\n  };\n  pub cleanup = () => {\n    List.iter(c => c#cleanup(), _children);\n  };\n  pub removeChild = (n: node) => {\n    _children =\n      List.filter(c => c#getInternalId() != n#getInternalId(), _children);\n    n#cleanup();\n    n#_setParent(None);\n    _this#markLayoutDirty();\n  };\n  pub firstChild = () => List.hd(_this#getChildren());\n  pub getParent = () => _parent;\n  pub getMeasureFunction = () => None;\n  pub handleEvent = (evt: NodeEvents.event) => {\n    Log.tracef(m =>\n      m(\n        \"Received event on node %i: %s\",\n        _internalId,\n        NodeEvents.show_event(evt),\n      )\n    );\n    switch (evt, _this#getEvents()) {\n    | (MouseDown(c), {onMouseDown: Some(cb), _}) => cb(c)\n    | (MouseMove(c), {onMouseMove: Some(cb), _}) => cb(c)\n    | (MouseUp(c), {onMouseUp: Some(cb), _}) => cb(c)\n    | (MouseWheel(c), {onMouseWheel: Some(cb), _}) => cb(c)\n    | (MouseEnter(c), {onMouseEnter: Some(cb), _}) => cb(c)\n    | (MouseLeave(c), {onMouseLeave: Some(cb), _}) => cb(c)\n    | (MouseOver(c), {onMouseOver: Some(cb), _}) => cb(c)\n    | (MouseOut(c), {onMouseOut: Some(cb), _}) => cb(c)\n    | (MouseDown(_), _)\n    | (MouseMove(_), _)\n    | (MouseUp(_), _)\n    | (MouseEnter(_), _)\n    | (MouseLeave(_), _)\n    | (MouseOver(_), _)\n    | (MouseOut(_), _)\n    | (MouseWheel(_), _) => ()\n    | (Focus, p) =>\n      _this#focus();\n      switch (p) {\n      | {onFocus: Some(cb), _} => cb()\n      | _ => ()\n      };\n    | (Blur, p) =>\n      _this#blur();\n      switch (p) {\n      | {onBlur: Some(cb), _} => cb()\n      | _ => ()\n      };\n    | (KeyDown(e), {onKeyDown: Some(cb), _}) => cb(e)\n    | (KeyUp(e), {onKeyUp: Some(cb), _}) => cb(e)\n    | (TextInput(e), {onTextInput: Some(cb), _}) => cb(e)\n    | (TextEdit(e), {onTextEdit: Some(cb), _}) => cb(e)\n    | (TextInput(_), _)\n    | (TextEdit(_), _)\n    | (KeyDown(_), _)\n    | (KeyUp(_), _) => ()\n    | (FileDropped(e), {onFileDropped: Some(cb), _}) => cb(e)\n    | (FileDropped(_), _) => ()\n    };\n  };\n  pub toLayoutNode = (~force, ()) => {\n    let layoutStyle = _layoutStyle;\n\n    if (_isLayoutDirty || force) {\n      let childNodes =\n        List.map(c => c#toLayoutNode(~force, ()), _this#getChildren());\n\n      let node =\n        switch (_this#getMeasureFunction()) {\n        | None => Layout.createNode(Array.of_list(childNodes), layoutStyle)\n        | Some(m) =>\n          Layout.createNodeWithMeasure(\n            Array.of_list(childNodes),\n            layoutStyle,\n            m,\n          )\n        };\n\n      _layoutNode = node;\n      node;\n    } else {\n      _layoutNode;\n    };\n  };\n  pri _queueCallback = (cb: callback) => {\n    _queuedCallbacks = List.append([cb], _queuedCallbacks);\n  };\n  pub flushCallbacks = () => {\n    let f = cb => cb();\n    List.iter(f, _queuedCallbacks);\n    _queuedCallbacks = [];\n\n    let fc = c => c#flushCallbacks();\n    List.iter(fc, _this#getChildren());\n  };\n  /* TODO: This should really be private - it should never be explicitly set */\n  pub _setParent = (n: option(node)) => {\n    _parent = n;\n\n    /* Dispatch ref event if we just got attached */\n    switch (n) {\n    | Some(_) =>\n      let ret = (_this :> node);\n      let maybeRef = _this#getEvents().ref;\n\n      switch (maybeRef) {\n      | Some(ref) =>\n        /*\n         * Defer dispatching the `ref` until AFTER layout has occurred.\n         * A common use-case for using the ref will be getting dimension\n         * and layout information. This won't be available until AFTER\n         * layout.\n         */\n        _this#_queueCallback(() => ref(ret))\n      | None => ()\n      };\n    | _ => ()\n    };\n\n    _this#markLayoutDirty();\n  };\n  pub canBeFocused = () =>\n    switch (_tabIndex) {\n    | Some(_) => true\n    | None => false\n    };\n  pub focus = () => {\n    _hasFocus = true;\n  };\n  pub blur = () => {\n    _hasFocus = false;\n  };\n  pub setMouseBehavior = behavior => _mouseBehavior = behavior;\n  pub getMouseBehavior = () => _mouseBehavior;\n};\n\nlet iter = (f, node: node) => {\n  let rec apply = node => {\n    f(node);\n\n    let children = node#getChildren();\n    List.iter(apply, children);\n  };\n\n  apply(node);\n};\n"
  },
  {
    "path": "src/UI/NodeDrawContext.re",
    "content": "/*\n * NodeDrawContext\n *\n * This is context that is passed from parent -> child nodes when redrawing the scene\n */\ntype t = {\n  zIndex: int,\n  dpi: float,\n  canvasScalingFactor: float,\n  opacity: float,\n  canvas: Revery_Draw.CanvasContext.t,\n  debug: bool,\n};\n\nlet create =\n    (\n      ~dpi,\n      ~canvasScalingFactor,\n      ~debug,\n      ~canvas,\n      ~zIndex: int,\n      ~opacity: float,\n      (),\n    ) => {\n  dpi,\n  canvasScalingFactor,\n  debug,\n  canvas,\n  zIndex,\n  opacity,\n};\n\nlet createFromParent = (parentContext: t, localOpacity: float) => {\n  let zIndex = parentContext.zIndex + 1;\n  let opacity = parentContext.opacity *. localOpacity;\n\n  {...parentContext, zIndex, opacity};\n};\n"
  },
  {
    "path": "src/UI/NodeEvents.re",
    "content": "/* NodeEvents.re */\n\n/* A collection of event handlers to be used by the Nodes */\n\nopen Revery_Core;\nopen Revery_Math;\n\nmodule Actions = {\n  type global = [ | `preventDefault];\n  type bubble = [ | `stopPropagation];\n  type mouseDown = [ | `capture(unit => unit)];\n  type nonBubble = [ mouseDown | global];\n  type all = [ mouseDown | bubble | global];\n\n  let capture = (~onRelease) => `capture(onRelease);\n  let stopPropagation = `stopPropagation;\n  let preventDefault = `preventDefault;\n};\n\n[@deriving show({with_path: false})]\ntype mouseMoveEventParams = {\n  mouseX: float,\n  mouseY: float,\n  ctrlKey: bool,\n  altKey: bool,\n  shiftKey: bool,\n  guiKey: bool,\n};\n\n[@deriving show({with_path: false})]\ntype mouseButtonEventParams = {\n  mouseX: float,\n  mouseY: float,\n  button: MouseButton.t,\n  ctrlKey: bool,\n  altKey: bool,\n  shiftKey: bool,\n  guiKey: bool,\n};\n\n[@deriving show({with_path: false})]\ntype mouseWheelEventParams = {\n  deltaX: float,\n  deltaY: float,\n  ctrlKey: bool,\n  altKey: bool,\n  shiftKey: bool,\n  guiKey: bool,\n};\n\n[@deriving show({with_path: false})]\ntype textInputEventParams = {text: string};\n\n[@deriving show({with_path: false})]\ntype textEditEventParams = {\n  text: string,\n  start: int,\n  length: int,\n};\n\n/*\n  Might be useful to extend in the future\n */\n[@deriving show({with_path: false})]\ntype focusEventParams = unit;\n\n[@deriving show({with_path: false})]\ntype keyEventParams = {\n  keycode: [@opaque] Key.Keycode.t,\n  scancode: [@opaque] Key.Scancode.t,\n  keymod: [@opaque] Key.Keymod.t,\n  repeat: bool,\n  ctrlKey: bool,\n  altKey: bool,\n  shiftKey: bool,\n  guiKey: bool,\n};\n\nmodule DimensionsChangedEventParams = {\n  type t = {\n    width: int,\n    height: int,\n  };\n\n  let create = (~width=0, ~height=0, ()) => {width, height};\n};\n\n[@deriving show({with_path: false})]\ntype fileDropEventParams = {\n  mouseX: float,\n  mouseY: float,\n  paths: list(string),\n  ctrlKey: bool,\n  altKey: bool,\n  shiftKey: bool,\n  guiKey: bool,\n};\n\n[@deriving show({with_path: false})]\ntype event =\n  | MouseDown(mouseButtonEventParams)\n  | MouseMove(mouseMoveEventParams)\n  | MouseUp(mouseButtonEventParams)\n  | MouseWheel(mouseWheelEventParams)\n  | KeyDown(keyEventParams)\n  | KeyUp(keyEventParams)\n  | TextInput(textInputEventParams)\n  | TextEdit(textEditEventParams)\n  //| KeyPress(keyPressEventParams)\n  | MouseEnter(mouseMoveEventParams)\n  | MouseLeave(mouseMoveEventParams)\n  | MouseOver(mouseMoveEventParams)\n  | MouseOut(mouseMoveEventParams)\n  | FileDropped(fileDropEventParams)\n  | Blur\n  | Focus;\n\ntype refCallback('a) = 'a => unit;\ntype mouseDownHandler = mouseButtonEventParams => unit;\ntype mouseUpHandler = mouseButtonEventParams => unit;\ntype mouseMoveHandler = mouseMoveEventParams => unit;\ntype mouseOverHandler = mouseMoveEventParams => unit;\ntype mouseOutHandler = mouseMoveEventParams => unit;\ntype mouseWheelHandler = mouseWheelEventParams => unit;\ntype mouseWindowHandler = Window.t => unit;\ntype focusHandler = focusEventParams => unit;\ntype keyDownHandler = keyEventParams => unit;\ntype keyUpHandler = keyEventParams => unit;\ntype textInputHandler = textInputEventParams => unit;\ntype textEditHandler = textEditEventParams => unit;\ntype dimensionsChangedHandler = DimensionsChangedEventParams.t => unit;\ntype fileDropHandler = fileDropEventParams => unit;\n\ntype t('a) = {\n  ref: option(refCallback('a)),\n  onMouseDown: option(mouseDownHandler),\n  onMouseMove: option(mouseMoveHandler),\n  onMouseUp: option(mouseUpHandler),\n  onMouseWheel: option(mouseWheelHandler),\n  onMouseEnter: option(mouseMoveHandler),\n  onMouseLeave: option(mouseMoveHandler),\n  onMouseOver: option(mouseOverHandler),\n  onMouseOut: option(mouseOutHandler),\n  onFocus: option(focusHandler),\n  onBlur: option(focusHandler),\n  onKeyUp: option(keyUpHandler),\n  onKeyDown: option(keyDownHandler),\n  onTextInput: option(textInputHandler),\n  onTextEdit: option(textEditHandler),\n  onDimensionsChanged: option(dimensionsChangedHandler),\n  onBoundingBoxChanged: option(BoundingBox2d.t => unit),\n  onFileDropped: option(fileDropHandler),\n};\n\nlet make =\n    (\n      ~ref=?,\n      ~onMouseDown=?,\n      ~onMouseMove=?,\n      ~onMouseUp=?,\n      ~onMouseWheel=?,\n      ~onMouseEnter=?,\n      ~onMouseLeave=?,\n      ~onMouseOver=?,\n      ~onMouseOut=?,\n      ~onFocus=?,\n      ~onBlur=?,\n      //~onKeyPress=?,\n      ~onTextEdit=?,\n      ~onTextInput=?,\n      ~onKeyDown=?,\n      ~onKeyUp=?,\n      ~onDimensionsChanged=?,\n      ~onBoundingBoxChanged=?,\n      ~onFileDropped=?,\n      _unit: unit,\n    ) => {\n  ref,\n  onMouseDown,\n  onMouseMove,\n  onMouseUp,\n  onMouseWheel,\n  onMouseEnter,\n  onMouseLeave,\n  onMouseOver,\n  onMouseOut,\n  onFocus,\n  onBlur,\n  onTextEdit,\n  onTextInput,\n  onKeyDown,\n  onKeyUp,\n  onDimensionsChanged,\n  onBoundingBoxChanged,\n  onFileDropped,\n};\n"
  },
  {
    "path": "src/UI/Offset.re",
    "content": "type t = {\n  top: int,\n  left: int,\n};\n"
  },
  {
    "path": "src/UI/Overflow.re",
    "content": "/*\n * Overflow.re\n *\n * Utilities for handling overflow clipping\n *\n */\n\nopen Layout;\n\ntype renderCallback = unit => unit;\n\nlet render =\n    (\n      canvas: Revery_Draw.CanvasContext.t,\n      overflow: LayoutTypes.overflow,\n      dimensions: Dimensions.t,\n      r: renderCallback,\n    ) => {\n  if (overflow == LayoutTypes.Hidden || overflow == LayoutTypes.Scroll) {\n    let clippingRect =\n      Skia.Rect.makeLtrb(\n        0.,\n        0.,\n        float_of_int(dimensions.width),\n        float_of_int(dimensions.height),\n      );\n\n    let _save: int = Revery_Draw.CanvasContext.save(canvas);\n    let () = Revery_Draw.CanvasContext.clipRect(canvas, clippingRect);\n    ();\n  };\n\n  r();\n\n  if (overflow == LayoutTypes.Hidden || overflow == LayoutTypes.Scroll) {\n    let () = Revery_Draw.CanvasContext.restore(canvas);\n    ();\n  };\n};\n"
  },
  {
    "path": "src/UI/React.re",
    "content": "include Reconciler;\n\nmodule React = Brisk_reconciler;\n\ninclude React;\n"
  },
  {
    "path": "src/UI/Reconciler.re",
    "content": "/* Reconciler.re\n *\n * This implements a reconciler for our UI primitives.\n * This is the 'back-end' for our React API - we hand this\n * over to the Brisk_reconciler, and get our\n * nice React API that works against Revery's node tree.\n */\n\nopen Revery_Core;\n\ntype reveryNode = Node.node;\n\ntype hostElement = reveryNode;\ntype node = reveryNode;\n\nlet onStale: Event.t(unit) = Event.create();\n\nlet insertNode = (~parent: node, ~child: node, ~position) => {\n  parent#addChild(child, position);\n  parent;\n};\n\nlet deleteNode = (~parent: node, ~child: node, ~position as _) => {\n  parent#removeChild(child);\n  parent;\n};\n\nlet moveNode = (~parent, ~child as _, ~from as _, ~to_ as _) => {\n  parent;\n};\n\nBrisk_reconciler.addStaleTreeHandler(() => Event.dispatch(onStale, ()));\n"
  },
  {
    "path": "src/UI/Render.re",
    "content": "/*\n * UiRender.re\n *\n * Core render logic for a UI bound to a Window\n */\n\nopen Revery_Core;\nopen Revery_Draw;\n\nmodule Layout = Layout;\nmodule LayoutTypes = Layout.LayoutTypes;\nmodule Log = (val Log.withNamespace(\"Revery.UI.Render\"));\n\nopen RenderContainer;\n\nlet render =\n    (\n      ~forceLayout=false,\n      renderContainer: RenderContainer.t,\n      component: React.element('node),\n    ) => {\n  Log.trace(\"BEGIN: Render frame\");\n  let {rootNode, window, container, _} = renderContainer;\n\n  /* Perform reconciliation */\n  Performance.bench(\"reconcile\", () =>\n    container := Container.update(container^, component)\n  );\n\n  /* Layout */\n  let size = Window.getSize(window);\n\n  let pixelRatio = Window.getDevicePixelRatio(window);\n  let scaleAndZoomFactor = Window.getScaleAndZoom(window);\n  let canvasScalingFactor = pixelRatio *. scaleAndZoomFactor;\n\n  let zoomFactor = Window.getZoom(window);\n\n  Log.tracef(m =>\n    m(\n      \"-- RENDER: pixelRatio: %f scaleAndZoom: %f zoom: %f canvasScaling: %f\",\n      pixelRatio,\n      scaleAndZoomFactor,\n      zoomFactor,\n      canvasScalingFactor,\n    )\n  );\n\n  let adjustedHeight =\n    float_of_int(size.height) /. zoomFactor |> int_of_float;\n  let adjustedWidth = float_of_int(size.width) /. zoomFactor |> int_of_float;\n  Log.tracef(m =>\n    m(\n      \"-- RENDER: adjustedWidth: %d adjustedHeight: %d\",\n      adjustedWidth,\n      adjustedHeight,\n    )\n  );\n\n  RenderContainer.updateCanvas(window, renderContainer);\n\n  rootNode#setStyle(\n    Style.make(\n      ~position=LayoutTypes.Relative,\n      ~width=adjustedWidth,\n      ~height=adjustedHeight,\n      (),\n    ),\n  );\n  Layout.layout(~force=forceLayout, rootNode);\n\n  /* Recalculate cached parameters */\n  Performance.bench(\"recalculate\", () => rootNode#recalculate());\n\n  /* Flush any node callbacks */\n  Performance.bench(\"flush\", () => rootNode#flushCallbacks());\n\n  /* Render */\n  Performance.bench(\"draw\", () => {\n    switch (renderContainer.canvas^) {\n    | None => ()\n    | Some(canvas) =>\n      let skiaRoot =\n        Skia.Matrix.makeScale(\n          canvasScalingFactor,\n          canvasScalingFactor,\n          0.,\n          0.,\n        );\n      CanvasContext.setRootTransform(skiaRoot, canvas);\n      let debug = DebugDraw.isEnabled();\n      let drawContext =\n        NodeDrawContext.create(\n          ~canvasScalingFactor,\n          ~dpi=pixelRatio,\n          ~debug,\n          ~canvas,\n          ~zIndex=0,\n          ~opacity=1.0,\n          (),\n        );\n\n      let backgroundColor = Window.getBackgroundColor(window);\n      CanvasContext.clear(~color=backgroundColor |> Color.toSkia, canvas);\n\n      rootNode#draw(drawContext);\n\n      CanvasContext.setMatrix(canvas, Skia.Matrix.identity);\n\n      if (debug) {\n        DebugDraw.draw(canvas);\n      };\n\n      if (Window.shouldShowFPSCounter(window) || debug) {\n        let w = float_of_int(adjustedWidth);\n        let (x, y) = (w -. 64., 32.);\n        let paint = Skia.Paint.make();\n        Skia.Paint.setColor(\n          paint,\n          Skia.Color.makeArgb(255l, 50l, 200l, 50l),\n        );\n        CanvasContext.drawText(\n          ~paint,\n          ~x,\n          ~y,\n          ~text=Printf.sprintf(\"FPS: %d\", Window.getFPS(window)),\n          canvas,\n        );\n      };\n\n      Revery_Draw.CanvasContext.flush(canvas);\n    }\n  });\n  Log.trace(\"END: Render frame\");\n\n  let forceDirty = DebugDraw.isEnabled();\n  forceDirty;\n};\n"
  },
  {
    "path": "src/UI/RenderCondition.re",
    "content": "type t =\n  | Condition({\n      shouldRender: ('a, 'a) => bool,\n      v: 'a,\n      pipe: ref(option('a)),\n    })\n    : t;\n\ntype condition('a) = 'a => t;\n\nlet make = (shouldRender: ('a, 'a) => bool) => {\n  let pipe: ref(option('a)) = ref(None);\n\n  v => {\n    Condition({shouldRender, v, pipe});\n  };\n};\n\nlet always: t = make((_, _) => true, ());\nlet never: t = make((_, _) => false, ());\n\nlet shouldRender = (oldCondition: t, newCondition: t) => {\n  switch (oldCondition, newCondition) {\n  | (\n      Condition({v: oldValue, pipe: oldPipe, _}),\n      Condition({v: newValue, pipe: newPipe, shouldRender}),\n    ) =>\n    // We don't know for sure that these are the same type, so use the pipe trick...\n\n    newPipe := None;\n    oldPipe := None;\n    oldPipe := Some(oldValue);\n\n    switch (newPipe^) {\n    | Some(realOldValue) => shouldRender(realOldValue, newValue)\n    | None =>\n      // Different pipes... different types...\n      // TODO: Log\n      true\n    };\n  };\n};\n\nlet shouldRenderOpt = (maybeOldCondition, maybeNewCondition) => {\n  switch (maybeOldCondition, maybeNewCondition) {\n  | (None, None) => false\n  | (Some(_), None) => true\n  | (None, Some(_)) => true\n  | (Some(oldCondition), Some(newCondition)) =>\n    shouldRender(oldCondition, newCondition)\n  };\n};\n"
  },
  {
    "path": "src/UI/RenderContainer.re",
    "content": "/*\n * RenderContainer.re\n *\n * State that is persisted across renderings.\n * This stores the connection between a window and its UI\n */\n\nmodule Window = Revery_Core.Window;\n\ntype t = {\n  rootNode: ViewNode.viewNode,\n  container: ref(Container.t),\n  window: Window.t,\n  mouseCursor: Mouse.Cursor.t,\n  canvas: ref(option(Revery_Draw.CanvasContext.t)),\n};\n\nlet create = (window, rootNode, container, mouseCursor) => {\n  window,\n  rootNode,\n  container: ref(container),\n  mouseCursor,\n  canvas: ref(None),\n};\n\nlet updateCanvas = (window, container: t) => {\n  switch (container.canvas^) {\n  | None =>\n    container.canvas :=\n      Revery_Draw.CanvasContext.create(\n        Revery_Draw.CanvasContext.createGpuContext(),\n        window,\n      )\n  | Some(_) as v =>\n    container.canvas := Revery_Draw.CanvasContext.resize(window, v)\n  };\n};\n"
  },
  {
    "path": "src/UI/Revery_UI.re",
    "content": "module Animation = Animation;\nmodule Spring = Spring;\nmodule Easing = Easing;\n\nmodule ImageResizeMode = ImageResizeMode;\nmodule Layout = Layout;\nmodule LayoutTypes = Layout.LayoutTypes;\nmodule Style = Style;\nmodule Transform = Transform;\nmodule Selector = Selector;\nmodule RichText = RichText;\n\nclass node = class Node.node;\nclass layerNode = class LayerNode.layerNode;\nclass viewNode = class ViewNode.viewNode;\nclass textNode = class TextNode.textNode;\nclass imageNode = class ImageNode.imageNode;\nclass canvasNode = class CanvasNode.canvasNode;\nclass nativeButtonNode = class NativeButtonNode.nativeButtonNode;\nmodule NodeDrawContext = NodeDrawContext;\n\nmodule Keyboard = Keyboard;\nmodule Mouse = Mouse;\nmodule NodeEvents = NodeEvents;\nmodule UiEvents = UiEvents;\n\nmodule Container = Container;\nmodule React = React;\nmodule Focus = Focus;\nmodule Dimensions = Dimensions;\nmodule Offset = Offset;\n\nmodule RenderCondition = RenderCondition;\n\ntype element = React.element(node);\n\nlet measureText =\n    (\n      ~smoothing=Revery_Font.Smoothing.default,\n      ~width,\n      ~style,\n      ~fontFamily,\n      ~fontWeight,\n      ~fontSize,\n      text,\n    ) => {\n  let node: TextNode.textNode = (new TextNode.textNode)(text);\n  let styles = Style.create(~style, ());\n  node#setStyle(styles);\n  node#setFontFamily(fontFamily);\n  node#setFontWeight(fontWeight);\n  node#setFontSize(fontSize);\n  node#setSmoothing(smoothing);\n\n  let measureResult = node#measure(width, 0);\n  measureResult.height;\n};\n\ninclude Ui;\n"
  },
  {
    "path": "src/UI/RichText.re",
    "content": "open Revery_Core;\nopen Revery_Font;\n\ntype textInfo = {\n  fontFamily: Family.t,\n  fontWeight: Weight.t,\n  italic: bool,\n  fontSize: float,\n  text: string,\n  color: Color.t,\n};\ntype t =\n  | Leaf(textInfo)\n  | Node(t, t);\n\nlet (++) = (left: t, right: t) => Node(left, right);\n\nlet rec foldRight =\n        (fn: ('acc, textInfo) => 'acc, accumulator: 'acc, richtext: t) =>\n  switch (richtext) {\n  | Leaf(textInfo) => fn(accumulator, textInfo)\n  | Node(left, right) =>\n    let rightAcc = foldRight(fn, accumulator, right);\n    let leftAcc = foldRight(fn, rightAcc, left);\n    leftAcc;\n  };\n\nlet rec map = (updateLeaf: textInfo => t, richtext: t) =>\n  switch (richtext) {\n  | Leaf(textInfo) => updateLeaf(textInfo)\n  | Node(left, right) =>\n    let newLeft = map(updateLeaf, left);\n    let newRight = map(updateLeaf, right);\n    Node(newLeft, newRight);\n  };\n\nlet measure = (~smoothing=Smoothing.default, richtext: t) =>\n  foldRight(\n    (acc: Dimensions.t, {italic, fontFamily, fontSize, fontWeight, text, _}) => {\n      let dimensions =\n        Revery_Draw.Text.dimensions(\n          ~smoothing,\n          ~italic,\n          ~fontFamily,\n          ~fontSize,\n          ~fontWeight,\n          text,\n        );\n      let width = acc.width + int_of_float(dimensions.width);\n      let height = max(acc.height, int_of_float(dimensions.height));\n\n      Dimensions.create(~top=0, ~left=0, ~width, ~height, ());\n    },\n    Dimensions.create(~top=0, ~left=0, ~width=0, ~height=0, ()),\n    richtext,\n  );\n\nmodule DSL = {\n  let text =\n      (\n        ~fontFamily=Family.default,\n        ~fontWeight=Weight.Normal,\n        ~italic=false,\n        ~fontSize=14.,\n        ~color=Colors.black,\n        text: string,\n      ) =>\n    Leaf({fontFamily, fontWeight, italic, fontSize, text, color});\n\n  let fontWeight = (fontWeight: Weight.t, richtext: t) =>\n    richtext |> map(textInfo => Leaf({...textInfo, fontWeight}));\n  let thin = (richtext: t) =>\n    richtext |> map(textInfo => Leaf({...textInfo, fontWeight: Weight.Thin}));\n  let ultralight = (richtext: t) =>\n    richtext\n    |> map(textInfo => Leaf({...textInfo, fontWeight: Weight.UltraLight}));\n  let light = (richtext: t) =>\n    richtext |> map(textInfo => Leaf({...textInfo, fontWeight: Weight.Light}));\n  let normal = (richtext: t) =>\n    richtext\n    |> map(textInfo => Leaf({...textInfo, fontWeight: Weight.Normal}));\n  let medium = (richtext: t) =>\n    richtext\n    |> map(textInfo => Leaf({...textInfo, fontWeight: Weight.Medium}));\n  let semibold = (richtext: t) =>\n    richtext\n    |> map(textInfo => Leaf({...textInfo, fontWeight: Weight.SemiBold}));\n  let bold = (richtext: t) =>\n    richtext |> map(textInfo => Leaf({...textInfo, fontWeight: Weight.Bold}));\n  let ultrabold = (richtext: t) =>\n    richtext\n    |> map(textInfo => Leaf({...textInfo, fontWeight: Weight.UltraBold}));\n  let heavy = (richtext: t) =>\n    richtext |> map(textInfo => Leaf({...textInfo, fontWeight: Weight.Heavy}));\n\n  let fontFamily = (fontFamily: Family.t, richtext: t) =>\n    richtext |> map(textInfo => Leaf({...textInfo, fontFamily}));\n  let italic = (richtext: t) =>\n    richtext |> map(textInfo => Leaf({...textInfo, italic: true}));\n  let fontSize = (fontSize: float, richtext: t) =>\n    richtext |> map(textInfo => Leaf({...textInfo, fontSize}));\n  let color = (color: Color.t, richtext: t) =>\n    richtext |> map(textInfo => Leaf({...textInfo, color}));\n};\ninclude DSL;\n"
  },
  {
    "path": "src/UI/RichText.rei",
    "content": "open Revery_Core;\nopen Revery_Font;\n\ntype textInfo = {\n  fontFamily: Family.t,\n  fontWeight: Weight.t,\n  italic: bool,\n  fontSize: float,\n  text: string,\n  color: Color.t,\n};\ntype t =\n  | Leaf(textInfo)\n  | Node(t, t);\n\nlet (++): (t, t) => t;\n\nlet foldRight: (('acc, textInfo) => 'acc, 'acc, t) => 'acc;\n\nlet map: (textInfo => t, t) => t;\n\nlet measure: (~smoothing: Smoothing.t=?, t) => Dimensions.t;\n\nmodule DSL: {\n  let text:\n    (\n      ~fontFamily: Family.t=?,\n      ~fontWeight: Weight.t=?,\n      ~italic: bool=?,\n      ~fontSize: float=?,\n      ~color: Color.t=?,\n      string\n    ) =>\n    t;\n  let fontWeight: (Weight.t, t) => t;\n  let thin: t => t;\n  let ultralight: t => t;\n  let light: t => t;\n  let normal: t => t;\n  let medium: t => t;\n  let semibold: t => t;\n  let bold: t => t;\n  let ultrabold: t => t;\n  let heavy: t => t;\n\n  let fontFamily: (Family.t, t) => t;\n  let italic: t => t;\n  let fontSize: (float, t) => t;\n  let color: (Color.t, t) => t;\n};\nlet text:\n  (\n    ~fontFamily: Family.t=?,\n    ~fontWeight: Weight.t=?,\n    ~italic: bool=?,\n    ~fontSize: float=?,\n    ~color: Color.t=?,\n    string\n  ) =>\n  t;\nlet fontWeight: (Weight.t, t) => t;\nlet thin: t => t;\nlet ultralight: t => t;\nlet light: t => t;\nlet normal: t => t;\nlet medium: t => t;\nlet semibold: t => t;\nlet bold: t => t;\nlet ultrabold: t => t;\nlet heavy: t => t;\n\nlet fontFamily: (Family.t, t) => t;\nlet italic: t => t;\nlet fontSize: (float, t) => t;\nlet color: (Color.t, t) => t;\n"
  },
  {
    "path": "src/UI/Selector.re",
    "content": "open Revery_Core;\nopen Style;\n\ntype selector('a) =\n  | Color: selector(option(Color.t))\n  | Width: selector(int)\n  | FlexGrow: selector(int)\n  | FlexWrap: selector(Layout.LayoutTypes.wrapType)\n  | FlexDirection: selector(Layout.LayoutTypes.flexDirection)\n  | JustifyContent: selector(Layout.LayoutTypes.justify)\n  | AlignItems: selector(Layout.LayoutTypes.align)\n  | Position: selector(Layout.LayoutTypes.positionType)\n  | BackgroundColor: selector(Color.t)\n  | Height: selector(int)\n  | Top: selector(int)\n  | Right: selector(int)\n  | Bottom: selector(int)\n  | Left: selector(int)\n  | MarginTop: selector(int)\n  | MarginLeft: selector(int)\n  | MarginRight: selector(int)\n  | MarginBottom: selector(int)\n  | Margin: selector(int)\n  | MarginVertical: selector(int)\n  | MarginHorizontal: selector(int)\n  | Margin2: selector(xy)\n  | Margin4: selector(coords)\n  | PaddingTop: selector(int)\n  | PaddingLeft: selector(int)\n  | PaddingRight: selector(int)\n  | PaddingBottom: selector(int)\n  | Padding: selector(int)\n  | PaddingVertical: selector(int)\n  | PaddingHorizontal: selector(int)\n  | Padding2: selector(xy)\n  | Padding4: selector(coords)\n  | Overflow: selector(Layout.LayoutTypes.overflow)\n  | BorderTop: selector(Border.t)\n  | BorderLeft: selector(Border.t)\n  | BorderRight: selector(Border.t)\n  | BorderBottom: selector(Border.t)\n  | Border: selector(Border.t)\n  | BorderHorizontal: selector(Border.t)\n  | BorderVertical: selector(Border.t)\n  | Transform: selector(list(Transform.t))\n  | Opacity: selector(float)\n  | BoxShadow: selector(BoxShadow.properties)\n  | Cursor: selector(option(MouseCursors.t));\n\n/**\n     This function pulls out (selects) a style property based on the matching Variant that is passed in\n     it uses a GADT to correctly type the return value of the function by matching the variant passed in\n   */\nlet rec select:\n  type selectionType.\n    (list(allProps), selector(selectionType), selectionType) => selectionType =\n  (styles, selector, default) => {\n    switch (styles, selector) {\n    | ([], _) => default\n    | ([`Color(c), ...rest], Color) => select(rest, selector, c)\n    | ([`Width(size), ...rest], Width) => select(rest, selector, size)\n    | ([`FlexGrow(grow), ...rest], FlexGrow) =>\n      select(rest, selector, grow)\n    | ([`FlexDirection(direction), ...rest], FlexDirection) =>\n      select(rest, selector, direction)\n\n    | ([`FlexWrap(wrap), ...rest], FlexWrap) =>\n      select(rest, selector, wrap)\n\n    | ([`JustifyContent(justify), ...rest], JustifyContent) =>\n      select(rest, selector, justify)\n    | ([`AlignItems(align), ...rest], AlignItems) =>\n      select(rest, selector, align)\n    | ([`Position(pos), ...rest], Position) => select(rest, selector, pos)\n    | ([`BackgroundColor(col), ...rest], BackgroundColor) =>\n      select(rest, selector, col)\n    | ([`Height(h), ...rest], Height) => select(rest, selector, h)\n    | ([`Top(t), ...rest], Top) => select(rest, selector, t)\n    | ([`Right(r), ...rest], Right) => select(rest, selector, r)\n    | ([`Bottom(b), ...rest], Bottom) => select(rest, selector, b)\n    | ([`Left(l), ...rest], Left) => select(rest, selector, l)\n    | ([`MarginTop(mt), ...rest], MarginTop) => select(rest, selector, mt)\n    | ([`MarginLeft(ml), ...rest], MarginLeft) => select(rest, selector, ml)\n    | ([`MarginRight(mr), ...rest], MarginRight) =>\n      select(rest, selector, mr)\n    | ([`MarginBottom(mb), ...rest], MarginBottom) =>\n      select(rest, selector, mb)\n    | ([`Margin(m), ...rest], Margin) => select(rest, selector, m)\n    | ([`MarginVertical(mv), ...rest], MarginVertical) =>\n      select(rest, selector, mv)\n    | ([`MarginHorizontal(mh), ...rest], MarginHorizontal) =>\n      select(rest, selector, mh)\n    | ([`Margin2(m2), ...rest], Margin2) => select(rest, selector, m2)\n    | ([`Margin4(m4), ...rest], Margin4) => select(rest, selector, m4)\n    | ([`PaddingTop(mt), ...rest], PaddingTop) => select(rest, selector, mt)\n    | ([`PaddingLeft(ml), ...rest], PaddingLeft) =>\n      select(rest, selector, ml)\n    | ([`PaddingRight(mr), ...rest], PaddingRight) =>\n      select(rest, selector, mr)\n    | ([`PaddingBottom(mb), ...rest], PaddingBottom) =>\n      select(rest, selector, mb)\n    | ([`Padding(m), ...rest], Padding) => select(rest, selector, m)\n    | ([`PaddingVertical(mv), ...rest], PaddingVertical) =>\n      select(rest, selector, mv)\n    | ([`PaddingHorizontal(mh), ...rest], PaddingHorizontal) =>\n      select(rest, selector, mh)\n    | ([`Padding2(m2), ...rest], Padding2) => select(rest, selector, m2)\n    | ([`Padding4(m4), ...rest], Padding4) => select(rest, selector, m4)\n    | ([`Overflow(ovfl), ...rest], Overflow) =>\n      select(rest, selector, ovfl)\n    | ([`BorderTop(bt), ...rest], BorderTop) => select(rest, selector, bt)\n    | ([`BorderLeft(bl), ...rest], BorderLeft) => select(rest, selector, bl)\n    | ([`BorderRight(br), ...rest], BorderRight) =>\n      select(rest, selector, br)\n    | ([`BorderBottom(bb), ...rest], BorderBottom) =>\n      select(rest, selector, bb)\n    | ([`Border(b), ...rest], Border) => select(rest, selector, b)\n    | ([`BorderHorizontal(bh), ...rest], BorderHorizontal) =>\n      select(rest, selector, bh)\n    | ([`BorderVertical(bv), ...rest], BorderVertical) =>\n      select(rest, selector, bv)\n    | ([`Transform(tr), ...rest], Transform) => select(rest, selector, tr)\n    | ([`Opacity(op), ...rest], Opacity) => select(rest, selector, op)\n    | ([`BoxShadow(bxsh), ...rest], BoxShadow) =>\n      select(rest, selector, bxsh)\n    | ([`Cursor(cur), ...rest], Cursor) => select(rest, selector, cur)\n    | ([_, ...rest], _) => select(rest, selector, default)\n    };\n  };\n"
  },
  {
    "path": "src/UI/Spring.re",
    "content": "/* Hooks specific to Revery */\nopen Revery_Core;\n\nmodule Options = {\n  type t = {\n    stiffness: float,\n    damping: float,\n  };\n\n  let create = (~stiffness=180., ~damping=12., ()) => {damping, stiffness};\n\n  // Presets from react-spring: https://www.react-spring.io/docs/hooks/api\n  let default = create(~stiffness=170., ~damping=26., ());\n  let gentle = create(~stiffness=120., ~damping=14., ());\n  let wobbly = create(~stiffness=180., ~damping=12., ());\n  let stiff = create(~stiffness=210., ~damping=20., ());\n  let slow = create(~stiffness=280., ~damping=60., ());\n  let molasses = create(~stiffness=280., ~damping=120., ());\n};\n\ntype t = {\n  value: float,\n  velocity: float,\n  acceleration: float,\n  time: Time.t,\n};\n\nlet create = (value: float, time: Time.t) => {\n  value,\n  velocity: 0.,\n  acceleration: 0.,\n  time,\n};\n\nlet tick = (target: float, spring: t, options: Options.t, time: Time.t) => {\n  let deltaT = Time.(time - spring.time) |> Time.toFloatSeconds;\n  if (deltaT > 0.) {\n    // Cap the delta at 33 milliseconds / 30 FPS\n    // This is important if the animation has been inactive!\n    let deltaT = min(deltaT, 0.033);\n    let force = Float.abs(target -. spring.value) *. options.stiffness;\n    let dir = spring.value > target ? (-1.) : 1.;\n\n    let acceleration = dir *. force -. options.damping *. spring.velocity;\n    let velocity = spring.velocity +. acceleration *. deltaT;\n    let value = spring.value +. velocity *. deltaT;\n\n    {acceleration, velocity, value, time};\n  } else {\n    {...spring, time};\n  };\n};\n\nlet isAtRest = (~restThreshold=0.1, {acceleration, velocity, _}) =>\n  Float.abs(acceleration) <= restThreshold\n  && Float.abs(velocity) <= restThreshold;\n\nlet toString = (spring: t) =>\n  Printf.sprintf(\n    \"x: %f v: %f a: %f\",\n    spring.value,\n    spring.velocity,\n    spring.acceleration,\n  );\n\nlet setPosition = (value, state) => {\n  ...state,\n  value,\n  velocity: 0.,\n  acceleration: 0.,\n};\n"
  },
  {
    "path": "src/UI/Spring.rei",
    "content": "open Revery_Core;\n\nmodule Options: {\n  type t = {\n    stiffness: float,\n    damping: float,\n  };\n\n  let create: (~stiffness: float=?, ~damping: float=?, unit) => t;\n\n  // Some basic presets\n  let default: t;\n  let gentle: t;\n  let wobbly: t;\n  let stiff: t;\n  let slow: t;\n  let molasses: t;\n};\n\ntype t = {\n  value: float,\n  velocity: float,\n  acceleration: float,\n  time: Time.t,\n};\n\n// [create(position, time)] creates a new spring model with a mass at position of [position]\n// starting at time [time].\nlet create: (float, Time.t) => t;\n\n// [tick(targetPosition, spring, options, time)] calculates the updated spring model\n// based on the [targetPosition], the current [spring] state, the spring [options],\n// and the current absolute [time].\nlet tick: (float, t, Options.t, Time.t) => t;\n\nlet toString: t => string;\n\n// isResting[spring] returns whether the spring is in a restng state\nlet isAtRest: (~restThreshold: float=?, t) => bool;\n\nlet setPosition: (float, t) => t;\n"
  },
  {
    "path": "src/UI/Style.re",
    "content": "open Layout;\n\nopen Revery_Core;\n\nmodule Border = {\n  type t = {\n    color: Color.t,\n    width: int,\n  };\n\n  let make = (~color=Colors.black, ~width=Encoding.cssUndefined, ()) => {\n    color,\n    width,\n  };\n};\n\nmodule BoxShadow = {\n  type properties = {\n    xOffset: float,\n    yOffset: float,\n    blurRadius: float,\n    spreadRadius: float,\n    color: Color.t,\n  };\n\n  type t = properties;\n\n  let make =\n      (\n        ~xOffset=(-5.),\n        ~yOffset=(-5.),\n        ~blurRadius=20.,\n        ~spreadRadius=00.,\n        ~color=Color.rgba(0., 0., 0., 1.0),\n        (),\n      ) => {\n    xOffset,\n    yOffset,\n    blurRadius,\n    spreadRadius,\n    color,\n  };\n\n  let default: t = {\n    xOffset: 0.,\n    yOffset: 0.,\n    blurRadius: 0.,\n    spreadRadius: 0.,\n    color: Colors.black,\n  };\n};\n\nmodule PointerEvents = {\n  type t =\n    | Default\n    | Allow\n    | Ignore;\n};\n\ntype t = {\n  backgroundColor: Color.t,\n  color: option(Color.t),\n  width: int,\n  height: int,\n  position: LayoutTypes.positionType,\n  flexGrow: int,\n  flexBasis: int,\n  flexShrink: int,\n  flexDirection: LayoutTypes.flexDirection,\n  flexWrap: LayoutTypes.wrapType,\n  justifyContent: LayoutTypes.justify,\n  alignItems: LayoutTypes.align,\n  alignSelf: LayoutTypes.align,\n  top: int,\n  bottom: int,\n  left: int,\n  right: int,\n  lineHeight: float,\n  pointerEvents: PointerEvents.t,\n  textWrap: TextWrapping.wrapType,\n  marginTop: int,\n  marginLeft: int,\n  marginRight: int,\n  marginBottom: int,\n  margin: int,\n  marginVertical: int,\n  marginHorizontal: int,\n  minWidth: int,\n  maxWidth: int,\n  minHeight: int,\n  maxHeight: int,\n  paddingTop: int,\n  paddingLeft: int,\n  paddingRight: int,\n  paddingBottom: int,\n  padding: int,\n  paddingVertical: int,\n  paddingHorizontal: int,\n  textOverflow: TextOverflow.t,\n  overflow: LayoutTypes.overflow,\n  borderTop: Border.t,\n  borderLeft: Border.t,\n  borderRight: Border.t,\n  borderBottom: Border.t,\n  border: Border.t,\n  borderHorizontal: Border.t,\n  borderVertical: Border.t,\n  borderRadius: float,\n  transform: list(Transform.t),\n  opacity: float,\n  boxShadow: BoxShadow.properties,\n  cursor: option(MouseCursors.t),\n};\n\nlet make =\n    (\n      ~textOverflow=TextOverflow.Overflow,\n      ~backgroundColor: Color.t=Colors.transparentBlack,\n      ~width=Encoding.cssUndefined,\n      ~height=Encoding.cssUndefined,\n      ~flexBasis=Encoding.cssUndefined,\n      ~flexDirection=LayoutTypes.Column,\n      ~flexGrow=Encoding.cssUndefined,\n      ~flexShrink=Encoding.cssUndefined,\n      ~flexWrap=LayoutTypes.CssNoWrap,\n      ~alignItems=LayoutTypes.AlignStretch,\n      ~justifyContent=LayoutTypes.JustifyFlexStart,\n      ~alignSelf=LayoutTypes.AlignAuto,\n      ~position=LayoutTypes.Relative,\n      ~top=Encoding.cssUndefined,\n      ~bottom=Encoding.cssUndefined,\n      ~left=Encoding.cssUndefined,\n      ~right=Encoding.cssUndefined,\n      ~lineHeight=1.2,\n      ~textWrap=TextWrapping.Wrap,\n      ~marginTop=Encoding.cssUndefined,\n      ~marginLeft=Encoding.cssUndefined,\n      ~marginRight=Encoding.cssUndefined,\n      ~marginBottom=Encoding.cssUndefined,\n      ~margin=Encoding.cssUndefined,\n      ~marginVertical=Encoding.cssUndefined,\n      ~marginHorizontal=Encoding.cssUndefined,\n      ~minWidth=Encoding.cssUndefined,\n      ~maxWidth=Encoding.cssUndefined,\n      ~minHeight=Encoding.cssUndefined,\n      ~maxHeight=Encoding.cssUndefined,\n      ~paddingTop=Encoding.cssUndefined,\n      ~paddingLeft=Encoding.cssUndefined,\n      ~paddingRight=Encoding.cssUndefined,\n      ~paddingBottom=Encoding.cssUndefined,\n      ~padding=Encoding.cssUndefined,\n      ~paddingHorizontal=Encoding.cssUndefined,\n      ~paddingVertical=Encoding.cssUndefined,\n      ~overflow=LayoutTypes.Visible,\n      ~borderTop=Border.make(),\n      ~borderLeft=Border.make(),\n      ~borderRight=Border.make(),\n      ~borderBottom=Border.make(),\n      ~border=Border.make(),\n      ~borderHorizontal=Border.make(),\n      ~borderVertical=Border.make(),\n      ~borderRadius=0.0,\n      ~transform=[],\n      ~opacity=1.0,\n      ~pointerEvents=PointerEvents.Default,\n      ~boxShadow=BoxShadow.{\n                   xOffset: 0.0,\n                   yOffset: 0.0,\n                   blurRadius: 0.0,\n                   spreadRadius: 0.0,\n                   color: Colors.black,\n                 },\n      ~cursor=?,\n      ~color=?,\n      _unit: unit,\n    ) => {\n  let ret: t = {\n    textOverflow,\n    backgroundColor,\n    color,\n    width,\n    height,\n    flexBasis,\n    flexDirection,\n    flexGrow,\n    flexShrink,\n    flexWrap,\n    justifyContent,\n    alignItems,\n    alignSelf,\n    position,\n    top,\n    bottom,\n    left,\n    right,\n    lineHeight,\n    textWrap,\n    transform,\n    marginTop,\n    marginLeft,\n    marginRight,\n    marginBottom,\n    margin,\n    marginVertical,\n    marginHorizontal,\n    minWidth,\n    maxWidth,\n    minHeight,\n    maxHeight,\n    paddingTop,\n    paddingLeft,\n    paddingRight,\n    paddingBottom,\n    padding,\n    paddingHorizontal,\n    paddingVertical,\n    pointerEvents,\n    overflow,\n    borderTop,\n    borderLeft,\n    borderRight,\n    borderBottom,\n    border,\n    borderHorizontal,\n    borderVertical,\n    borderRadius,\n    opacity,\n    boxShadow,\n    cursor,\n  };\n\n  ret;\n};\n\nlet defaultStyle = make();\n\nlet toLayoutNode = (s: t) => {\n  let ret: LayoutTypes.cssStyle = {\n    ...LayoutSupport.defaultStyle,\n    positionType: s.position,\n    top: s.top,\n    left: s.left,\n    bottom: s.bottom,\n    flexBasis: s.flexBasis,\n    flexDirection: s.flexDirection,\n    flexGrow: s.flexGrow,\n    flexShrink: s.flexShrink,\n    flexWrap: s.flexWrap,\n    alignItems: s.alignItems,\n    alignSelf: s.alignSelf,\n    justifyContent: s.justifyContent,\n    right: s.right,\n    width: s.width,\n    height: s.height,\n    marginTop: s.marginTop,\n    marginLeft: s.marginLeft,\n    marginRight: s.marginRight,\n    marginBottom: s.marginBottom,\n    margin: s.margin,\n    marginVertical: s.marginVertical,\n    marginHorizontal: s.marginHorizontal,\n    minWidth: s.minWidth,\n    maxWidth: s.maxWidth,\n    minHeight: s.minHeight,\n    maxHeight: s.maxHeight,\n    paddingTop: s.paddingTop,\n    paddingLeft: s.paddingLeft,\n    paddingRight: s.paddingRight,\n    paddingBottom: s.paddingBottom,\n    padding: s.padding,\n    paddingVertical: s.paddingVertical,\n    paddingHorizontal: s.paddingHorizontal,\n    borderTop: s.borderTop.width,\n    borderLeft: s.borderLeft.width,\n    borderRight: s.borderRight.width,\n    borderBottom: s.borderBottom.width,\n    border: s.border.width,\n    borderHorizontal: s.borderHorizontal.width,\n    borderVertical: s.borderVertical.width,\n    overflow: s.overflow,\n  };\n  ret;\n};\n\n/* -------------------------------------------------------------------------------\n        Styles: As a list of Polymorphic variants\n   -------------------------------------------------------------------------------*/\ntype coords = {\n  top: int,\n  right: int,\n  bottom: int,\n  left: int,\n};\n\ntype xy = {\n  horizontal: int,\n  vertical: int,\n};\n\ntype coreStyleProps = [\n  | `FlexGrow(int)\n  | `FlexShrink(int)\n  | `FlexBasis(int)\n  | `FlexDirection(LayoutTypes.flexDirection)\n  | `FlexWrap(LayoutTypes.wrapType)\n  | `JustifyContent(LayoutTypes.justify)\n  | `AlignItems(LayoutTypes.align)\n  | `AlignSelf(LayoutTypes.align)\n  | `Position(LayoutTypes.positionType)\n  | `BackgroundColor(Color.t)\n  | `Color(option(Color.t))\n  | `Width(int)\n  | `Height(int)\n  | `Top(int)\n  | `Right(int)\n  | `Bottom(int)\n  | `Left(int)\n  | `MarginTop(int)\n  | `MarginLeft(int)\n  | `MarginRight(int)\n  | `MarginBottom(int)\n  | `Margin(int)\n  | `MarginVertical(int)\n  | `MarginHorizontal(int)\n  | `Margin2(xy)\n  | `Margin4(coords)\n  | `MinWidth(int)\n  | `MaxWidth(int)\n  | `MinHeight(int)\n  | `MaxHeight(int)\n  | `PaddingTop(int)\n  | `PaddingLeft(int)\n  | `PaddingRight(int)\n  | `PaddingBottom(int)\n  | `Padding(int)\n  | `PaddingHorizontal(int)\n  | `PaddingVertical(int)\n  | `Padding2(xy)\n  | `Padding4(coords)\n  | `Overflow(LayoutTypes.overflow)\n  | `BorderTop(Border.t)\n  | `BorderLeft(Border.t)\n  | `BorderRight(Border.t)\n  | `BorderBottom(Border.t)\n  | `Border(Border.t)\n  | `BorderHorizontal(Border.t)\n  | `BorderVertical(Border.t)\n  | `BorderRadius(float)\n  | `Transform(list(Transform.t))\n  | `Opacity(float)\n  | `BoxShadow(BoxShadow.properties)\n  | `Cursor(option(MouseCursors.t))\n  | `PointerEvents(PointerEvents.t)\n];\n\ntype textProps = [\n  | `LineHeight(float)\n  | `TextWrap(TextWrapping.wrapType)\n  | `TextOverflow(TextOverflow.t)\n];\n\n/*\n   Text and View props take different style properties as such\n   these nodes are typed to only allow styles to be specified\n   which are relevant to each\n */\ntype textStyleProps = [ textProps | coreStyleProps];\ntype viewStyleProps = [ coreStyleProps];\ntype imageStyleProps = [ coreStyleProps];\n\ntype allProps = [ coreStyleProps | textProps];\n\nlet emptyTextStyle: list(textStyleProps) = [];\nlet emptyViewStyle: list(viewStyleProps) = [];\nlet emptyImageStyle: list(imageStyleProps) = [];\n\nlet flexDirection = d => {\n  let dir =\n    switch (d) {\n    | `Column => LayoutTypes.Column\n    | `ColumnReverse => LayoutTypes.ColumnReverse\n    | `RowReverse => LayoutTypes.RowReverse\n    | `Row => LayoutTypes.Row\n    };\n  `FlexDirection(dir);\n};\n\nlet flexWrap = w => {\n  let wrap =\n    switch (w) {\n    | `Wrap => LayoutTypes.CssWrap\n    | `NoWrap => LayoutTypes.CssNoWrap\n    };\n  `FlexWrap(wrap);\n};\n\nlet pointerEvents = v => {\n  let pe =\n    switch (v) {\n    | `Allow => PointerEvents.Allow\n    | `Ignore => PointerEvents.Ignore\n    };\n  `PointerEvents(pe);\n};\n\nlet textOverflow = overflow =>\n  (\n    switch (overflow) {\n    | `Ellipsis => TextOverflow.Ellipsis\n    | `Overflow => TextOverflow.Overflow\n    | `UserDefined(v) => TextOverflow.UserDefined(v)\n    }\n  )\n  |> (s => `TextOverflow(s));\n\nlet alignment = a =>\n  switch (a) {\n  | `Center => LayoutTypes.AlignCenter\n  | `Stretch => LayoutTypes.AlignStretch\n  | `Auto => LayoutTypes.AlignAuto\n  | `FlexStart => LayoutTypes.AlignFlexStart\n  | `FlexEnd => LayoutTypes.AlignFlexEnd\n  };\n\nlet justify = j =>\n  switch (j) {\n  | `FlexStart => LayoutTypes.JustifyFlexStart\n  | `Center => LayoutTypes.JustifyCenter\n  | `FlexEnd => LayoutTypes.JustifyFlexEnd\n  | `SpaceBetween => LayoutTypes.JustifySpaceBetween\n  | `SpaceAround => LayoutTypes.JustifySpaceAround\n  };\n\nlet flexGrow = g => `FlexGrow(g);\nlet flexShrink = g => `FlexShrink(g);\nlet flexBasis = g => `FlexBasis(g);\n\nlet right = f => `Right(f);\nlet bottom = f => `Bottom(f);\nlet left = f => `Left(f);\nlet top = f => `Top(f);\n\nlet lineHeight = h => `LineHeight(h);\nlet textWrap = w => `TextWrap(w);\n\nlet height = h => `Height(h);\nlet width = w => `Width(w);\n\nlet minWidth = w => `MinWidth(w);\nlet maxWidth = w => `MaxWidth(w);\nlet minHeight = h => `MinHeight(h);\nlet maxHeight = h => `MaxHeight(h);\n\nlet position = p => {\n  let value =\n    switch (p) {\n    | `Absolute => LayoutTypes.Absolute\n    | `Relative => LayoutTypes.Relative\n    };\n  `Position(value);\n};\n\nlet margin = m => `Margin(m);\nlet marginLeft = m => `MarginLeft(m);\nlet marginRight = m => `MarginRight(m);\nlet marginTop = m => `MarginTop(m);\nlet marginBottom = m => `MarginBottom(m);\nlet marginVertical = m => `MarginVertical(m);\nlet marginHorizontal = m => `MarginHorizontal(m);\nlet margin2 = (~horizontal, ~vertical) => `Margin2({horizontal, vertical});\nlet margin4 = (~top, ~right, ~bottom, ~left) =>\n  `Margin4({top, right, bottom, left});\n\nlet padding = m => `Padding(m);\nlet paddingLeft = m => `PaddingLeft(m);\nlet paddingRight = m => `PaddingRight(m);\nlet paddingTop = m => `PaddingTop(m);\nlet paddingBottom = m => `PaddingBottom(m);\nlet paddingVertical = m => `PaddingVertical(m);\nlet paddingHorizontal = m => `PaddingHorizontal(m);\nlet padding2 = (~horizontal, ~vertical) => `Padding2({horizontal, vertical});\nlet padding4 = (~top, ~right, ~bottom, ~left) =>\n  `Padding4({top, right, bottom, left});\n\nlet border = (~color, ~width) =>\n  Border.make(~color, ~width, ()) |> (b => `Border(b));\nlet borderLeft = (~color, ~width) =>\n  Border.make(~color, ~width, ()) |> (b => `BorderLeft(b));\nlet borderRight = (~color, ~width) =>\n  Border.make(~color, ~width, ()) |> (b => `BorderRight(b));\nlet borderTop = (~color, ~width) =>\n  Border.make(~color, ~width, ()) |> (b => `BorderTop(b));\nlet borderBottom = (~color, ~width) =>\n  Border.make(~color, ~width, ()) |> (b => `BorderBottom(b));\nlet borderHorizontal = (~color, ~width) =>\n  Border.make(~color, ~width, ()) |> (b => `BorderHorizontal(b));\nlet borderVertical = (~color, ~width) =>\n  Border.make(~color, ~width, ()) |> (b => `BorderVertical(b));\n\nlet borderRadius = r => `BorderRadius(r);\n\nlet alignItems = a => `AlignItems(alignment(a));\nlet justifyContent = a => `JustifyContent(justify(a));\nlet alignSelf = a => `AlignSelf(alignment(a));\n\nlet cursor = c => `Cursor(Some(c));\n\nlet opacity = o => `Opacity(o);\nlet transform = t => `Transform(t);\nlet boxShadow = (~xOffset, ~yOffset, ~spreadRadius, ~blurRadius, ~color) =>\n  `BoxShadow(BoxShadow.{xOffset, yOffset, spreadRadius, blurRadius, color});\n\nlet overflow = o =>\n  switch (o) {\n  | `Visible => `Overflow(LayoutTypes.Visible)\n  | `Hidden => `Overflow(LayoutTypes.Hidden)\n  | `Scroll => `Overflow(LayoutTypes.Scroll)\n  };\n\nlet color = o => `Color(Some(o));\nlet backgroundColor = o => `BackgroundColor(o);\n\n/*\n   Helper function to narrow down a list of all possible style props to\n   one specific to a type of component\n */\nlet rec extractViewStyles = (styles: list(allProps)): list(viewStyleProps) =>\n  switch (styles) {\n  | [] => []\n  | [#viewStyleProps as v, ...list] => [v, ...extractViewStyles(list)]\n  | [_, ...list] => extractViewStyles(list)\n  };\n\n/*\n   Apply style takes all style props and maps each to the correct style\n   and is used to build up the style record, which is eventually\n   used to apply styling to elements.\n */\nlet applyStyle = (style, styleRule) =>\n  switch (styleRule) {\n  | `AlignItems(alignItems) => {...style, alignItems}\n  | `AlignSelf(alignSelf) => {...style, alignSelf}\n  | `JustifyContent(justifyContent) => {...style, justifyContent}\n  | `FlexGrow(flexGrow) => {...style, flexGrow}\n  | `FlexShrink(flexShrink) => {...style, flexShrink}\n  | `FlexBasis(flexBasis) => {...style, flexBasis}\n  | `FlexDirection(flexDirection) => {...style, flexDirection}\n  | `FlexWrap(flexWrap) => {...style, flexWrap}\n  | `Position(position) => {...style, position}\n  | `Margin(margin) => {...style, margin}\n  | `MarginTop(marginTop) => {...style, marginTop}\n  | `MarginBottom(marginBottom) => {...style, marginBottom}\n  | `MarginRight(marginRight) => {...style, marginRight}\n  | `MarginLeft(marginLeft) => {...style, marginLeft}\n  | `MarginVertical(marginVertical) => {...style, marginVertical}\n  | `MarginHorizontal(marginHorizontal) => {...style, marginHorizontal}\n  | `Margin2({horizontal, vertical}) => {\n      ...style,\n      marginHorizontal: horizontal,\n      marginVertical: vertical,\n    }\n  | `Margin4({top, right, bottom, left}) => {\n      ...style,\n      marginTop: top,\n      marginLeft: left,\n      marginRight: right,\n      marginBottom: bottom,\n    }\n  | `MinWidth(minWidth) => {...style, minWidth}\n  | `MaxWidth(maxWidth) => {...style, maxWidth}\n  | `MinHeight(minHeight) => {...style, minHeight}\n  | `MaxHeight(maxHeight) => {...style, maxHeight}\n  | `Padding(padding) => {...style, padding}\n  | `PaddingTop(paddingTop) => {...style, paddingTop}\n  | `PaddingBottom(paddingBottom) => {...style, paddingBottom}\n  | `PaddingRight(paddingRight) => {...style, paddingRight}\n  | `PaddingLeft(paddingLeft) => {...style, paddingLeft}\n  | `PaddingVertical(paddingVertical) => {...style, paddingVertical}\n  | `PaddingHorizontal(paddingHorizontal) => {...style, paddingHorizontal}\n  | `Padding2({horizontal, vertical}) => {\n      ...style,\n      paddingHorizontal: horizontal,\n      paddingVertical: vertical,\n    }\n  | `Padding4({top, right, bottom, left}) => {\n      ...style,\n      paddingTop: top,\n      paddingLeft: left,\n      paddingRight: right,\n      paddingBottom: bottom,\n    }\n  | `Overflow(overflow) => {...style, overflow}\n  | `Border(border) => {...style, border}\n  | `BorderBottom(borderBottom) => {...style, borderBottom}\n  | `BorderTop(borderTop) => {...style, borderTop}\n  | `BorderLeft(borderLeft) => {...style, borderLeft}\n  | `BorderRight(borderRight) => {...style, borderRight}\n  | `BorderVertical(borderVertical) => {...style, borderVertical}\n  | `BorderHorizontal(borderHorizontal) => {...style, borderHorizontal}\n  | `BorderRadius(borderRadius) => {...style, borderRadius}\n  | `Opacity(opacity) => {...style, opacity}\n  | `BoxShadow(boxShadow) => {...style, boxShadow}\n  | `Transform(transform) => {...style, transform}\n  | `LineHeight(lineHeight) => {...style, lineHeight}\n  | `TextOverflow(textOverflow) => {...style, textOverflow}\n  | `TextWrap(textWrap) => {...style, textWrap}\n  | `Cursor(cursor) => {...style, cursor}\n  | `Color(color) => {...style, color}\n  | `BackgroundColor(backgroundColor) => {...style, backgroundColor}\n  | `Width(width) => {...style, width}\n  | `Height(height) => {...style, height}\n  | `Bottom(bottom) => {...style, bottom}\n  | `Left(left) => {...style, left}\n  | `Top(top) => {...style, top}\n  | `Right(right) => {...style, right}\n  | `PointerEvents(pointerEvents) => {...style, pointerEvents}\n  };\n\nlet create = (~style, ~default=make(), ()) =>\n  List.fold_left(applyStyle, default, style);\n\n/*\n   This function merges two lists of type styleProps\n   the target values override any similar source values\n\n   TODO: is there is a faster/more performant way to do this?\n */\nlet merge = (~source, ~target) =>\n  List.fold_left(\n    (merged, targetStyle) => {\n      let newStyles =\n        List.fold_left(\n          (accum, sourceStyle) =>\n            (\n              switch (targetStyle, sourceStyle) {\n              | (`Cursor(_), `Cursor(_)) => targetStyle\n              | (`Position(_), `Position(_)) => targetStyle\n              | (`BackgroundColor(_), `BackgroundColor(_)) => targetStyle\n              | (`Color(_), `Color(_)) => targetStyle\n              | (`Width(_), `Width(_)) => targetStyle\n              | (`Height(_), `Height(_)) => targetStyle\n              | (`Bottom(_), `Bottom(_)) => targetStyle\n              | (`Left(_), `Left(_)) => targetStyle\n              | (`Right(_), `Right(_)) => targetStyle\n              | (`MarginTop(_), `MarginTop(_)) => targetStyle\n              | (`MarginLeft(_), `MarginLeft(_)) => targetStyle\n              | (`MarginRight(_), `MarginRight(_)) => targetStyle\n              | (`MarginBottom(_), `MarginBottom(_)) => targetStyle\n              | (`Margin(_), `Margin(_)) => targetStyle\n              | (`MarginVertical(_), `MarginVertical(_)) => targetStyle\n              | (`MarginHorizontal(_), `MarginHorizontal(_)) => targetStyle\n              | (`Margin2(_), `Margin2(_)) => targetStyle\n              | (`Margin4(_), `Margin4(_)) => targetStyle\n              | (`PaddingTop(_), `PaddingTop(_)) => targetStyle\n              | (`PaddingLeft(_), `PaddingLeft(_)) => targetStyle\n              | (`PaddingRight(_), `PaddingRight(_)) => targetStyle\n              | (`PaddingBottom(_), `PaddingBottom(_)) => targetStyle\n              | (`Padding(_), `Padding(_)) => targetStyle\n              | (`PaddingVertical(_), `PaddingVertical(_)) => targetStyle\n              | (`PaddingHorizontal(_), `PaddingHorizontal(_)) => targetStyle\n              | (`Padding2(_), `Padding2(_)) => targetStyle\n              | (`Padding4(_), `Padding4(_)) => targetStyle\n              | (`Overflow(_), `Overflow(_)) => targetStyle\n              | (`BorderTop(_), `BorderTop(_)) => targetStyle\n              | (`BorderLeft(_), `BorderLeft(_)) => targetStyle\n              | (`BorderRight(_), `BorderRight(_)) => targetStyle\n              | (`BorderBottom(_), `BorderBottom(_)) => targetStyle\n              | (`Border(_), `Border(_)) => targetStyle\n              | (`BorderHorizontal(_), `BorderHorizontal(_)) => targetStyle\n              | (`BorderVertical(_), `BorderVertical(_)) => targetStyle\n              | (`BorderRadius(_), `BorderRadius(_)) => targetStyle\n              | (`Transform(_), `Transform(_)) => targetStyle\n              | (`Opacity(_), `Opacity(_)) => targetStyle\n              | (`PointerEvents(_), `PointerEvents(_)) => targetStyle\n              | (`BoxShadow(_), `BoxShadow(_)) => targetStyle\n              | (newRule, _) => newRule\n              }\n            )\n            |> (style => [style, ...accum]),\n          [],\n          source,\n        );\n      List.append(merged, newStyles);\n    },\n    source,\n    target,\n  );\n\n/* -------------------------------------------------------------------------------*/\n"
  },
  {
    "path": "src/UI/TextNode.re",
    "content": "open Revery_Draw;\n\nmodule Layout = Layout;\nmodule LayoutTypes = Layout.LayoutTypes;\n\nopen Revery_Core;\nopen Revery_Font;\n\nopen Style;\nopen ViewNode;\n\nopen {\n       let int_of_float_ceil = f => int_of_float(f +. 1.);\n     };\n\nclass textNode (text: string) = {\n  as _this;\n  val mutable text = text;\n  val mutable _isMeasured = false;\n  val mutable _lines: list(string) = [];\n  val mutable _smoothing = Smoothing.default;\n  val mutable _fontFamily = Family.default;\n  val mutable _fontWeight = Weight.Normal;\n  val mutable _italicized = false;\n  val mutable _fontSize = 14.;\n  val mutable _underlined = false;\n  val mutable _features: list(Feature.t) = [];\n  val _textPaint = {\n    let paint = Skia.Paint.make();\n    Skia.Paint.setTextEncoding(paint, GlyphId);\n    Skia.Paint.setLcdRenderText(paint, true);\n    Skia.Paint.setAntiAlias(paint, true);\n    paint;\n  };\n  inherit (class viewNode)() as _super;\n  pub! draw = (parentContext: NodeDrawContext.t) => {\n    let style = _super#getStyle();\n\n    let {color: maybeColor, lineHeight, _} = style;\n    let color = Option.value(maybeColor, ~default=Colors.white);\n    let opacity = parentContext.opacity *. style.opacity;\n    let colorWithAppliedOpacity = Color.multiplyAlpha(opacity, color);\n\n    switch (Family.resolve(~italic=_italicized, _fontWeight, _fontFamily)) {\n    | Error(_) => ()\n    | Ok(font) =>\n      Revery_Font.Smoothing.setPaint(~smoothing=_smoothing, _textPaint);\n      Skia.Paint.setColor(_textPaint, Color.toSkia(colorWithAppliedOpacity));\n      Skia.Paint.setTextSize(_textPaint, _fontSize);\n\n      let ascentPx =\n        Text.ascent(~italic=_italicized, _fontFamily, _fontSize, _fontWeight);\n      let lineHeightPx =\n        lineHeight\n        *. Text.lineHeight(\n             ~italic=_italicized,\n             _fontFamily,\n             _fontSize,\n             _fontWeight,\n           );\n\n      /* when style.width & style.height are defined, Layout doesn't call the measure function */\n      if (!_isMeasured) {\n        _this#measure(style.width, style.height) |> ignore;\n      };\n\n      let {canvas, _}: NodeDrawContext.t = parentContext;\n      // TODO find a way to only manage the matrix stack in Node\n      let world = _this#getWorldTransform();\n      Revery_Draw.CanvasContext.setMatrix(canvas, world);\n\n      List.iteri(\n        (lineIndex, line) => {\n          let baselineY =\n            ascentPx *. (-1.0) +. lineHeightPx *. float_of_int(lineIndex);\n\n          let glyphStrings =\n            line\n            |> Revery_Font.shape(~features=_features, font)\n            |> Revery_Font.ShapeResult.getGlyphStrings;\n\n          let offset = ref(0.);\n\n          glyphStrings\n          |> List.iter(((skiaFace, str)) => {\n               Skia.Paint.setTypeface(_textPaint, skiaFace);\n\n               CanvasContext.drawText(\n                 ~paint=_textPaint,\n                 ~x=offset^,\n                 ~y=baselineY,\n                 ~text=str,\n                 canvas,\n               );\n\n               offset :=\n                 offset^ +. Skia.Paint.measureText(_textPaint, str, None);\n             });\n\n          if (_underlined) {\n            let {underlinePosition, underlineThickness, _}: FontMetrics.t =\n              FontCache.getMetrics(font, _fontSize);\n\n            let width =\n              FontRenderer.measure(\n                ~smoothing=_smoothing,\n                ~features=_features,\n                font,\n                _fontSize,\n                line,\n              ).\n                width;\n\n            let rect =\n              Skia.Rect.makeLtrb(\n                0.,\n                baselineY +. underlinePosition -. underlineThickness /. 2.,\n                width,\n                baselineY +. underlinePosition +. underlineThickness /. 2.,\n              );\n            CanvasContext.drawRect(~rect, ~paint=_textPaint, canvas);\n          };\n        },\n        _lines,\n      );\n    };\n  };\n  pub! setStyle = style => {\n    let lastStyle = _this#getStyle();\n    _super#setStyle(style);\n    let newStyle = _this#getStyle();\n\n    if (lastStyle.lineHeight != newStyle.lineHeight) {\n      _this#markLayoutDirty();\n    };\n  };\n  pub textOverflow = (maxWidth): LayoutTypes.dimensions => {\n    let {lineHeight, textOverflow, _}: Style.t = _super#getStyle();\n\n    let formattedText = TextOverflow.removeLineBreaks(text);\n\n    let measure = str =>\n      Text.dimensions(\n        ~smoothing=_smoothing,\n        ~italic=_italicized,\n        ~fontFamily=_fontFamily,\n        ~fontSize=_fontSize,\n        ~fontWeight=_fontWeight,\n        str,\n      )\n      |> (value => value.width);\n\n    let width = measure(formattedText);\n    let isOverflowing = width >= maxWidth;\n\n    let handleOverflow =\n      TextOverflow.handleOverflow(~maxWidth, ~text=formattedText, ~measure);\n\n    let truncated =\n      switch (textOverflow, isOverflowing) {\n      | (Ellipsis, true) => handleOverflow()\n      | (UserDefined(character), true) => handleOverflow(~character, ())\n      | (Clip, true) => handleOverflow(~character=\"\", ())\n      | (_, false)\n      | (Overflow, _) => text\n      };\n\n    _lines = [truncated];\n\n    let lineHeightPx =\n      lineHeight\n      *. Text.lineHeight(\n           ~italic=_italicized,\n           _fontFamily,\n           _fontSize,\n           _fontWeight,\n         );\n\n    {\n      width: int_of_float_ceil(width),\n      height: int_of_float_ceil(lineHeightPx),\n    };\n  };\n  pub setText = t =>\n    if (!String.equal(t, text)) {\n      text = t;\n      _isMeasured = false;\n      _this#markLayoutDirty();\n    };\n  pub setSmoothing = smoothing => _smoothing = smoothing;\n  pub setFontFamily = fontFamily =>\n    if (_fontFamily !== fontFamily) {\n      _fontFamily = fontFamily;\n      _isMeasured = false;\n      _this#markLayoutDirty();\n    };\n  pub setFontWeight = fontWeight =>\n    if (_fontWeight != fontWeight) {\n      _fontWeight = fontWeight;\n      _isMeasured = false;\n      _this#markLayoutDirty();\n    };\n  pub setItalicized = italicized =>\n    if (_italicized != italicized) {\n      _italicized = italicized;\n      _isMeasured = false;\n      _this#markLayoutDirty();\n    };\n  pub setFontSize = fontSize =>\n    if (_fontSize != fontSize) {\n      _fontSize = fontSize;\n      _isMeasured = false;\n      _this#markLayoutDirty();\n    };\n  pub setUnderlined = underlined => {\n    if (_underlined != underlined) {\n      _this#markLayoutDirty();\n    };\n    _underlined = underlined;\n  };\n  pub setFeatures = features => {\n    if (_features != features) {\n      _this#markLayoutDirty();\n    };\n    _features = features;\n  };\n  pub measure = (width, _height): LayoutTypes.dimensions => {\n    _isMeasured = true;\n    /**\n         If the width value is set to cssUndefined i.e. the user did not\n         set a width then do not attempt to use textOverflow\n       */\n    (\n      switch (_super#getStyle()) {\n      | {textOverflow: Ellipsis | UserDefined(_), _} =>\n        _this#textOverflow(float_of_int(width))\n      | style => _this#handleTextWrapping(width, style)\n      }\n    );\n  };\n  pub handleTextWrapping = (width, style) => {\n    let {textWrap, lineHeight, _}: Style.t = style;\n    let lineHeightPx =\n      lineHeight\n      *. Text.lineHeight(\n           ~italic=_italicized,\n           _fontFamily,\n           _fontSize,\n           _fontWeight,\n         );\n\n    let measureWidth = str =>\n      Text.dimensions(\n        ~smoothing=_smoothing,\n        ~italic=_italicized,\n        ~fontFamily=_fontFamily,\n        ~fontSize=_fontSize,\n        ~fontWeight=_fontWeight,\n        str,\n      )\n      |> (value => value.width);\n    _lines =\n      TextWrapping.wrapText(\n        ~text,\n        ~measureWidth,\n        ~maxWidth=float_of_int(width),\n        ~mode=textWrap,\n      );\n\n    let pickWiderLine = (leftWidth, right) => {\n      let rightWidth =\n        Text.dimensions(\n          ~smoothing=_smoothing,\n          ~italic=_italicized,\n          ~fontFamily=_fontFamily,\n          ~fontSize=_fontSize,\n          ~fontWeight=_fontWeight,\n          right,\n        ).\n          width;\n      max(leftWidth, rightWidth);\n    };\n    let maxWidthLine = List.fold_left(pickWiderLine, 0., _lines);\n    {\n      width: int_of_float_ceil(maxWidthLine),\n      height:\n        int_of_float_ceil(\n          float_of_int(List.length(_lines)) *. lineHeightPx,\n        ),\n    };\n  };\n  pub! getMeasureFunction = () => {\n    let measure =\n        (_mode, width, _widthMeasureMode, height, _heightMeasureMode) =>\n      _this#measure(width, height);\n    Some(measure);\n  };\n};\n"
  },
  {
    "path": "src/UI/Transform.re",
    "content": "open Revery_Math;\nopen Revery_Math.Angle;\n\ntype t =\n  | RotateZ(Angle.t)\n  | RotateY(Angle.t)\n  | RotateX(Angle.t)\n  | Rotate(Angle.t)\n  | Scale(float)\n  | ScaleX(float)\n  | ScaleY(float)\n  | TranslateX(float)\n  | TranslateY(float);\n\nmodule Internal = {\n  let rotateWithOrigin = (x: float, y: float, angle, axisX, axisY, axisZ) => {\n    // TODO:\n    // This could be made significantly more efficient, with less allocations,\n    // by using the pre* and post* operations, instead of set*.\n    let preTranslate = Skia.Matrix44.makeEmpty();\n    Skia.Matrix44.setTranslate(preTranslate, (-1.) *. x, (-1.) *. y, 0.0);\n\n    let rotation = Skia.Matrix44.makeEmpty();\n    switch (angle) {\n    | Degrees(deg) =>\n      Skia.Matrix44.setRotateAboutDegrees(rotation, axisX, axisY, axisZ, deg)\n    | Radians(rad) =>\n      Skia.Matrix44.setRotateAboutRadians(rotation, axisX, axisY, axisZ, rad)\n    };\n\n    let postTranslate = Skia.Matrix44.makeEmpty();\n    Skia.Matrix44.setTranslate(postTranslate, x, y, 0.);\n\n    let out = Skia.Matrix44.makeEmpty();\n    Skia.Matrix44.setConcat(out, rotation, preTranslate);\n    Skia.Matrix44.setConcat(out, postTranslate, out);\n    let mat = Skia.Matrix.make();\n    Skia.Matrix44.toMatrix(out, mat);\n    mat;\n  };\n\n  let toMat4 = (originX: float, originY: float, t) => {\n    switch (t) {\n    | RotateX(a) => rotateWithOrigin(originX, originY, a, 1.0, 0.0, 0.0)\n    | RotateY(a) => rotateWithOrigin(originX, originY, a, 0.0, 1.0, 0.0)\n    | RotateZ(a) => rotateWithOrigin(originX, originY, a, 0.0, 0.0, 1.0)\n    | Rotate(a) => rotateWithOrigin(originX, originY, a, 0.0, 0.0, 1.0)\n    | Scale(a) => Skia.Matrix.makeScale(a, a, 0.0, 0.0)\n    | ScaleX(a) => Skia.Matrix.makeScale(a, 1.0, 0.0, 0.0)\n    | ScaleY(a) => Skia.Matrix.makeScale(1.0, a, 0.0, 0.0)\n    | TranslateX(a) => Skia.Matrix.makeTranslate(a, 0.)\n    | TranslateY(a) => Skia.Matrix.makeTranslate(0., a)\n    };\n  };\n\n  let identity = {\n    let mat = Skia.Matrix.make();\n    Skia.Matrix.setIdentity(mat);\n    mat;\n  };\n};\n\nlet toMat4 = (originX, originY, transforms: list(t)) => {\n  switch (transforms) {\n  | [] => Internal.identity\n  | transforms =>\n    // We can't reuse Internal.identity because we write to this matrix\n    let initial = Skia.Matrix.make();\n    Skia.Matrix.setIdentity(initial);\n\n    List.fold_left(\n      (prev, transform) => {\n        let xfm = Internal.toMat4(originX, originY, transform);\n        Skia.Matrix.concat(prev, prev, xfm);\n        prev;\n      },\n      initial,\n      transforms,\n    );\n  };\n};\n"
  },
  {
    "path": "src/UI/Transform.rei",
    "content": "open Revery_Math;\n\ntype t =\n  | RotateZ(Angle.t)\n  | RotateY(Angle.t)\n  | RotateX(Angle.t)\n  | Rotate(Angle.t)\n  | Scale(float)\n  | ScaleX(float)\n  | ScaleY(float)\n  | TranslateX(float)\n  | TranslateY(float);\n\nlet toMat4: (float, float, list(t)) => Skia.Matrix.t;\n"
  },
  {
    "path": "src/UI/Ui.re",
    "content": "/*\n * Ui.re\n *\n * State that is persisted across renderings.\n * This stores the connection between a window and its UI\n */\n\n/*\n * TODO:\n * We have ignored the callback to clean the subscription.\n * We should call them if we want to have multiple windows support.\n */\n\nmodule Window = Revery_Core.Window;\nmodule Log = (val Revery_Core.Log.withNamespace(\"Revery.Ui\"));\n\nopen RenderContainer;\n\nlet _activeWindow: ref(option(Window.t)) = ref(None);\n\ntype renderFunction = React.element(React.reveryNode) => unit;\n\ntype mouseBehavior =\n  Sdl2.Window.hitTestResult =\n    | Normal\n    | Draggable\n    | ResizeTopLeft\n    | ResizeTop\n    | ResizeTopRight\n    | ResizeRight\n    | ResizeBottomRight\n    | ResizeBottom\n    | ResizeBottomLeft\n    | ResizeLeft;\n\nlet getActiveWindow = () => _activeWindow^;\n\nlet start =\n    (\n      ~onBeforeRender=() => (),\n      ~onAfterRender=() => (),\n      window: Window.t,\n      element: React.element(React.reveryNode),\n    ) => {\n  let uiDirty = ref(true);\n  let forceLayout = ref(true);\n  let latestElement = ref(element);\n\n  let onStale = () => uiDirty := true;\n\n  let _ignore = Revery_Core.Event.subscribe(React.onStale, onStale);\n\n  let rootNode = (new ViewNode.viewNode)();\n  let mouseCursor: Mouse.Cursor.t = Mouse.Cursor.make();\n  let container = Container.create(rootNode);\n  let ui = RenderContainer.create(window, rootNode, container, mouseCursor);\n\n  if (!Window.isDecorated(window)) {\n    Sdl2.Window.setHitTest(\n      Window.getSdlWindow(window),\n      HitTest.windowCallback(rootNode, window),\n    );\n  };\n\n  let _ignore = Window.onExposed(window, () => uiDirty := true);\n\n  let _ignore =\n    Window.onMouseMove(\n      window,\n      m => {\n        let scaleAndZoomFactor = Window.getScaleAndZoom(window);\n        let evt =\n          Revery_Core.Events.InternalMouseMove({\n            mouseX: m.mouseX /. scaleAndZoomFactor,\n            mouseY: m.mouseY /. scaleAndZoomFactor,\n            keymod: m.keymod,\n          });\n        Mouse.dispatch(mouseCursor, evt, rootNode);\n      },\n    );\n\n  let _ignore =\n    Window.onMouseWheel(\n      window,\n      m => {\n        let scaleAndZoomFactor = Window.getScaleAndZoom(window);\n        let evt =\n          Revery_Core.Events.InternalMouseWheel({\n            deltaX: m.deltaX,\n            deltaY: m.deltaY,\n            keymod: m.keymod,\n            mouseX: m.mouseX /. scaleAndZoomFactor,\n            mouseY: m.mouseY /. scaleAndZoomFactor,\n          });\n        Mouse.dispatch(mouseCursor, evt, rootNode);\n      },\n    );\n\n  let _ignore =\n    Window.onMouseDown(\n      window,\n      m => {\n        let evt =\n          Revery_Core.Events.InternalMouseDown({\n            button: m.button,\n            keymod: m.keymod,\n          });\n        Mouse.dispatch(mouseCursor, evt, rootNode);\n      },\n    );\n\n  let _ignore =\n    Window.onKeyDown(window, event =>\n      Keyboard.dispatch(Revery_Core.Events.InternalKeyDownEvent(event))\n    );\n\n  let _ignore =\n    Window.onKeyUp(window, event =>\n      Keyboard.dispatch(Revery_Core.Events.InternalKeyUpEvent(event))\n    );\n\n  let _ignore =\n    Window.onTextInputCommit(window, event =>\n      Keyboard.dispatch(Revery_Core.Events.InternalTextInputEvent(event))\n    );\n\n  let _ignore =\n    Window.onCompositionEdit(window, event =>\n      Keyboard.dispatch(Revery_Core.Events.InternalTextEditEvent(event))\n    );\n\n  let _ignore =\n    Window.onMouseUp(\n      window,\n      m => {\n        let evt =\n          Revery_Core.Events.InternalMouseUp({\n            button: m.button,\n            keymod: m.keymod,\n          });\n        Mouse.dispatch(mouseCursor, evt, rootNode);\n      },\n    );\n\n  let _ignore: Window.unsubscribe =\n    Window.onFileDropped(\n      window,\n      f => {\n        Log.trace(\"File dropped\");\n        let scaleAndZoomFactor = Window.getScaleAndZoom(window);\n        let evt =\n          Revery_Core.Events.InternalFileDropped({\n            mouseX: f.mouseX /. scaleAndZoomFactor,\n            mouseY: f.mouseY /. scaleAndZoomFactor,\n            paths: f.paths,\n            keymod: f.keymod,\n          });\n        FileDrop.dispatch(evt, rootNode);\n      },\n    );\n\n  let _ignore =\n    Revery_Core.Event.subscribe(Mouse.onCursorChanged, cursor =>\n      Revery_Core.MouseCursors.setCursor(cursor)\n    );\n\n  let _ignore =\n    Revery_Core.Event.subscribe(\n      Revery_Font.FontCache.onFontLoaded,\n      () => {\n        uiDirty := true;\n        forceLayout := true;\n      },\n    );\n\n  Window.setShouldRenderCallback(window, () => uiDirty^);\n  Window.setRenderCallback(\n    window,\n    () => {\n      onBeforeRender();\n      /*\n       * The dirty flag needs to be cleared before rendering,\n       * as some events during rendering might trigger a 'dirty',\n       * meaning that we'll need to re-render again next frame.\n       */\n      uiDirty := false;\n\n      /*\n       * The forceLayout event also needs to be cleared prior to rendering,\n       * as we might get an event during rendering - like font loaded -\n       * that would be ignored if we cleared after.\n       */\n      let fl = forceLayout^;\n      forceLayout := false;\n\n      _activeWindow := Some(window);\n      let forceRerender = Render.render(~forceLayout=fl, ui, latestElement^);\n      if (forceRerender) {\n        uiDirty := true;\n      };\n      onAfterRender();\n    },\n  );\n\n  let render = (element: React.element(React.reveryNode)) => {\n    latestElement := element;\n    uiDirty := true;\n  };\n\n  render;\n};\n"
  },
  {
    "path": "src/UI/Ui.rei",
    "content": "open Revery_Core;\n\ntype renderFunction = React.element(React.reveryNode) => unit;\n\n/**\n  [start] is the entry point for creating a UI driven by the\n  brisk-reconciler React infrastructure.\n\n  This does the following:\n  - Hook up a render loop\n  - Take a React.element and render it via GPU\n  - Hook up GLFW events and convert them to Revery UI events\n\n  [start] returns a function that can be used to update the UI.\n\n  An example usage would be:\n  [let update = start(window, <View />);]\n  [update(<Button />);]\n*/\n\nlet start:\n  (\n    ~onBeforeRender: unit => unit=?,\n    ~onAfterRender: unit => unit=?,\n    Window.t,\n    React.element(React.reveryNode)\n  ) =>\n  renderFunction;\n\nlet getActiveWindow: unit => option(Window.t);\n\n/** [mouseBehavior] is an alias for SDL2's hit-test results.\n       Each option is fairly self explanatory, but please note that\n       these only work if you set `decorated` to false on window\n       creation. This is unfortunately due to the fact that enabling\n       hit tests causes the default chrome behavior on Windows to no\n       longer work.\n   */\ntype mouseBehavior =\n  Sdl2.Window.hitTestResult =\n    | Normal\n    | Draggable\n    | ResizeTopLeft\n    | ResizeTop\n    | ResizeTopRight\n    | ResizeRight\n    | ResizeBottomRight\n    | ResizeBottom\n    | ResizeBottomLeft\n    | ResizeLeft;\n"
  },
  {
    "path": "src/UI/UiEvents.re",
    "content": "open Node;\nopen NodeEvents;\n\nmodule BubbleEvent: {\n  type t =\n    pri {\n      event,\n      mutable shouldPropagate: bool,\n      mutable defaultPrevented: bool,\n    };\n\n  let stopPropagation: t => unit;\n  let preventDefault: t => unit;\n  let make: event => t;\n} = {\n  type t = {\n    event,\n    mutable shouldPropagate: bool,\n    mutable defaultPrevented: bool,\n  };\n\n  let stopPropagation = event => event.shouldPropagate = false;\n\n  let preventDefault = event => event.defaultPrevented = true;\n\n  let make = event => {event, shouldPropagate: true, defaultPrevented: false};\n};\n\nlet isNodeImpacted = (n, x, y) => n#hitTest(x, y);\n\nlet rec getFirstFocusable = (node: node, x: float, y: float) =>\n  if (!isNodeImpacted(node, x, y)) {\n    None;\n  } else if (node#canBeFocused()) {\n    Some(node);\n  } else if (List.length(node#getChildren()) !== 0) {\n    checkChildren(node#getChildren(), x, y);\n  } else {\n    None;\n  }\nand checkChildren = (children, mouseX, mouseY) =>\n  switch (children) {\n  | [] => None\n  | [x, ...xs] =>\n    switch (getFirstFocusable(x, mouseX, mouseY)) {\n    | Some(node) => Some(node)\n    | None => checkChildren(xs, mouseX, mouseY)\n    }\n  };\n\ntype pointerEventMode =\n  | Default\n  | Ignore;\n\nlet getTopMostNode = (node: node, x, y) => {\n  open Style;\n\n  let rec f = (node: node, pointerEventMode) => {\n    let style = node#getStyle();\n\n    if (!isNodeImpacted(node, x, y)) {\n      None;\n    } else {\n      let mode =\n        switch (style.pointerEvents) {\n        | PointerEvents.Allow => Default\n        | PointerEvents.Ignore => Ignore\n        | PointerEvents.Default => pointerEventMode\n        };\n\n      let ignored = mode == Ignore;\n\n      let revChildren = List.rev(node#getChildren());\n      let maybeChildNode =\n        switch (revChildren) {\n        | [] => ignored ? None : Some(node)\n        | children =>\n          List.fold_left(\n            (prev, curr) =>\n              switch (prev) {\n              | Some(v) => Some(v)\n              | None => f(curr, mode)\n              },\n            None,\n            children,\n          )\n        };\n\n      switch (maybeChildNode) {\n      | None => ignored ? None : Some(node)\n      | Some(childNode) => Some(childNode)\n      };\n    };\n  };\n\n  f(node, Default);\n};\n\nlet rec traverseHierarchy = (bubbled, node) => {\n  BubbleEvent.\n    /*\n     track if default prevent or propagation stopped per node\n     stop traversing node hierarchy if stop propagation is called\n      */\n    (\n      if (bubbled.shouldPropagate) {\n        node#handleEvent(bubbled.event);\n        node#getParent() |> Option.iter(traverseHierarchy(bubbled));\n      }\n    );\n};\n\nlet bubble = (node, event) => {\n  /* Wrap event with preventDefault and stopPropagation */\n  traverseHierarchy(\n    BubbleEvent.make(event),\n    node,\n  );\n};\n"
  },
  {
    "path": "src/UI/ViewNode.re",
    "content": "open Revery_Core;\n\nmodule Layout = Layout;\nmodule LayoutTypes = Layout.LayoutTypes;\n\nopen Node;\nopen Style;\nopen Style.Border;\nopen Style.BoxShadow;\n\nlet borderStyle = (side, axis, border) =>\n  Layout.Encoding.(\n    if (side.width !== cssUndefined) {\n      (float(side.width), side.color);\n    } else if (axis.width !== cssUndefined) {\n      (float(axis.width), axis.color);\n    } else if (border.width !== cssUndefined) {\n      (float(border.width), border.color);\n    } else {\n      (0., Colors.black);\n    }\n  );\n\nlet makeTriangle = (aX, aY, bX, bY, cX, cY) => {\n  let triangle = Skia.Path.make();\n  Skia.Path.moveTo(triangle, aX, aY);\n  Skia.Path.lineTo(triangle, bX, bY);\n  Skia.Path.lineTo(triangle, cX, cY);\n  Skia.Path.lineTo(triangle, aX, aY);\n  triangle;\n};\n\nlet renderBorders = (~canvas, ~style, ~outerRRect, ~opacity) => {\n  let {borderRadius, _} = style;\n  let (topBorderWidth, topBorderColor) =\n    borderStyle(style.borderTop, style.borderVertical, style.border);\n  let (leftBorderWidth, leftBorderColor) =\n    borderStyle(style.borderLeft, style.borderHorizontal, style.border);\n  let (rightBorderWidth, rightBorderColor) =\n    borderStyle(style.borderRight, style.borderHorizontal, style.border);\n  let (bottomBorderWidth, bottomBorderColor) =\n    borderStyle(style.borderBottom, style.borderVertical, style.border);\n\n  if (leftBorderWidth === 0.\n      && topBorderWidth === 0.\n      && rightBorderWidth === 0.\n      && bottomBorderWidth === 0.) {\n    outerRRect;\n  } else {\n    let outerWidth = Skia.RRect.getWidth(outerRRect);\n    let outerHeight = Skia.RRect.getHeight(outerRRect);\n\n    let innerRRect = Skia.RRect.make();\n    Skia.RRect.setNinePatch(\n      innerRRect,\n      Skia.Rect.makeLtrb(\n        leftBorderWidth,\n        topBorderWidth,\n        outerWidth -. rightBorderWidth,\n        outerHeight -. bottomBorderWidth,\n      ),\n      // TODO For some reason, the clipping won't work with radii assigned - we need to revisit this\n      0.,\n      0.,\n      0.,\n      0.,\n      // max(borderRadius -. leftBorderWidth, 0.),\n      // max(borderRadius -. topBorderWidth, 0.),\n      // max(borderRadius -. rightBorderWidth, 0.),\n      // max(borderRadius -. bottomBorderWidth, 0.),\n    );\n\n    let tbc = Color.multiplyAlpha(opacity, topBorderColor);\n    let tbcAlpha = Color.getAlpha(tbc);\n    let lbc = Color.multiplyAlpha(opacity, leftBorderColor);\n    let lbcAlpha = Color.getAlpha(lbc);\n    let rbc = Color.multiplyAlpha(opacity, rightBorderColor);\n    let rbcAlpha = Color.getAlpha(rbc);\n    let bbc = Color.multiplyAlpha(opacity, bottomBorderColor);\n    let bbcAlpha = Color.getAlpha(bbc);\n\n    let borderPaint = Skia.Paint.make();\n    Skia.Paint.setAntiAlias(borderPaint, true);\n\n    let innerWidth = Skia.RRect.getWidth(innerRRect);\n    let innerHeight = Skia.RRect.getHeight(innerRRect);\n    let innerCenterX = leftBorderWidth +. innerWidth /. 2.;\n    let innerCenterY = topBorderWidth +. innerHeight /. 2.;\n\n    // We use these for finding the points where the color borders in the corners would intersect\n    // when extended into the center so that we can correctly draw borders between each two border\n    // colors. A trapezoid would work for most cases as well but we need a full triangle for cases\n    // with a rounded center shape\n    let horizontalExtrapolationFactor =\n      outerWidth /. (leftBorderWidth +. rightBorderWidth);\n    let verticalExtrapolationFactor =\n      outerHeight /. (topBorderWidth +. bottomBorderWidth);\n\n    let hasLeftOrRightBorder =\n      leftBorderWidth !== 0. || rightBorderWidth !== 0.;\n    let hasTopOrBottomBorder =\n      topBorderWidth !== 0. || bottomBorderWidth !== 0.;\n\n    if (leftBorderWidth != 0. && lbcAlpha > 0.001) {\n      let _id: int = Revery_Draw.CanvasContext.save(canvas);\n\n      let clippingRectangle =\n        Skia.Rect.makeLtrb(0., 0., innerCenterX, outerHeight);\n      Revery_Draw.CanvasContext.clipRect(canvas, clippingRectangle);\n\n      if (hasTopOrBottomBorder) {\n        let imaginaryIntersectionX =\n          verticalExtrapolationFactor *. leftBorderWidth;\n        let imaginaryIntersectionY =\n          verticalExtrapolationFactor *. topBorderWidth;\n        let clippingTriangle =\n          makeTriangle(\n            0.,\n            outerHeight,\n            0.,\n            0.,\n            imaginaryIntersectionX,\n            imaginaryIntersectionY,\n          );\n        Revery_Draw.CanvasContext.clipPath(canvas, clippingTriangle);\n      };\n\n      Revery_Draw.CanvasContext.clipRRect(\n        canvas,\n        ~clipOp=Difference,\n        innerRRect,\n      );\n\n      Skia.Paint.setColor(borderPaint, Color.toSkia(lbc));\n      Revery_Draw.CanvasContext.drawRRect(canvas, outerRRect, borderPaint);\n\n      Revery_Draw.CanvasContext.restore(canvas);\n    };\n\n    if (topBorderWidth != 0. && tbcAlpha > 0.001) {\n      let _id: int = Revery_Draw.CanvasContext.save(canvas);\n\n      let clippingRectangle =\n        Skia.Rect.makeLtrb(0., 0., outerWidth, innerCenterY);\n      Revery_Draw.CanvasContext.clipRect(canvas, clippingRectangle);\n\n      if (hasLeftOrRightBorder) {\n        let imaginaryIntersectionX =\n          horizontalExtrapolationFactor *. leftBorderWidth;\n        let imaginaryIntersectionY =\n          horizontalExtrapolationFactor *. topBorderWidth;\n        let clippingTriangle =\n          makeTriangle(\n            0.,\n            0.,\n            imaginaryIntersectionX,\n            imaginaryIntersectionY,\n            outerWidth,\n            0.,\n          );\n        Revery_Draw.CanvasContext.clipPath(canvas, clippingTriangle);\n      };\n\n      Revery_Draw.CanvasContext.clipRRect(\n        canvas,\n        ~clipOp=Difference,\n        innerRRect,\n      );\n\n      Skia.Paint.setColor(borderPaint, Color.toSkia(tbc));\n      Revery_Draw.CanvasContext.drawRRect(canvas, outerRRect, borderPaint);\n\n      Revery_Draw.CanvasContext.restore(canvas);\n    };\n\n    if (rightBorderWidth != 0. && rbcAlpha > 0.001) {\n      let _id: int = Revery_Draw.CanvasContext.save(canvas);\n\n      let clippingRectangle =\n        Skia.Rect.makeLtrb(innerCenterX, 0., outerWidth, outerHeight);\n      Revery_Draw.CanvasContext.clipRect(canvas, clippingRectangle);\n\n      if (hasTopOrBottomBorder) {\n        let imaginaryIntersectionX =\n          outerWidth -. verticalExtrapolationFactor *. rightBorderWidth;\n        let imaginaryIntersectionY =\n          verticalExtrapolationFactor *. topBorderWidth;\n        let clippingTriangle =\n          makeTriangle(\n            outerWidth,\n            0.,\n            outerWidth,\n            outerHeight,\n            imaginaryIntersectionX,\n            imaginaryIntersectionY,\n          );\n        Revery_Draw.CanvasContext.clipPath(canvas, clippingTriangle);\n      };\n\n      Revery_Draw.CanvasContext.clipRRect(\n        canvas,\n        ~clipOp=Difference,\n        innerRRect,\n      );\n\n      Skia.Paint.setColor(borderPaint, Color.toSkia(rbc));\n      Revery_Draw.CanvasContext.drawRRect(canvas, outerRRect, borderPaint);\n\n      Revery_Draw.CanvasContext.restore(canvas);\n    };\n\n    if (bottomBorderWidth != 0. && bbcAlpha > 0.001) {\n      let _id: int = Revery_Draw.CanvasContext.save(canvas);\n\n      let clippingRectangle =\n        Skia.Rect.makeLtrb(0., innerCenterY, outerWidth, outerHeight);\n      Revery_Draw.CanvasContext.clipRect(canvas, clippingRectangle);\n\n      if (hasLeftOrRightBorder) {\n        let imaginaryIntersectionX =\n          horizontalExtrapolationFactor *. leftBorderWidth;\n        let imaginaryIntersectionY =\n          outerHeight -. horizontalExtrapolationFactor *. bottomBorderWidth;\n        let clippingTriangle =\n          makeTriangle(\n            outerWidth,\n            outerHeight,\n            0.,\n            outerHeight,\n            imaginaryIntersectionX,\n            imaginaryIntersectionY,\n          );\n        Revery_Draw.CanvasContext.clipPath(canvas, clippingTriangle);\n      };\n\n      Revery_Draw.CanvasContext.clipRRect(\n        canvas,\n        ~clipOp=Difference,\n        innerRRect,\n      );\n\n      Skia.Paint.setColor(borderPaint, Color.toSkia(bbc));\n      Revery_Draw.CanvasContext.drawRRect(canvas, outerRRect, borderPaint);\n\n      Revery_Draw.CanvasContext.restore(canvas);\n    };\n\n    // TODO once the clipping works with non-zero radii, we won't need to reassign this here\n    Skia.RRect.setNinePatch(\n      innerRRect,\n      Skia.Rect.makeLtrb(\n        leftBorderWidth,\n        topBorderWidth,\n        outerWidth -. rightBorderWidth,\n        outerHeight -. bottomBorderWidth,\n      ),\n      max(borderRadius -. leftBorderWidth, 0.),\n      max(borderRadius -. topBorderWidth, 0.),\n      max(borderRadius -. rightBorderWidth, 0.),\n      max(borderRadius -. bottomBorderWidth, 0.),\n    );\n    innerRRect;\n  };\n};\n\nlet makeShadowImageFilter = boxShadow => {\n  let {blurRadius, xOffset, yOffset, color, _} = boxShadow;\n\n  // Per spec, sigma is exactly half the blur radius:\n  // https://www.w3.org/TR/css-backgrounds-3/#shadow-blur\n  // https://html.spec.whatwg.org/C/#when-shadows-are-drawn\n  let sigma = 0.5 *. blurRadius;\n  //print_endline(\"sigms:\" ++ string_of_float(sigma));\n\n  // TODO spreadRadius is unused - find out if this should be removed\n  Skia.ImageFilter.makeDropShadow(\n    xOffset,\n    yOffset,\n    sigma,\n    sigma,\n    Color.toSkia(color),\n    DrawShadowOnly,\n    None,\n    None,\n  );\n};\n\nclass viewNode (()) = {\n  as _this;\n  inherit (class node)() as _super;\n  val _fillPaint = {\n    let paint = Skia.Paint.make();\n    // Set antialias for fill paint, so that rounded edges for the inner rectangle look OK\n    Skia.Paint.setAntiAlias(paint, true);\n    paint;\n  };\n  val _outerRRect = Skia.RRect.make();\n  val _helperRect = Skia.Rect.makeLtrb(0., 0., 0., 0.);\n  pub! draw = (parentContext: NodeDrawContext.t) => {\n    let dimensions = _this#measurements();\n    let width = float_of_int(dimensions.width);\n    let height = float_of_int(dimensions.height);\n\n    let style = _super#getStyle();\n    let opacity = style.opacity *. parentContext.opacity;\n\n    let {canvas, _}: NodeDrawContext.t = parentContext;\n\n    let world = _this#getWorldTransform();\n    Revery_Draw.CanvasContext.setMatrix(canvas, world);\n\n    let borderRadius = style.borderRadius;\n    Skia.Rect.Mutable.setLtrb(~out=_helperRect, 0., 0., width, height);\n\n    let color = Color.multiplyAlpha(opacity, style.backgroundColor);\n    let colorAlpha = Color.getAlpha(color);\n\n    Skia.RRect.setRectXy(\n      _outerRRect,\n      _helperRect,\n      borderRadius,\n      borderRadius,\n    );\n\n    // Draw the shadow before anything else, so we don't clip the border\n    let isDrawingShadow = style.boxShadow.blurRadius > 0.1;\n\n    if (isDrawingShadow) {\n      let shadowImageFilter = makeShadowImageFilter(style.boxShadow);\n      Skia.Paint.setImageFilter(_fillPaint, Some(shadowImageFilter));\n\n      // This isn't perfect - we're redrawing the background color again, later,\n      // which might cause issues when the background color is non-opaque.\n      // The issue is, to use the `setImageFilter` stratey for rendering a shadow,\n      // we need to render _something_. We could consider instead drawing a blurred, offset\n      // rectangle underneath...\n      let shadowInteriorColor = Color.toSkia(color);\n      Skia.Paint.setColor(_fillPaint, shadowInteriorColor);\n\n      Revery_Draw.CanvasContext.drawRRect(canvas, _outerRRect, _fillPaint);\n\n      // Reset the image filter, as the `_fillPaint` is stateful - if we don't reset it,\n      // then if the style changes such that there should be no shadow drawn, `_fillPaint`\n      // will still have the draw-shadow image filter applied.\n      Skia.Paint.setImageFilter(_fillPaint, None);\n    };\n\n    let innerRRect =\n      renderBorders(~canvas, ~style, ~outerRRect=_outerRRect, ~opacity);\n\n    if (colorAlpha > 0.001) {\n      let skiaColor = Color.toSkia(color);\n      Skia.Paint.setColor(_fillPaint, skiaColor);\n\n      Revery_Draw.CanvasContext.drawRRect(canvas, innerRRect, _fillPaint);\n    };\n\n    _super#draw(parentContext);\n  };\n};\n"
  },
  {
    "path": "src/UI/dune",
    "content": "(library\n (name Revery_UI)\n (public_name Revery.UI)\n (preprocess\n  (pps lwt_ppx ppx_deriving.show))\n (libraries brisk-reconciler lwt lwt.unix sdl2 skia flex rebez.lib\n   Revery_Core Revery_Draw Revery_Math))\n"
  },
  {
    "path": "src/UI_Components/Button.re",
    "content": "open Revery_UI;\nopen Revery_Core;\nopen Revery_UI_Primitives;\nopen Revery_Font;\n\nlet noop = () => ();\n\nlet make =\n    (\n      ~title,\n      ~onClick=noop,\n      ~color as c=Colors.dodgerBlue,\n      ~fontSize=40.,\n      ~width as w=300,\n      ~height as h=100,\n      ~disabled=false,\n      ~tabindex=?,\n      ~onFocus=?,\n      ~onBlur=?,\n      ~fontFamily=Family.default,\n      (),\n    ) =>\n  <Clickable onClick={disabled ? noop : onClick} ?onFocus ?onBlur ?tabindex>\n    <View\n      style=Style.[\n        position(`Relative),\n        backgroundColor(disabled ? Colors.dimGrey : c),\n        justifyContent(`Center),\n        alignItems(`Center),\n        border(~width=1, ~color=Colors.white),\n        height(h),\n        width(w),\n      ]>\n      <Text\n        style=Style.[color(Colors.white)]\n        fontSize\n        fontFamily\n        text=title\n      />\n    </View>\n  </Clickable>;\n"
  },
  {
    "path": "src/UI_Components/Button.rei",
    "content": "/**\n{2  Description:}\n\nSimple out-of-box button component\n\n{2 Usage:}\n\n{[\n    <Button width=50 height=150 color=Colors.blue title=\"Ok\"/>\n]}\n*/\nlet make:\n  (\n    ~title: string,\n    ~onClick: unit => unit=?,\n    ~color: Revery_Core.Color.t=?,\n    ~fontSize: float=?,\n    ~width: int=?,\n    ~height: int=?,\n    ~disabled: bool=?,\n    ~tabindex: int=?,\n    ~onFocus: Revery_UI.NodeEvents.focusHandler=?,\n    ~onBlur: Revery_UI.NodeEvents.focusHandler=?,\n    ~fontFamily: Revery_Font.Family.t=?,\n    unit\n  ) =>\n  Brisk_reconciler.element(Revery_UI.React.node);\n"
  },
  {
    "path": "src/UI_Components/Center.re",
    "content": "open Revery_UI;\nopen Revery_UI_Primitives;\n\nlet make = (~children=React.empty, ()) => {\n  let style =\n    Style.[flexGrow(1), justifyContent(`Center), alignItems(`Center)];\n\n  <View style> children </View>;\n};\n"
  },
  {
    "path": "src/UI_Components/Checkbox.re",
    "content": "open Revery_UI;\nopen Revery_Core;\nopen Revery_UI_Primitives;\n\nlet defaultStyle =\n  Style.[\n    width(50),\n    height(50),\n    border(~width=5, ~color=Colors.dodgerBlue),\n  ];\n\nlet noop = _c => ();\n\nlet make =\n    (\n      ~checked=false,\n      ~checkedColor=Colors.dodgerBlue,\n      ~style=defaultStyle,\n      ~onChange=noop,\n      (),\n    ) => {\n  let stylesToUse = Style.merge(~source=defaultStyle, ~target=style);\n  let bgColor = checked ? checkedColor : Colors.transparentWhite;\n  let checkedContent = checked ? {||} : \"\";\n\n  <Clickable onClick=onChange>\n    <View\n      style=Style.(\n        merge(\n          ~source=[\n            backgroundColor(bgColor),\n            justifyContent(`Center),\n            alignItems(`Center),\n          ],\n          ~target=stylesToUse,\n        )\n      )>\n      <Text\n        text=checkedContent\n        fontSize=30.\n        fontFamily={Revery_Font.Family.fromFile(\"FontAwesome5FreeSolid.otf\")}\n        style=Style.[\n          color(checked ? Colors.white : Colors.black),\n          textWrap(TextWrapping.NoWrap),\n        ]\n      />\n    </View>\n  </Clickable>;\n};\n"
  },
  {
    "path": "src/UI_Components/Checkbox.rei",
    "content": "/**\n{2 Description:}\n\nSimple out-of-box checkbox component\n\n{2 Usage:}\n\n{[\n    <Checkbox checked=true onChange={(_) => print_endline(\"Checkbox changed!\")}/>\n]}\n*/\n\nlet make:\n  (\n    ~checked: bool=?,\n    ~checkedColor: Revery_Core.Color.t=?,\n    ~style: list(Revery_UI.Style.viewStyleProps)=?,\n    ~onChange: unit => unit=?,\n    unit\n  ) =>\n  Brisk_reconciler.element(Revery_UI.React.node);\n"
  },
  {
    "path": "src/UI_Components/Clickable.re",
    "content": "/*\n * Clickable.re\n *\n * This module provides a `<Clickable />` component, loosely inspired by the\n * `<Touchable />` component in React-Native, but geared towards the desktop.\n */\n\nopen Revery_UI;\nopen Revery_Core;\nopen Revery_UI_Primitives;\n\nmodule Hooks = Revery_UI_Hooks;\nmodule Log = (val Log.withNamespace(\"Revery.Components.Clickable\"));\n\nmodule Constants = {\n  let initialMouseDownTimes = (Time.ms(1000), Time.zero);\n  let doubleClickSpeed = Time.ms(500);\n};\n\nlet isMouseCaptured = ref(false);\n\nlet%component make =\n              (\n                ~style=[],\n                ~onClick=() => (),\n                ~onRightClick=() => (),\n                ~onDoubleClick=?,\n                ~onAnyClick=_event => (),\n                ~componentRef=?,\n                ~onBoundingBoxChanged=?,\n                ~onBlur=?,\n                ~onFocus=?,\n                ~tabindex=0,\n                ~onKeyDown=?,\n                ~onKeyUp=?,\n                ~onTextEdit=?,\n                ~onTextInput=?,\n                ~onMouseEnter as onMouseEnterUserCallback=?,\n                ~onMouseLeave as onMouseLeaveUserCallback=_evt => (),\n                ~children,\n                (),\n              ) => {\n  let%hook isMouseCapturedHere = Hooks.ref(false);\n\n  let%hook mouseDownTimes = Hooks.ref(Constants.initialMouseDownTimes);\n\n  let isDoubleClick = () =>\n    Time.(fst(mouseDownTimes^) - snd(mouseDownTimes^))\n    <= Constants.doubleClickSpeed;\n  let resetMouseDownTimes = () =>\n    mouseDownTimes := Constants.initialMouseDownTimes;\n\n  let capture = () =>\n    if (! isMouseCaptured^) {\n      Log.trace(\"Capture\");\n      isMouseCapturedHere := true;\n      isMouseCaptured := true;\n    };\n  let releaseCapture = () =>\n    if (isMouseCapturedHere^) {\n      Log.trace(\"Release\");\n      isMouseCapturedHere := false;\n      isMouseCaptured := false;\n    };\n\n  let onMouseDown = _event => {\n    capture();\n    mouseDownTimes := (Time.now(), fst(mouseDownTimes^));\n  };\n  let onMouseLeave = _event => {\n    releaseCapture();\n    onMouseLeaveUserCallback(_event);\n  };\n  let onMouseUp = (mouseEvt: NodeEvents.mouseButtonEventParams) =>\n    if (isMouseCapturedHere^) {\n      releaseCapture();\n\n      switch (mouseEvt.button) {\n      | MouseButton.BUTTON_LEFT =>\n        if (onDoubleClick != None && isDoubleClick()) {\n          resetMouseDownTimes();\n          Option.get(onDoubleClick, ());\n        } else {\n          onClick();\n        }\n      | MouseButton.BUTTON_RIGHT => onRightClick()\n      | _ => ()\n      };\n\n      onAnyClick(mouseEvt);\n    };\n  let%hook () = Hooks.effect(OnMount, () => Some(releaseCapture));\n\n  let style = Style.[cursor(MouseCursors.pointer), ...style];\n\n  <View\n    style\n    onMouseDown\n    onMouseUp\n    onMouseLeave\n    onMouseEnter=?onMouseEnterUserCallback\n    ?onBoundingBoxChanged\n    ?onBlur\n    ?onFocus\n    ?onKeyDown\n    ?onKeyUp\n    ?onTextEdit\n    ?onTextInput\n    tabindex={Some(tabindex)}\n    ref=?componentRef>\n    children\n  </View>;\n};\n"
  },
  {
    "path": "src/UI_Components/Clickable.rei",
    "content": "open Revery_UI;\n\n/**\n{2 Description:}\n\nThis module provides a `<Clickable />` component, loosely inspired by the\n`<Touchable />` component in React-Native, but geared towards the desktop.\n\n{2 Usage:}\n\n{[\n<Clickable onClick={(_) => print_endline(\"Clicked!\")}>\n  <Container width=100 height=100 />\n</Clickable>\n]}\n */\n\nlet make:\n  (\n    ~key: Brisk_reconciler.Key.t=?,\n    ~style: list(Revery_UI.Style.viewStyleProps)=?,\n    ~onClick: unit => unit=?,\n    ~onRightClick: unit => unit=?,\n    ~onDoubleClick: unit => unit=?,\n    ~onAnyClick: NodeEvents.mouseButtonEventParams => unit=?,\n    ~componentRef: Revery_UI.node => unit=?,\n    ~onBoundingBoxChanged: Revery_Math.BoundingBox2d.t => unit=?,\n    ~onBlur: Revery_UI.NodeEvents.focusHandler=?,\n    ~onFocus: Revery_UI.NodeEvents.focusHandler=?,\n    ~tabindex: int=?,\n    ~onKeyDown: Revery_UI.NodeEvents.keyDownHandler=?,\n    ~onKeyUp: Revery_UI.NodeEvents.keyUpHandler=?,\n    ~onTextEdit: Revery_UI.NodeEvents.textEditHandler=?,\n    ~onTextInput: Revery_UI.NodeEvents.textInputHandler=?,\n    ~onMouseEnter: NodeEvents.mouseMoveEventParams => unit=?,\n    ~onMouseLeave: NodeEvents.mouseMoveEventParams => unit=?,\n    ~children: Brisk_reconciler.element(Revery_UI.React.node),\n    unit\n  ) =>\n  Brisk_reconciler.element(Revery_UI.React.node);\n"
  },
  {
    "path": "src/UI_Components/ClickableText.re",
    "content": "open Revery_UI;\nopen Revery_Core;\nopen Revery_UI_Primitives;\nopen Revery_Font;\n\nmodule Hooks = Revery_UI_Hooks;\n\nlet noop = () => ();\n\nlet%component make =\n              (\n                ~text,\n                ~inactiveStyle,\n                ~activeStyle,\n                ~fontSize=14.,\n                ~fontFamily=Family.default,\n                ~fontWeight=Weight.Normal,\n                ~italic=false,\n                ~underlined=false,\n                ~onClick=noop,\n                (),\n              ) => {\n  let%hook (isHovered, onMouseEnter, onMouseLeave) = Hooks.hover();\n\n  let outerStyle = Style.[cursor(MouseCursors.pointer)];\n\n  <Clickable style=outerStyle onClick onMouseEnter onMouseLeave>\n    <Text\n      text\n      fontFamily\n      fontSize\n      fontWeight\n      italic\n      style={isHovered ? activeStyle : inactiveStyle}\n      underlined\n    />\n  </Clickable>;\n};\n"
  },
  {
    "path": "src/UI_Components/ClipContainer.re",
    "content": "open Revery_Core;\nopen Revery_UI;\nopen Revery_UI_Primitives;\n\nlet make =\n    (~children, ~color=Colors.transparentWhite, ~width as w, ~height as h, ()) => {\n  let c = color;\n  let style =\n    Style.[width(w), height(h), overflow(`Hidden), backgroundColor(c)];\n\n  <View style> ...children </View>;\n};\n"
  },
  {
    "path": "src/UI_Components/ClipContainer.rei",
    "content": "/**\n{2 Description:}\n\n[ClipContainer] is similar to a container, except that it clips\nany items outside.\n\n{2 Usage:}\n\n{[\n<ClipContainer width=50 height=50>\n  <Container width=100 height=100 color=Colors.green />\n</ClipContainer\n]}\n\n@param [width] The width of the container, in pixels.\n@param [height] The height of the container, in pixels.\n@param [color] The color of the container.\n*/\nlet make:\n  (\n    ~children: Revery_UI.element,\n    ~color: Revery_Core.Color.t=?,\n    ~width: int,\n    ~height: int,\n    unit\n  ) =>\n  Brisk_reconciler.element(Revery_UI.viewNode);\n"
  },
  {
    "path": "src/UI_Components/Column.re",
    "content": "open Revery_UI;\nopen Revery_UI_Primitives;\n\nlet make = (~children, ()) => {\n  let style =\n    Style.[\n      flexDirection(`Column),\n      alignItems(`Stretch),\n      justifyContent(`Center),\n    ];\n\n  <View style> ...children </View>;\n};\n"
  },
  {
    "path": "src/UI_Components/Container.re",
    "content": "open Revery_Core;\nopen Revery_UI;\nopen Revery_UI_Primitives;\n\nlet make =\n    (\n      ~children=React.empty,\n      ~color=Colors.transparentWhite,\n      ~width as w,\n      ~height as h,\n      (),\n    ) => {\n  let c = color;\n  let style = Style.[width(w), height(h), backgroundColor(c)];\n\n  <View style> children </View>;\n};\n"
  },
  {
    "path": "src/UI_Components/Container.rei",
    "content": "/**\nDescription:\n\nBasic component to act as a container for multiple child components.\n\nUsage:\n\n{[\n    <Container width=50 height=50 color=Colors.red />\n]}\n\n@param [width] The width of the container, in pixels.\n@param [height] The height of the container, in pixels.\n@param [color] The color of the container.\n*/\nlet make:\n  (\n    ~children: Revery_UI.element=?,\n    ~color: Revery_Core.Color.t=?,\n    ~width: int,\n    ~height: int,\n    unit\n  ) =>\n  Brisk_reconciler.element(Revery_UI.viewNode);\n"
  },
  {
    "path": "src/UI_Components/Dropdown.re",
    "content": "open Revery_UI;\nopen Revery_Core;\nopen Revery_UI_Primitives;\n\nmodule Hooks = Revery_UI_Hooks;\nmodule Make = (Type: {type t;}) => {\n  type item = {\n    value: Type.t,\n    label: string,\n  };\n\n  type items = list(item);\n\n  type state = {\n    items,\n    selected: item,\n    _open: bool,\n  };\n\n  type action =\n    | ShowDropdown\n    | SelectItem(item);\n\n  let reducer = (action, state) =>\n    switch (action) {\n    | ShowDropdown => {...state, _open: !state._open}\n    | SelectItem(item) => {...state, selected: item, _open: false}\n    };\n\n  let textStyles = Style.[color(Colors.black)];\n\n  let noop = _item => ();\n\n  let%component make =\n                (\n                  ~items: list(item),\n                  ~onItemSelected,\n                  ~width as w=200,\n                  ~height as h=50,\n                  (),\n                ) => {\n    let initialState = {items, selected: List.nth(items, 0), _open: false};\n\n    let%hook (state, dispatch) = Hooks.reducer(~initialState, reducer);\n\n    let items =\n      state._open\n        ? List.map(\n            item =>\n              <Clickable\n                style=Style.[\n                  height(h),\n                  justifyContent(`Center),\n                  alignItems(`FlexStart),\n                  paddingHorizontal(5),\n                  backgroundColor(\n                    item == state.selected\n                      ? Color.hex(\"#0078D7\") : Colors.transparentWhite,\n                  ),\n                  borderBottom(\n                    ~color=Colors.black,\n                    ~width=float_of_int(h) *. 0.05 |> int_of_float,\n                  ),\n                ]\n                onClick={() => {\n                  dispatch(SelectItem(item));\n                  onItemSelected(item);\n                }}>\n                <Text style=textStyles fontSize=20. text={item.label} />\n              </Clickable>,\n            state.items,\n          )\n        : [];\n\n    <View\n      style=Style.[\n        position(`Relative),\n        backgroundColor(Colors.white),\n        width(w),\n      ]>\n      <Clickable onClick={() => dispatch(ShowDropdown)}>\n        <View\n          style=Style.[\n            flexDirection(`Row),\n            height(h),\n            justifyContent(`SpaceBetween),\n            paddingHorizontal(5),\n            border(\n              ~width=float_of_int(h) *. 0.05 |> int_of_float,\n              ~color=Colors.black,\n            ),\n          ]>\n          <View\n            style=Style.[\n              width(float_of_int(w) *. 0.8 |> int_of_float),\n              justifyContent(`Center),\n              overflow(`Hidden),\n            ]>\n            <Text style=textStyles fontSize=20. text={state.selected.label} />\n          </View>\n          <Text\n            style=Style.[\n              color(Colors.black),\n              paddingRight(5),\n              alignSelf(`Center),\n            ]\n            fontSize=30.\n            fontFamily={Revery_Font.Family.fromFile(\n              \"FontAwesome5FreeSolid.otf\",\n            )}\n            text={||}\n          />\n        </View>\n      </Clickable>\n      <View\n        style=Style.[\n          position(`Absolute),\n          top(h),\n          backgroundColor(Colors.white),\n          borderHorizontal(\n            ~width=float_of_int(h) *. 0.05 |> int_of_float,\n            ~color=Colors.black,\n          ),\n          overflow(`Hidden),\n        ]>\n        {items |> React.listToElement}\n      </View>\n    </View>;\n  };\n};\n"
  },
  {
    "path": "src/UI_Components/ExpandContainer.re",
    "content": "open Revery_Core;\nopen Revery_UI;\nopen Revery_UI_Primitives;\n\nlet make = (~children, ~color=Colors.transparentWhite, ()) => {\n  let c = color;\n  let style = Style.[flexGrow(1), backgroundColor(c)];\n\n  <View style> ...children </View>;\n};\n"
  },
  {
    "path": "src/UI_Components/Input.re",
    "content": "module ContainerComponent = Container;\nopen Revery_UI;\nopen Revery_Core;\nopen Revery_UI_Primitives;\nopen Revery_Font;\n\nmodule Hooks = Revery_UI_Hooks;\n\nmodule Cursor = {\n  type state = {\n    time: Time.t,\n    isOn: bool,\n  };\n\n  type action =\n    | Reset\n    | Tick(Time.t);\n\n  let use = (~interval, ~isFocused) => {\n    let%hook (state, dispatch) =\n      Hooks.reducer(\n        ~initialState={time: Time.zero, isOn: false}, (action, state) => {\n        switch (action) {\n        | Reset => {isOn: true, time: Time.zero}\n        | Tick(increasedTime) =>\n          let newTime = Time.(state.time + increasedTime);\n\n          /* if newTime is above the interval a `Tick` has passed */\n          newTime >= interval\n            ? {isOn: !state.isOn, time: Time.zero}\n            : {...state, time: newTime};\n        }\n      });\n\n    let%hook () =\n      Hooks.effect(\n        OnMount,\n        () => {\n          let clear =\n            Tick.interval(\n              ~name=\"Revery:Input:Cursor Blink Interval\",\n              time => dispatch(Tick(time)),\n              Time.ms(16),\n            );\n          Some(clear);\n        },\n      );\n\n    let cursorOpacity = isFocused && state.isOn ? 1.0 : 0.0;\n\n    (cursorOpacity, () => dispatch(Reset));\n  };\n};\n\ntype state = {\n  value: string,\n  cursorPosition: int,\n};\n\ntype action =\n  | TextInput(string, int);\n\nlet reducer = (action, _state) =>\n  switch (action) {\n  | TextInput(value, cursorPosition) => {value, cursorPosition}\n  };\n\nlet getStringParts = (index, str) => {\n  switch (index) {\n  | 0 => (\"\", str)\n  | _ =>\n    let strBeginning =\n      try(Str.string_before(str, index)) {\n      | _ => str\n      };\n    let strEnd =\n      try(Str.string_after(str, index)) {\n      | _ => \"\"\n      };\n    (strBeginning, strEnd);\n  };\n};\n\nlet getSafeStringBounds = (str, cursorPosition, change) => {\n  let nextPosition = cursorPosition + change;\n  let currentLength = String.length(str);\n  nextPosition > currentLength\n    ? currentLength : nextPosition < 0 ? 0 : nextPosition;\n};\n\nlet removeCharacterBefore = (word, cursorPosition) => {\n  let (startStr, endStr) = getStringParts(cursorPosition, word);\n  let nextPosition = getSafeStringBounds(startStr, cursorPosition, -1);\n  let newString = Str.string_before(startStr, nextPosition) ++ endStr;\n  (newString, nextPosition);\n};\n\nlet removeCharacterAfter = (word, cursorPosition) => {\n  let (startStr, endStr) = getStringParts(cursorPosition, word);\n  let newString =\n    startStr\n    ++ (\n      switch (endStr) {\n      | \"\" => \"\"\n      | _ => Str.last_chars(endStr, String.length(endStr) - 1)\n      }\n    );\n  (newString, cursorPosition);\n};\n\nlet insertString = (currentValue, insertion, index) => {\n  let (startStr, endStr) = getStringParts(index, currentValue);\n  (\n    startStr ++ insertion ++ endStr,\n    String.length(startStr) + String.length(insertion),\n  );\n};\n\nmodule Constants = {\n  let defaultHeight = 50;\n  let defaultWidth = 200;\n  let textMargin = 10;\n  let cursorWidth = 2;\n};\n\nmodule Styles = {\n  open Style;\n\n  let defaultPlaceholderColor = Colors.grey;\n  let defaultCursorColor = Colors.black;\n\n  let default = [\n    color(Colors.black),\n    width(Constants.defaultWidth),\n    height(Constants.defaultHeight),\n    border(\n      // The default border width should be 5% of the full input height\n      ~width=float_of_int(Constants.defaultHeight) *. 0.05 |> int_of_float,\n      ~color=Colors.black,\n    ),\n    backgroundColor(Colors.transparentWhite),\n  ];\n\n  let _all = (~style) =>\n    merge(\n      ~source=[\n        flexDirection(`Row),\n        alignItems(`Center),\n        justifyContent(`FlexStart),\n        overflow(`Hidden),\n        cursor(MouseCursors.text),\n        ...default,\n      ],\n      ~target=style,\n    );\n\n  let box = (~style) => extractViewStyles(_all(~style));\n\n  let marginContainer = [\n    flexDirection(`Row),\n    alignItems(`Center),\n    justifyContent(`FlexStart),\n    marginLeft(Constants.textMargin),\n    marginRight(Constants.textMargin),\n    flexGrow(1),\n  ];\n\n  let cursor = (~offset) => [\n    position(`Absolute),\n    marginTop(2),\n    transform(Transform.[TranslateX(float(offset))]),\n  ];\n\n  let textContainer = [flexGrow(1), overflow(`Hidden)];\n\n  let text =\n      (~showPlaceholder, ~scrollOffset, ~placeholderColor, ~color: Color.t) => [\n    Style.color(showPlaceholder ? placeholderColor : color),\n    alignItems(`Center),\n    justifyContent(`FlexStart),\n    textWrap(TextWrapping.NoWrap),\n    transform(Transform.[TranslateX(float(- scrollOffset^))]),\n  ];\n};\n\nlet%component make =\n              (\n                ~style=Styles.default,\n                ~fontFamily=Family.default,\n                ~fontWeight=Weight.Normal,\n                ~italic=false,\n                ~fontSize=14.0,\n                ~underlined=false,\n                ~placeholderColor=Styles.defaultPlaceholderColor,\n                ~cursorColor=Styles.defaultCursorColor,\n                ~autofocus=false,\n                ~placeholder=\"\",\n                ~onFocus=() => (),\n                ~onBlur=() => (),\n                ~onKeyDown=_ => (),\n                ~onChange=(_, _) => (),\n                ~value=?,\n                ~cursorPosition=?,\n                ~isPassword=false,\n                (),\n              ) => {\n  // TODO: This ought to be a state hook not reducer but\n  // a bug with stale hook setters might happen.\n  //\n  // Refactor when https://github.com/briskml/brisk-reconciler/issues/74 has been fixed\n  let%hook (state, dispatch) =\n    Hooks.reducer(\n      ~initialState={\n        value: Option.value(value, ~default=\"\"),\n        cursorPosition: Option.value(cursorPosition, ~default=0),\n      },\n      reducer,\n    );\n  let%hook textRef = Hooks.ref(None);\n  let%hook scrollOffset = Hooks.ref(0);\n\n  let color =\n    Selector.select(style, Color, Some(Colors.black)) |> Option.get;\n\n  let value = Option.value(value, ~default=state.value);\n  let showPlaceholder = value == \"\";\n  let cursorPosition =\n    Option.value(cursorPosition, ~default=state.cursorPosition)\n    |> min(String.length(value));\n\n  // TODO: Expose as argument\n  let smoothing = Revery_Font.Smoothing.default;\n\n  let measureTextWidth = text => {\n    let dimensions =\n      Revery_Draw.Text.dimensions(\n        ~smoothing,\n        ~italic,\n        ~fontWeight,\n        ~fontFamily,\n        ~fontSize,\n        text,\n      );\n\n    dimensions.width |> int_of_float;\n  };\n\n  let%hook clickableRef = Hooks.ref(None);\n  let isFocused = () => {\n    switch (clickableRef^) {\n    | Some(node) => Focus.isFocused(node)\n    | None => false\n    };\n  };\n\n  let%hook (cursorOpacity, resetCursor) =\n    Cursor.use(~interval=Time.ms(500), ~isFocused=isFocused());\n\n  let () = {\n    let cursorOffset =\n      measureTextWidth(String.sub(value, 0, cursorPosition));\n\n    switch (Option.bind(textRef^, r => r#getParent())) {\n    | Some(containerNode) =>\n      let container: Dimensions.t = containerNode#measurements();\n\n      if (cursorOffset < scrollOffset^) {\n        // out of view to the left, so align with left edge\n        scrollOffset := cursorOffset;\n      } else if (cursorOffset - scrollOffset^ > container.width) {\n        // out of view to the right, so align with right edge\n        scrollOffset := cursorOffset - container.width;\n      };\n\n    | None => ()\n    };\n  };\n\n  let handleFocus = () => {\n    resetCursor();\n    onFocus();\n    Sdl2.TextInput.start();\n  };\n\n  let handleBlur = () => {\n    resetCursor();\n    onBlur();\n    Sdl2.TextInput.stop();\n  };\n\n  // TODO:This ought to be in the reducer, but since reducer calls are deferred\n  // the ordering of side-effects can't be guaranteed.\n  //\n  // Refactor when https://github.com/briskml/brisk-reconciler/issues/54 has been fixed\n  let update = (value, cursorPosition) => {\n    onChange(value, cursorPosition);\n    dispatch(TextInput(value, cursorPosition));\n  };\n\n  let paste = (currentValue, currentCursorPosition) => {\n    switch (Sdl2.Clipboard.getText()) {\n    | None => ()\n    | Some(data) =>\n      let (newValue, newCursorPosition) =\n        insertString(currentValue, data, currentCursorPosition);\n      update(newValue, newCursorPosition);\n    };\n  };\n\n  let handleTextInput = (event: NodeEvents.textInputEventParams) => {\n    resetCursor();\n    let (value, cursorPosition) =\n      insertString(value, event.text, cursorPosition);\n    update(value, cursorPosition);\n  };\n\n  let handleKeyDown = (event: NodeEvents.keyEventParams) => {\n    open Key;\n\n    resetCursor();\n    onKeyDown(event);\n\n    let code = event.keycode;\n    let super = Sdl2.Keymod.isGuiDown(event.keymod);\n    let ctrl = Sdl2.Keymod.isControlDown(event.keymod);\n\n    if (code == Keycode.left) {\n      let cursorPosition = getSafeStringBounds(value, cursorPosition, -1);\n      update(value, cursorPosition);\n    } else if (code == Keycode.right) {\n      let cursorPosition = getSafeStringBounds(value, cursorPosition, 1);\n      update(value, cursorPosition);\n    } else if (code == Keycode.delete) {\n      let (value, cursorPosition) =\n        removeCharacterAfter(value, cursorPosition);\n      update(value, cursorPosition);\n    } else if (code == Keycode.backspace) {\n      let (value, cursorPosition) =\n        removeCharacterBefore(value, cursorPosition);\n      update(value, cursorPosition);\n    } else if (code == Keycode.escape) {\n      Focus.loseFocus();\n    } else if (code == Keycode.v) {\n      if (Environment.isMac && super || !Environment.isMac && ctrl) {\n        paste(value, cursorPosition);\n      };\n    };\n  };\n\n  let handleClick = (event: NodeEvents.mouseButtonEventParams) => {\n    switch (textRef^) {\n    | Some(node) =>\n      let sceneOffsets: Offset.t = node#getSceneOffsets();\n      let textOffset =\n        int_of_float(event.mouseX) - sceneOffsets.left + scrollOffset^;\n      let cursorPosition =\n        Revery_Draw.Text.indexNearestOffset(\n          ~measure=measureTextWidth,\n          value,\n          textOffset,\n        );\n\n      resetCursor();\n      update(value, cursorPosition);\n\n    | None => ()\n    };\n  };\n\n  let handleRef = node => {\n    clickableRef := Some(node);\n    if (autofocus) {\n      Focus.focus(node);\n    };\n  };\n\n  let cursor = () => {\n    let (startStr, _) = getStringParts(cursorPosition, value);\n    let textWidth = measureTextWidth(startStr);\n\n    let offset = textWidth - scrollOffset^;\n\n    <View style={Styles.cursor(~offset)}>\n      <Opacity opacity=cursorOpacity>\n        <ContainerComponent\n          width=Constants.cursorWidth\n          height={fontSize |> int_of_float}\n          color=cursorColor\n        />\n      </Opacity>\n    </View>;\n  };\n\n  let text =\n    if (showPlaceholder) {\n      placeholder;\n    } else if (isPassword) {\n      String.map(_ => '*', value);\n    } else {\n      value;\n    };\n\n  let text = () =>\n    <Text\n      ref={node => textRef := Some(node)}\n      text\n      fontFamily\n      fontWeight\n      italic\n      fontSize\n      underlined\n      smoothing\n      style={Styles.text(\n        ~showPlaceholder,\n        ~scrollOffset,\n        ~placeholderColor,\n        ~color,\n      )}\n    />;\n\n  <Clickable\n    onFocus=handleFocus\n    onBlur=handleBlur\n    componentRef=handleRef\n    onAnyClick=handleClick\n    onKeyDown=handleKeyDown\n    onTextInput=handleTextInput>\n    <View style={Styles.box(~style)}>\n      <View style=Styles.marginContainer>\n        <View style=Styles.textContainer> <text /> </View>\n        <cursor />\n      </View>\n    </View>\n  </Clickable>;\n};\n"
  },
  {
    "path": "src/UI_Components/Link.re",
    "content": "open Revery_Native;\nopen Revery_Font;\n\nlet make =\n    (\n      ~text,\n      ~inactiveStyle,\n      ~activeStyle,\n      ~href,\n      ~fontSize=14.,\n      ~fontFamily=Family.default,\n      ~fontWeight=Weight.Normal,\n      ~italic=false,\n      ~underlined=true,\n      (),\n    ) => {\n  let onClick = _ => Shell.openURL(href) |> ignore;\n  <ClickableText\n    text\n    inactiveStyle\n    activeStyle\n    onClick\n    fontSize\n    fontFamily\n    fontWeight\n    italic\n    underlined\n  />;\n};\n"
  },
  {
    "path": "src/UI_Components/Markdown.re",
    "content": "open Revery_Core;\nopen Revery_UI;\nopen Revery_UI_Primitives;\nopen Revery_Font;\n\nmodule Hooks = Revery_UI_Hooks;\n\nopen Omd;\n\nmodule Log = (val Log.withNamespace(\"Revery.Components.Markdown\"));\n\nmodule StringSet = Set.Make(String);\n\n// Since we dont have rich text support, sometimes links get broken\n// up by style. This causes only part of them to hover/activate at\n// a time. We create our own link system to fix this, where all links\n// with a certain href are highlighted at a time.\ntype state = {hoveredLinks: StringSet.t};\n\ntype events =\n  | LinkHovered(string)\n  | LinkUnhovered(string);\n\nlet update = (event, state) =>\n  switch (event) {\n  | LinkHovered(href) => {\n      hoveredLinks: StringSet.add(href, state.hoveredLinks),\n    }\n  | LinkUnhovered(href) => {\n      hoveredLinks: StringSet.remove(href, state.hoveredLinks),\n    }\n  };\n\ntype style = {\n  paragraph: list(Style.textStyleProps),\n  activeLink: list(Style.textStyleProps),\n  inactiveLink: list(Style.textStyleProps),\n  inlineCode: list(Style.textStyleProps),\n  h1: list(Style.textStyleProps),\n  h2: list(Style.textStyleProps),\n  h3: list(Style.textStyleProps),\n  h4: list(Style.textStyleProps),\n  h5: list(Style.textStyleProps),\n  h6: list(Style.textStyleProps),\n  fontFamily: Family.t,\n  codeFontFamily: Family.t,\n  codeBlockFontSize: float,\n  baseFontSize: float,\n  codeBlock: list(Style.viewStyleProps),\n};\n\nmodule SyntaxHighlight = {\n  type highlight = {\n    byteIndex: int,\n    color: Color.t,\n    bold: bool,\n    italic: bool,\n  }\n  and t =\n    (~language: option(string), list(string)) => list(list(highlight));\n\n  let makeHighlight = (~byteIndex, ~color, ~bold, ~italic): highlight => {\n    byteIndex,\n    color,\n    bold,\n    italic,\n  };\n\n  let default: t =\n    (~language as _, lines) => {\n      List.init(List.length(lines), _ =>\n        [{byteIndex: 0, color: Colors.white, bold: false, italic: false}]\n      );\n    };\n};\n\nmodule Styles = {\n  open Style;\n\n  let inline = [\n    flexDirection(`Row),\n    flexWrap(`Wrap),\n    alignItems(`FlexStart),\n  ];\n  let container = [justifyContent(`FlexStart), alignItems(`FlexStart)];\n  let hardBreak = [height(6)];\n\n  module Blockquote = {\n    let container = [\n      flexDirection(`Row),\n      borderLeft(~width=4, ~color=Colors.grey),\n    ];\n    let contents = [\n      paddingLeft(8),\n      justifyContent(`FlexStart),\n      alignItems(`FlexStart),\n      flexGrow(1),\n    ];\n  };\n\n  module Code = {\n    let inlineContainer = [\n      backgroundColor(Color.rgba(0., 0., 0., 0.25)),\n      borderRadius(3.),\n      border(~width=3, ~color=Color.rgba(0., 0., 0., 0.25)),\n    ];\n\n    let defaultBlock = [\n      backgroundColor(Color.rgba(0., 0., 0., 0.25)),\n      border(~width=3, ~color=Color.rgba(0., 0., 0., 0.25)),\n      borderRadius(3.),\n      padding(2),\n      marginTop(4),\n      marginBottom(4),\n    ];\n  };\n\n  module List = {\n    let marker = [\n      marginRight(6),\n      textWrap(TextWrapping.WrapIgnoreWhitespace),\n    ];\n    let contents = [\n      justifyContent(`FlexStart),\n      alignItems(`FlexStart),\n      flexGrow(1),\n    ];\n  };\n\n  module ThematicBreak = {\n    let hr = [\n      flexGrow(1),\n      height(2),\n      marginTop(2),\n      marginBottom(2),\n      backgroundColor(Color.rgba(0., 0., 0., 0.25)),\n    ];\n  };\n};\n\ntype inlineAttrs =\n  | Italicized\n  | Bolded\n  | Monospaced;\n\ntype kind = [ | `Paragraph | `Heading(int) | `Link(string) | `InlineCode];\n\nlet selectStyleFromKind = (kind: kind, styles) =>\n  switch (kind) {\n  | `Heading(1) => styles.h1\n  | `Heading(2) => styles.h2\n  | `Heading(3) => styles.h3\n  | `Heading(4) => styles.h4\n  | `Heading(5) => styles.h5\n  | `Heading(6)\n  | `Heading(_) => styles.h6\n  | `InlineCode => styles.inlineCode\n  | _ => styles.paragraph\n  };\n\n// Sourced from: http://zuga.net/articles/html-heading-elements/\nlet fontSizeFromKind = (kind: kind, styles) =>\n  switch (kind) {\n  | `Heading(1) => styles.baseFontSize *. 2.\n  | `Heading(2) => styles.baseFontSize *. 1.5\n  | `Heading(3) => styles.baseFontSize *. 1.17\n  | `Heading(4) => styles.baseFontSize *. 1.\n  | `Heading(5) => styles.baseFontSize *. 0.83\n  | `Heading(6)\n  | `Heading(_) => styles.baseFontSize *. 0.67\n  | `InlineCode => styles.baseFontSize *. 0.75\n  | _ => styles.baseFontSize\n  };\n\ntype attrs = {\n  inline: list(inlineAttrs),\n  kind,\n};\n\nlet isBold = attrs => List.mem(Bolded, attrs.inline);\nlet isItalicized = attrs => List.mem(Italicized, attrs.inline);\nlet isMonospaced = attrs => List.mem(Monospaced, attrs.inline);\n\nlet generateText = (text, styles, attrs, dispatch, state) => {\n  let fontSize = fontSizeFromKind(attrs.kind, styles);\n  let fontWeight = {\n    isBold(attrs) ? Weight.Bold : Weight.Normal;\n  };\n\n  switch (attrs.kind) {\n  | `Link(href) =>\n    let onMouseEnter = _ => dispatch(LinkHovered(href));\n    let onMouseLeave = _ => dispatch(LinkUnhovered(href));\n    let onMouseUp = _ => Revery_Native.Shell.openURL(href) |> ignore;\n    <View onMouseEnter onMouseLeave onMouseUp>\n      <Text\n        text\n        style=Style.[\n          textWrap(TextWrapping.WrapIgnoreWhitespace),\n          ...{StringSet.mem(href, state.hoveredLinks)\n                ? styles.activeLink : styles.inactiveLink},\n        ]\n        fontSize\n        fontFamily={\n          isMonospaced(attrs) ? styles.codeFontFamily : styles.fontFamily\n        }\n        fontWeight\n        italic={isItalicized(attrs)}\n        underlined=true\n      />\n    </View>;\n  | `InlineCode =>\n    <View style=Styles.Code.inlineContainer>\n      <Text\n        text\n        fontSize\n        fontFamily={styles.codeFontFamily}\n        fontWeight\n        style=Style.[\n          textWrap(TextWrapping.WrapIgnoreWhitespace),\n          ...{selectStyleFromKind(attrs.kind, styles)},\n        ]\n        italic={isItalicized(attrs)}\n      />\n    </View>\n  | _ =>\n    <Text\n      text\n      fontSize\n      fontFamily={\n        isMonospaced(attrs) ? styles.codeFontFamily : styles.fontFamily\n      }\n      fontWeight\n      style=Style.[\n        textWrap(TextWrapping.WrapIgnoreWhitespace),\n        ...selectStyleFromKind(attrs.kind, styles),\n      ]\n      italic={isItalicized(attrs)}\n    />\n  };\n};\n\nlet rec generateInline' = (inline, styles, attrs, dispatch, state) => {\n  switch (inline) {\n  | Html(t)\n  | Text(t) => generateText(t, styles, attrs, dispatch, state)\n  | Emph(e) =>\n    generateInline'(\n      e.content,\n      styles,\n      switch (e.style) {\n      | Star => {...attrs, inline: [Bolded, ...attrs.inline]}\n      | Underscore => {...attrs, inline: [Italicized, ...attrs.inline]}\n      },\n      dispatch,\n      state,\n    )\n  | Soft_break => generateText(\" \", styles, attrs, dispatch, state)\n  | Hard_break => <View style=Styles.hardBreak />\n  | Ref(r) =>\n    generateInline'(\n      r.label,\n      styles,\n      {...attrs, kind: `Link(r.def.destination)},\n      dispatch,\n      state,\n    )\n  | Link(l) =>\n    generateInline'(\n      l.def.label,\n      styles,\n      {...attrs, kind: `Link(l.def.destination)},\n      dispatch,\n      state,\n    )\n  | Code(c) =>\n    generateText(\n      c.content,\n      styles,\n      {kind: `InlineCode, inline: [Monospaced, ...attrs.inline]},\n      dispatch,\n      state,\n    )\n  | Concat(c) =>\n    <View style=Styles.inline>\n      {c\n       |> List.map(il => generateInline'(il, styles, attrs, dispatch, state))\n       |> React.listToElement}\n    </View>\n  | _ => <View />\n  };\n}\nand generateInline = (inline, styles, attrs, dispatch, state) =>\n  <Row> {generateInline'(inline, styles, attrs, dispatch, state)} </Row>;\n\n/* Block level elements\n\n     These include things like code blocks, paragraphs, etc.\n   */\n\nlet thematicBreak =\n  <View style=Style.[flexDirection(`Row)]>\n    <View style=Styles.ThematicBreak.hr />\n  </View>;\n\nlet rec generateMarkdown' = (element, styles, highlighter, dispatch, state) =>\n  switch (element) {\n  | Paragraph(p) =>\n    generateInline(\n      p,\n      styles,\n      {inline: [], kind: `Paragraph},\n      dispatch,\n      state,\n    )\n  // We don't support HTML rendering as of right now, so we'll just render it\n  // as text\n  | Html_block(html) =>\n    generateInline(\n      Text(html),\n      styles,\n      {inline: [], kind: `Paragraph},\n      dispatch,\n      state,\n    )\n  | Blockquote(blocks) =>\n    generateBlockquote(blocks, styles, highlighter, dispatch, state)\n  | Heading(h) =>\n    generateInline(\n      h.text,\n      styles,\n      {inline: [Bolded], kind: `Heading(h.level)},\n      dispatch,\n      state,\n    )\n  | Code_block(cb) => generateCodeBlock(cb, styles, highlighter)\n  | List(blist) => generateList(blist, styles, highlighter, dispatch, state)\n  | Thematic_break => thematicBreak\n  | _ => <View />\n  }\nand generateList =\n    (\n      blist: Block_list.t(block(inline)),\n      styles,\n      highlighter,\n      dispatch,\n      state,\n    ) => {\n  <View>\n    {List.mapi(\n       (i, blocks) => {\n         let text =\n           switch (blist.kind) {\n           | Ordered(_, _) => string_of_int(i + 1) ++ \".\"\n           | Unordered(_) => \"•\"\n           };\n         <View style=Styles.inline>\n           <Text\n             text\n             style=Styles.List.marker\n             fontFamily={styles.fontFamily}\n           />\n           <View style=Styles.List.contents>\n             {List.map(\n                block =>\n                  generateMarkdown'(\n                    block,\n                    styles,\n                    highlighter,\n                    dispatch,\n                    state,\n                  ),\n                blocks,\n              )\n              |> React.listToElement}\n           </View>\n         </View>;\n       },\n       blist.blocks,\n     )\n     |> React.listToElement}\n  </View>;\n}\nand generateBlockquote = (blocks, styles, highlighter, dispatch, state) => {\n  <View style=Styles.Blockquote.container>\n    <View style=Styles.Blockquote.contents>\n      {List.map(\n         block =>\n           generateMarkdown'(block, styles, highlighter, dispatch, state),\n         blocks,\n       )\n       |> React.listToElement}\n    </View>\n  </View>;\n}\nand generateCodeBlock =\n    (codeBlock: Code_block.t, styles, highlighter: SyntaxHighlight.t) => {\n  // Not sure why this is an `option` because when there is no label, the\n  // Parser gives `Some(\"\")`. For our purposes it's easier to make that `None`\n  let label =\n    switch (codeBlock.label) {\n    | Some(\"\")\n    | None => None\n    | Some(_) as label => label\n    };\n\n  Log.tracef(m =>\n    m(\"Code block has label : %s\", Option.value(label, ~default=\"(none)\"))\n  );\n\n  <View style={styles.codeBlock}>\n    {switch (codeBlock.code) {\n     | Some(code) =>\n       let lines = String.split_on_char('\\n', code);\n       let highlights = highlighter(~language=label, lines);\n       List.map2(\n         (line, highlight) => {\n           <View style=Styles.inline>\n             {List.mapi(\n                (i, block: SyntaxHighlight.highlight) => {\n                  // We want to style this block up to the next one.\n                  // If there is a next block, stop at it's index\n                  // Otherwise, stop at the end of the string.\n                  let endIndex =\n                    switch (List.nth_opt(highlight, i + 1)) {\n                    | Some(blk: SyntaxHighlight.highlight) => blk.byteIndex\n                    | None => String.length(line)\n                    };\n                  let length = endIndex - block.byteIndex;\n                  let text = String.sub(line, block.byteIndex, length);\n                  <Text\n                    text\n                    style=Style.[\n                      textWrap(TextWrapping.WrapIgnoreWhitespace),\n                      color(block.color),\n                    ]\n                    fontFamily={styles.codeFontFamily}\n                    fontWeight={block.bold ? Weight.Bold : Weight.Normal}\n                    fontSize={styles.codeBlockFontSize}\n                  />;\n                },\n                highlight,\n              )\n              |> React.listToElement}\n           </View>\n         },\n         lines,\n         highlights,\n       )\n       |> React.listToElement;\n\n     | None => <View />\n     }}\n  </View>;\n};\n\nlet generateMarkdown = (mdText: string, styles, highlighter, dispatch, state) => {\n  let md = Omd.of_string(mdText);\n  Log.debugf(m => m(\"Parsed Markdown as: %s\", Omd.to_sexp(md)));\n  List.map(\n    elt => generateMarkdown'(elt, styles, highlighter, dispatch, state),\n    md,\n  )\n  |> React.listToElement;\n};\n\nlet%component make =\n              (\n                ~markdown as mdText=\"\",\n                ~fontFamily=Family.default,\n                ~codeFontFamily=Family.defaultMono,\n                ~baseFontSize=14.0,\n                ~codeBlockFontSize=baseFontSize,\n                ~paragraphStyle=Style.emptyTextStyle,\n                ~activeLinkStyle=Style.emptyTextStyle,\n                ~inactiveLinkStyle=Style.emptyTextStyle,\n                ~h1Style=Style.emptyTextStyle,\n                ~h2Style=Style.emptyTextStyle,\n                ~h3Style=Style.emptyTextStyle,\n                ~h4Style=Style.emptyTextStyle,\n                ~h5Style=Style.emptyTextStyle,\n                ~h6Style=Style.emptyTextStyle,\n                ~inlineCodeStyle=Style.emptyTextStyle,\n                ~codeBlockStyle=Styles.Code.defaultBlock,\n                ~syntaxHighlighter=SyntaxHighlight.default,\n                (),\n              ) => {\n  let%hook (state, setState) = Hooks.state({hoveredLinks: StringSet.empty});\n\n  let dispatch = event => setState(s => update(event, s));\n\n  <View style=Styles.container>\n    {generateMarkdown(\n       mdText,\n       {\n         paragraph: paragraphStyle,\n         activeLink: activeLinkStyle,\n         inactiveLink: inactiveLinkStyle,\n         inlineCode: inlineCodeStyle,\n         h1: h1Style,\n         h2: h2Style,\n         h3: h3Style,\n         h4: h4Style,\n         h5: h5Style,\n         h6: h6Style,\n         fontFamily,\n         codeFontFamily,\n         baseFontSize,\n         codeBlockFontSize,\n         codeBlock: codeBlockStyle,\n       },\n       syntaxHighlighter,\n       dispatch,\n       state,\n     )}\n  </View>;\n};\n"
  },
  {
    "path": "src/UI_Components/Markdown.rei",
    "content": "module SyntaxHighlight: {\n  type highlight;\n  type t =\n    (~language: option(string), list(string)) => list(list(highlight));\n\n  let default: t;\n\n  let makeHighlight:\n    (\n      ~byteIndex: int,\n      ~color: Revery_Core.Color.t,\n      ~bold: bool,\n      ~italic: bool\n    ) =>\n    highlight;\n};\n\nlet make:\n  (\n    ~key: Brisk_reconciler.Key.t=?,\n    ~markdown: string=?,\n    ~fontFamily: Revery_Font.Family.t=?,\n    ~codeFontFamily: Revery_Font.Family.t=?,\n    ~baseFontSize: float=?,\n    ~codeBlockFontSize: float=?,\n    ~paragraphStyle: list(Revery_UI.Style.textStyleProps)=?,\n    ~activeLinkStyle: list(Revery_UI.Style.textStyleProps)=?,\n    ~inactiveLinkStyle: list(Revery_UI.Style.textStyleProps)=?,\n    ~h1Style: list(Revery_UI.Style.textStyleProps)=?,\n    ~h2Style: list(Revery_UI.Style.textStyleProps)=?,\n    ~h3Style: list(Revery_UI.Style.textStyleProps)=?,\n    ~h4Style: list(Revery_UI.Style.textStyleProps)=?,\n    ~h5Style: list(Revery_UI.Style.textStyleProps)=?,\n    ~h6Style: list(Revery_UI.Style.textStyleProps)=?,\n    ~inlineCodeStyle: list(Revery_UI.Style.textStyleProps)=?,\n    ~codeBlockStyle: list(Revery_UI.Style.viewStyleProps)=?,\n    ~syntaxHighlighter: SyntaxHighlight.t=?,\n    unit\n  ) =>\n  Brisk_reconciler.element(Revery_UI.React.node);\n"
  },
  {
    "path": "src/UI_Components/Positioned.re",
    "content": "open Revery_UI;\nopen Revery_UI_Primitives;\n\nlet make = (~top=?, ~left=?, ~right=?, ~bottom=?, ~children, ()) => {\n  let style =\n    switch (top, left, right, bottom) {\n    | (Some(t), Some(l), _, _) =>\n      Style.[position(`Absolute), top(t), left(l)]\n    | (Some(t), _, Some(r), _) =>\n      Style.[position(`Absolute), top(t), right(r)]\n    | (_, _, Some(r), Some(b)) =>\n      Style.[position(`Absolute), bottom(b), right(r)]\n    | (_, Some(l), _, Some(b)) =>\n      Style.[position(`Absolute), bottom(b), left(l)]\n    | _ => []\n    };\n\n  <View style> ...children </View>;\n};\n"
  },
  {
    "path": "src/UI_Components/Positioned.rei",
    "content": "let make:\n  (\n    ~top: int=?,\n    ~left: int=?,\n    ~right: int=?,\n    ~bottom: int=?,\n    ~children: Revery_UI.element,\n    unit\n  ) =>\n  Brisk_reconciler.element(Revery_UI.viewNode);\n"
  },
  {
    "path": "src/UI_Components/RadioButtons.re",
    "content": "open Revery_UI;\nopen Revery_Core;\nopen Revery_UI_Primitives;\n\nmodule Make = (Type: {type t;}) => {\n  type button = {\n    text: string,\n    value: Type.t,\n  };\n\n  let defaultStyle =\n    Style.[width(100), color(Colors.white), marginLeft(10)];\n\n  let%component make =\n                (\n                  ~defaultSelected,\n                  ~buttons: list(button),\n                  ~iconSize=12.,\n                  ~style=defaultStyle,\n                  ~onChange=_ => (),\n                  (),\n                ) => {\n    let defaultVal = List.nth(buttons, defaultSelected).value;\n    let%hook (checkedVal, setCheckedVal) = React.Hooks.state(defaultVal);\n\n    let icon = v => v == checkedVal ? {||} : {||};\n\n    let buttons =\n      buttons\n      |> List.map(button =>\n           <Clickable\n             onClick={() => {\n               setCheckedVal(_ => button.value);\n               onChange(button.value);\n             }}\n             style=Style.[\n               justifyContent(`Center),\n               flexDirection(`Row),\n               alignItems(`Center),\n               height(30),\n             ]>\n             <Text\n               text={icon(button.value)}\n               fontSize=iconSize\n               fontFamily={Revery_Font.Family.fromFile(\n                 \"FontAwesome5FreeSolid.otf\",\n               )}\n               style=Style.[textWrap(TextWrapping.NoWrap)]\n             />\n             <Text text={button.text} style fontSize=16. />\n           </Clickable>\n         );\n\n    <View style=Style.[justifyContent(`Center), alignItems(`Center)]>\n      {buttons |> React.listToElement}\n    </View>;\n  };\n};\n"
  },
  {
    "path": "src/UI_Components/Revery_UI_Components.re",
    "content": "/*\n * Revery_UI_Components.re\n *\n * Top-level API exposed for Revery.UI.Components\n */\n\nmodule Button = Button;\nmodule Center = Center;\nmodule Checkbox = Checkbox;\nmodule Clickable = Clickable;\nmodule ClipContainer = ClipContainer;\nmodule Column = Column;\nmodule Container = Container;\nmodule Dropdown = Dropdown;\nmodule DropdownInt =\n  Dropdown.Make({\n    type t = int;\n  });\nmodule DropdownString =\n  Dropdown.Make({\n    type t = string;\n  });\nmodule ExpandContainer = ExpandContainer;\nmodule Input = Input;\nmodule Link = Link;\nmodule Positioned = Positioned;\nmodule Row = Row;\nmodule ScrollView = ScrollView;\nmodule Slider = Slider;\nmodule Stack = Stack;\nmodule RadioButtons = RadioButtons;\nmodule RadioButtonsInt =\n  RadioButtons.Make({\n    type t = int;\n  });\nmodule RadioButtonsString =\n  RadioButtons.Make({\n    type t = string;\n  });\nmodule SVG = SVG;\nmodule ClickableText = ClickableText;\nmodule Ticker = Ticker;\nmodule Tree = Tree;\nmodule RichTextView = RichTextView;\nmodule Markdown = Markdown;\n"
  },
  {
    "path": "src/UI_Components/RichTextView.re",
    "content": "open Revery_UI;\nopen Revery_UI_Primitives;\nopen Revery_Font;\n\nlet make =\n    (\n      ~style=[],\n      ~smoothing=Smoothing.default,\n      ~richtext: RichText.t,\n      ~textWrap as textWrapping: Revery_Core.TextWrapping.wrapType=Wrap,\n      (),\n    ) => {\n  let text =\n    RichText.foldRight(\n      (acc, {italic, fontFamily, fontSize, fontWeight, text, color}) =>\n        [\n          <Text\n            style=[Style.color(color), Style.textWrap(textWrapping)]\n            fontFamily\n            fontWeight\n            italic\n            fontSize\n            text\n            smoothing\n          />,\n          ...acc,\n        ],\n      [],\n      richtext,\n    );\n\n  // TODO: Add alignItems(`Baseline) if that exists\n  <View style=Style.[flexDirection(`Row), ...style]>\n    {text |> React.listToElement}\n  </View>;\n};\n"
  },
  {
    "path": "src/UI_Components/Row.re",
    "content": "open Revery_UI;\nopen Revery_UI_Primitives;\n\nlet make = (~children=React.empty, ()) => {\n  let style =\n    Style.[\n      flexDirection(`Row),\n      alignItems(`Stretch),\n      justifyContent(`Center),\n    ];\n\n  <View style> children </View>;\n};\n"
  },
  {
    "path": "src/UI_Components/SVG.re",
    "content": "open Revery_UI;\nopen Revery_UI_Primitives;\nopen Revery_Draw;\nopen Skia;\n\nmodule Defaults = {\n  let svgWidth = 300.;\n  let svgHeight = 150.;\n};\nlet make =\n    (\n      ~src: [ | `Str(string) | `File(string)],\n      ~scaleMode: [ | `Fit | `Fill]=`Fit,\n      ~width as maybeWidth: option(float)=?,\n      ~height as maybeHeight: option(float)=?,\n      (),\n    ) => {\n  let maybeStream =\n    switch (src) {\n    | `Str(svgStr) =>\n      Some(Stream.makeMemoryStreamFromString(svgStr, String.length(svgStr)))\n    | `File(filePath) => Stream.makeFileStream(filePath)\n    };\n  let maybeSVG =\n    Option.bind(maybeStream, stream => SVG.makeFromStream(stream));\n\n  switch (maybeSVG) {\n  | Some(svg) =>\n    let intrinsicWidth = SVG.getContainerWidth(svg);\n    let intrinsicHeight = SVG.getContainerHeight(svg);\n\n    let adjustedWidth =\n      if (intrinsicWidth != 0.) {\n        intrinsicWidth;\n      } else {\n        Defaults.svgWidth;\n      };\n    let adjustedHeight =\n      if (intrinsicHeight != 0.) {\n        intrinsicHeight;\n      } else {\n        Defaults.svgHeight;\n      };\n\n    let canvasWidth = Option.value(maybeWidth, ~default=adjustedWidth);\n    let canvasHeight = Option.value(maybeHeight, ~default=adjustedHeight);\n\n    let (xScale, yScale) =\n      switch (maybeWidth, maybeHeight, scaleMode) {\n      | (Some(width), Some(height), `Fill)\n          when intrinsicWidth != 0. && intrinsicHeight != 0. => (\n          width /. intrinsicWidth,\n          height /. intrinsicHeight,\n        )\n      | (Some(width), Some(height), `Fit)\n          when intrinsicWidth != 0. && intrinsicHeight != 0. =>\n        let scale =\n          if (width < height) {\n            width /. intrinsicWidth;\n          } else {\n            height /. intrinsicHeight;\n          };\n        (scale, scale);\n      | _ => (1., 1.)\n      };\n\n    if (intrinsicHeight == 0. && intrinsicWidth == 0.) {\n      SVG.setContainerSize(svg, canvasWidth, canvasHeight);\n    };\n\n    let canvasStyle =\n      Style.[\n        width(canvasWidth +. 0.5 |> int_of_float),\n        height(canvasHeight +. 0.5 |> int_of_float),\n      ];\n\n    <Revery_UI_Primitives.Canvas\n      style=canvasStyle\n      render={(canvasContext, _dimensions) => {\n        let transform = Matrix.make();\n        Matrix.setIdentity(transform);\n        Matrix.setScaleX(transform, xScale);\n        Matrix.setScaleY(transform, yScale);\n        CanvasContext.concat(transform, canvasContext);\n\n        SVG.render(svg, canvasContext.canvas);\n      }}\n    />;\n  | None => <View />\n  };\n};\n"
  },
  {
    "path": "src/UI_Components/ScrollView.re",
    "content": "open Revery_Core;\nopen Revery_UI;\nopen Revery_UI.Transform;\nopen Revery_UI_Primitives;\n\nmodule Hooks = Revery_UI_Hooks;\n\ntype bouncingState =\n  | Bouncing(int)\n  | Idle;\n\nlet empty = React.empty;\n\nlet scrollTrackColor = Color.rgba(0.0, 0.0, 0.0, 0.4);\nlet scrollThumbColor = Color.rgba(0.5, 0.5, 0.5, 0.4);\n\ntype action =\n  | ScrollUpdated(int);\n\nlet reducer = (action, _state) => {\n  switch (action) {\n  | ScrollUpdated(v) => v\n  };\n};\n\nlet bounceAnimation = (~origin, ~force) =>\n  Animation.(\n    {\n      let bounceAway =\n        animate(Time.ms(100))\n        |> ease(Easing.cubicBezier(0.23, 1., 0.32, 1.))\n        |> tween(float(origin), float(origin + force));\n\n      let bounceBack =\n        Animation.animate(Time.ms(800))\n        |> ease(Easing.cubicBezier(0.23, 1., 0.32, 1.))\n        |> tween(float(origin + force), float(origin));\n\n      bounceAway |> andThen(~next=bounceBack) |> map(int_of_float);\n    }\n  );\n\nlet bounceAnimationHook = (scrollPosition, bouncingState, setBouncingState) => {\n  switch (bouncingState) {\n  | Idle =>\n    // TODO: Why isn't Animation.const always sufficient to stop the timer?\n    Hooks.animation(\n      ~name=\"Revery:ScrollView:Bounce Animation Hook (Idle)\",\n      ~active=false,\n      Animation.const(scrollPosition),\n    )\n\n  | Bouncing(force) =>\n    Hooks.animation(\n      ~name=\"Revery:ScrollView:Bounce Animation Hook (Bouncing)\",\n      bounceAnimation(~origin=scrollPosition, ~force),\n      ~onComplete=() =>\n      setBouncingState(_ => Idle)\n    )\n  };\n};\n\nlet handeScroll =\n    (\n      ~deltaValue,\n      ~bounce,\n      ~scrollPosition,\n      ~maxScrollValue,\n      ~bouncingState,\n      ~setBouncingState,\n      ~scrollDispatch,\n    ) => {\n  let delta = int_of_float(deltaValue *. 25.);\n  let newScrollPosition = scrollPosition - delta;\n\n  let isMinScrollValue = newScrollPosition < 0;\n  let isMaxScrollValue = newScrollPosition > maxScrollValue;\n\n  switch (bouncingState) {\n  | Bouncing(force) when force < 0 && deltaValue < 0. =>\n    setBouncingState(_ => Idle)\n  | Bouncing(force) when force > 0 && deltaValue > 0. =>\n    setBouncingState(_ => Idle)\n  | Bouncing(_) => ()\n  | Idle when !bounce && (isMinScrollValue || isMaxScrollValue) =>\n    let clampedScrollValue = isMinScrollValue ? 0 : maxScrollValue;\n    scrollDispatch(ScrollUpdated(clampedScrollValue));\n  | Idle when bounce && (isMinScrollValue || isMaxScrollValue) =>\n    setBouncingState(_ => Bouncing(- delta * 2));\n    scrollDispatch(ScrollUpdated(isMinScrollValue ? 0 : maxScrollValue));\n  | Idle => scrollDispatch(ScrollUpdated(newScrollPosition))\n  };\n};\n\nlet%component make =\n              (\n                ~style,\n                ~scrollLeft=0,\n                ~scrollTop=0,\n                ~bounce=Environment.isMac,\n                ~children=React.empty,\n                (),\n              ) => {\n  let%hook (outerRef: option(Revery_UI.node), setOuterRef) =\n    Hooks.state(None);\n\n  let%hook (actualScrollTop, scrollYdispatch) =\n    Hooks.reducer(~initialState=scrollTop, reducer);\n  let%hook (bouncingStateY, setBouncingStateY) = Hooks.state(Idle);\n  let%hook (actualScrollTop, _bounceAnimationState, resetBouncingAnimationY) =\n    bounceAnimationHook(actualScrollTop, bouncingStateY, setBouncingStateY);\n\n  let setBouncingStateY = state => {\n    resetBouncingAnimationY();\n    setBouncingStateY(state);\n  };\n\n  let%hook (actualScrollLeft, scrollXdispatch) =\n    Hooks.reducer(~initialState=scrollLeft, reducer);\n  let%hook (bouncingStateX, setBouncingStateX) = Hooks.state(Idle);\n  let%hook (actualScrollLeft, _bounceAnimationState, resetBouncingAnimationX) =\n    bounceAnimationHook(actualScrollLeft, bouncingStateX, setBouncingStateX);\n  let setBouncingStateX = state => {\n    resetBouncingAnimationX();\n    setBouncingStateX(state);\n  };\n\n  let scrollBarThickness = 10;\n\n  let innerViewTransform = [\n    TranslateX((-1.) *. float_of_int(actualScrollLeft)),\n    TranslateY((-1.) *. float_of_int(actualScrollTop)),\n  ];\n\n  let (horizontalScrollBar, verticalScrollBar, scroll) =\n    switch (outerRef) {\n    | Some(outer) =>\n      let inner = outer#firstChild();\n\n      // TODO: #287 For some reason `inner` component doesn't get expanded by it's childrens\n      // Could be bug with https://github.com/jordwalke/flex or how we use it\n\n      let maxChildWidth =\n        List.fold_left(\n          (maxWidth, child: node) => {\n            let width = child#measurements().width;\n\n            if (maxWidth > width) {\n              maxWidth;\n            } else {\n              width;\n            };\n          },\n          0,\n          inner#getChildren(),\n        );\n\n      let childHeight = inner#measurements().height;\n      let outerMeasurements = outer#measurements();\n\n      let maxHeight = max(0, childHeight - outerMeasurements.height);\n      let maxWidth = max(0, maxChildWidth - outerMeasurements.width);\n\n      /*\n       * TODO: #287\n       * prerr_endline (\"Child width: \" ++ string_of_int(childMeasurements.width));\n       * prerr_endline (\"Container width: \" ++ string_of_int(outerMeasurements.width));\n       * This can be removed once #287 is fixed\n       */\n\n      let verticalThumbHeight =\n        childHeight > 0\n          ? outerMeasurements.height * outerMeasurements.height / childHeight\n          : 1;\n      let horizontalThumbHeight =\n        maxChildWidth > 0\n          ? outerMeasurements.width * outerMeasurements.width / maxChildWidth\n          : 1;\n\n      let isVerticalScrollBarVisible = maxHeight > 0;\n      let isHorizontalScrollBarVisible = maxWidth > 0;\n\n      let verticalScrollBar =\n        isVerticalScrollBarVisible\n          ? <Slider\n              onValueChanged={v =>\n                scrollYdispatch(ScrollUpdated(int_of_float(v)))\n              }\n              minimumValue=0.\n              maximumValue={float_of_int(maxHeight)}\n              sliderLength={outerMeasurements.height}\n              thumbLength=verticalThumbHeight\n              value={float_of_int(actualScrollTop)}\n              trackThickness=scrollBarThickness\n              thumbThickness=scrollBarThickness\n              minimumTrackColor=scrollTrackColor\n              maximumTrackColor=scrollTrackColor\n              thumbColor=scrollThumbColor\n              vertical=true\n            />\n          : empty;\n\n      let horizontalScrollBar =\n        isHorizontalScrollBarVisible\n          ? <Slider\n              onValueChanged={v =>\n                scrollXdispatch(ScrollUpdated(int_of_float(v)))\n              }\n              minimumValue=0.\n              maximumValue={float_of_int(maxWidth)}\n              sliderLength={outerMeasurements.width}\n              thumbLength=horizontalThumbHeight\n              value={float_of_int(actualScrollLeft)}\n              trackThickness=scrollBarThickness\n              thumbThickness=scrollBarThickness\n              minimumTrackColor=scrollTrackColor\n              maximumTrackColor=scrollTrackColor\n              thumbColor=scrollThumbColor\n            />\n          : empty;\n\n      let scroll = (wheelEvent: NodeEvents.mouseWheelEventParams) => {\n        let horizontalScroll =\n          wheelEvent.shiftKey || abs_float(wheelEvent.deltaX) > 0.;\n\n        if (horizontalScroll) {\n          if (isHorizontalScrollBarVisible) {\n            let horizontalScrollMultiplier = Environment.isMac ? (-1.) : 1.;\n            handeScroll(\n              ~deltaValue=\n                abs_float(wheelEvent.deltaX) > 0.\n                  ? horizontalScrollMultiplier *. wheelEvent.deltaX\n                  : wheelEvent.deltaY,\n              ~bounce,\n              ~scrollPosition=actualScrollLeft,\n              ~maxScrollValue=maxWidth,\n              ~bouncingState=bouncingStateX,\n              ~setBouncingState=setBouncingStateX,\n              ~scrollDispatch=scrollXdispatch,\n            );\n          };\n        } else if (isVerticalScrollBarVisible) {\n          handeScroll(\n            ~deltaValue=wheelEvent.deltaY,\n            ~bounce,\n            ~scrollPosition=actualScrollTop,\n            ~maxScrollValue=maxHeight,\n            ~bouncingState=bouncingStateY,\n            ~setBouncingState=setBouncingStateY,\n            ~scrollDispatch=scrollYdispatch,\n          );\n        };\n      };\n      (horizontalScrollBar, verticalScrollBar, scroll);\n    | _ => (empty, empty, (_ => ()))\n    };\n\n  let innerStyle =\n    Style.[\n      transform(innerViewTransform),\n      position(`Absolute),\n      top(0),\n      left(horizontalScrollBar == empty ? 0 : scrollBarThickness),\n      right(verticalScrollBar == empty ? 0 : scrollBarThickness),\n    ];\n\n  let verticalScrollBarContainerStyle =\n    Style.[\n      position(`Absolute),\n      right(0),\n      top(0),\n      bottom(0),\n      width(scrollBarThickness),\n    ];\n\n  let horizontalScrollBarContainerStyle =\n    Style.[\n      position(`Absolute),\n      right(0),\n      left(0),\n      bottom(0),\n      height(scrollBarThickness),\n    ];\n\n  <View style>\n    <View\n      onMouseWheel=scroll\n      ref={r => setOuterRef(_ => Some(r))}\n      style=Style.[flexGrow(1), position(`Relative), overflow(`Scroll)]>\n      <View style=innerStyle> children </View>\n      <View style=verticalScrollBarContainerStyle> verticalScrollBar </View>\n      <View style=horizontalScrollBarContainerStyle>\n        horizontalScrollBar\n      </View>\n    </View>\n  </View>;\n};\n"
  },
  {
    "path": "src/UI_Components/Slider.re",
    "content": "/*\n * Slider.re\n *\n * This module provides a `<Slider />` component, loosely inspired by the\n * `<Slider />` component in React-Native, but geared towards the desktop.\n */\n\nopen Revery_Math;\nopen Revery_Core;\nopen Revery_UI;\nopen Revery_UI_Primitives;\n\nmodule Hooks = Revery_UI_Hooks;\n\ntype clickFunction = unit => unit;\nlet noop = () => ();\n\ntype valueChangedFunction = float => unit;\nlet noopValueChanged = _f => ();\n\nlet defaultBoundingBox = BoundingBox2d.create(0., 0., 1., 1.);\n\nlet%component make =\n              (\n                ~onValueChanged=noopValueChanged,\n                ~minimumValue=0.,\n                ~maximumValue=1.,\n                ~initialValue=0.,\n                ~value=?,\n                ~vertical=false,\n                ~thumbLength=15,\n                ~sliderLength=100,\n                ~thumbThickness=15,\n                ~trackThickness=5,\n                ~maximumTrackColor=Colors.darkGray,\n                ~minimumTrackColor=Color.hex(\"#90f7ff\"),\n                ~thumbColor=Colors.gray,\n                (),\n              ) => {\n  let%hook (sliderBoundingBox, setSliderBoundingBox) =\n    Hooks.state(defaultBoundingBox);\n  let%hook (uncontrolledValue, setUncontrolledValue) =\n    Hooks.reducer((value, _) => value, ~initialState=initialValue);\n\n  let value =\n    switch (value) {\n    | Some(controlledValue) => controlledValue\n    | None => uncontrolledValue\n    };\n\n  let availableWidth = {\n    let (sliderX0, sliderY0, sliderX1, sliderY1) =\n      BoundingBox2d.getBounds(sliderBoundingBox);\n\n    let sliderWidth = vertical ? sliderY1 -. sliderY0 : sliderX1 -. sliderX0;\n    let thumbWidth = float(thumbLength);\n    sliderWidth -. thumbWidth;\n  };\n\n  let sliderUpdate = (width, startPosition, endPosition, mouseX, mouseY) => {\n    let mousePosition = vertical ? mouseY : mouseX;\n    let thumbPosition =\n      clamp(\n        mousePosition -. float(thumbLength) /. 2.,\n        startPosition,\n        endPosition,\n      )\n      -. startPosition;\n\n    let normalizedValue =\n      thumbPosition /. width *. (maximumValue -. minimumValue) +. minimumValue;\n    setUncontrolledValue(normalizedValue);\n    onValueChanged(normalizedValue);\n  };\n\n  let%hook (captureMouse, captureState) =\n    Hooks.mouseCapture(\n      ~onMouseMove=\n        (origin, evt: NodeEvents.mouseMoveEventParams) => {\n          sliderUpdate(\n            availableWidth,\n            origin,\n            origin +. availableWidth,\n            evt.mouseX,\n            evt.mouseY,\n          );\n          Some(origin);\n        },\n      ~onMouseUp=(_, _) => None,\n      (),\n    );\n\n  let onMouseDown = (evt: NodeEvents.mouseButtonEventParams) => {\n    let (x0, y0, _, _) = BoundingBox2d.getBounds(sliderBoundingBox);\n    let origin = vertical ? y0 : x0;\n    sliderUpdate(\n      availableWidth,\n      origin,\n      origin +. availableWidth,\n      evt.mouseX,\n      evt.mouseY,\n    );\n    captureMouse(origin);\n  };\n\n  let sliderBackgroundColor = maximumTrackColor;\n\n  let sliderOpacity = captureState == None ? 0.8 : 1.0;\n\n  let sliderHeight = max(thumbThickness, trackThickness);\n  let trackHeight = trackThickness;\n  let thumbHeight = thumbThickness;\n  let trackMargins = (sliderHeight - trackHeight) / 2;\n\n  let thumbPosition =\n    int_of_float(\n      (value -. minimumValue)\n      /. (maximumValue -. minimumValue)\n      *. availableWidth,\n    )\n    - thumbLength\n    / 2;\n\n  let style =\n    Style.[\n      width(vertical ? sliderHeight : sliderLength),\n      height(vertical ? sliderLength : sliderHeight),\n      cursor(MouseCursors.pointer),\n    ];\n\n  let thumbWidth = thumbLength;\n\n  let beforeTrackStyle =\n    Style.[\n      top(vertical ? 0 : trackMargins),\n      bottom(\n        vertical\n          ? sliderLength - thumbPosition - thumbWidth / 2 : trackMargins,\n      ),\n      left(vertical ? trackMargins : 0),\n      right(\n        vertical\n          ? trackMargins : sliderLength - thumbPosition - thumbWidth / 2,\n      ),\n      position(`Absolute),\n      backgroundColor(minimumTrackColor),\n    ];\n\n  let afterTrackStyle =\n    Style.[\n      top(vertical ? thumbPosition + thumbWidth * 3 / 2 : trackMargins),\n      bottom(vertical ? 0 : trackMargins),\n      left(vertical ? trackMargins : thumbPosition + thumbWidth * 3 / 2),\n      right(vertical ? trackMargins : 0),\n      position(`Absolute),\n      backgroundColor(sliderBackgroundColor),\n    ];\n\n  <Opacity opacity=sliderOpacity>\n    <View\n      onMouseDown\n      style\n      onBoundingBoxChanged={bbox => setSliderBoundingBox(_ => bbox)}>\n      <View style=beforeTrackStyle />\n      <View\n        style=Style.[\n          position(`Absolute),\n          height(vertical ? thumbWidth : thumbHeight),\n          width(vertical ? thumbHeight : thumbWidth),\n          left(vertical ? 0 : thumbPosition + thumbWidth / 2),\n          top(vertical ? thumbPosition + thumbWidth / 2 : 0),\n          backgroundColor(thumbColor),\n        ]\n      />\n      <View style=afterTrackStyle />\n    </View>\n  </Opacity>;\n};\n"
  },
  {
    "path": "src/UI_Components/Stack.re",
    "content": "open Revery_UI;\nopen Revery_UI_Primitives;\n\nlet style = (w, h) => Style.[position(`Relative), width(w), height(h)];\n\nlet make = (~width as w, ~height as h, ~children, ()) =>\n  <View style={style(w, h)}> ...children </View>;\n"
  },
  {
    "path": "src/UI_Components/Ticker.re",
    "content": "open Revery_UI;\nopen Revery_Core;\nopen Revery_UI_Primitives;\n\nmodule Hooks = Revery_UI_Hooks;\n\ntype tickFunction = Time.t => unit;\nlet noop: tickFunction = _ => ();\n\nlet%component make =\n              (\n                ~children=React.empty,\n                ~onTick=noop,\n                ~tickRate=Time.seconds(1),\n                ~name=\"<Ticker />\",\n                (),\n              ) => {\n  let%hook () =\n    Hooks.effect(\n      OnMount,\n      () => {\n        let dispose = Revery_Core.Tick.interval(~name, onTick, tickRate);\n\n        Some(dispose);\n      },\n    );\n\n  <View> children </View>;\n};\n"
  },
  {
    "path": "src/UI_Components/Ticker.rei",
    "content": "open Revery_Core;\n\ntype tickFunction = Time.t => unit;\n\n/**\n{2 Description:}\n\nThe [Ticker] component is a helper that calls a specific function periodically as a side-effect.\n\n{2 Usage:}\n\n{[\nlet tick tick = (t) => print_endline(\"Time: \" ++ string_of_float(Time.toFloatSeconds(t)));\n\n<Ticker onTick=tick tickRate=Seconds(1.0) />\n]}\n\n@param [onTick] Callback function, called for each tick.\n@param [tickRate] Frequency of calling the [onTick] function.\n*/\nlet make:\n  (\n    ~key: Brisk_reconciler.Key.t=?,\n    ~children: Revery_UI.element=?,\n    ~onTick: tickFunction=?,\n    ~tickRate: Revery_Core.Time.t=?,\n    ~name: string=?,\n    unit\n  ) =>\n  Brisk_reconciler.element(Revery_UI.viewNode);\n"
  },
  {
    "path": "src/UI_Components/Tree.re",
    "content": "open Revery_UI;\nopen Revery_Core;\nopen Revery_Font;\nopen Revery_UI_Primitives;\n\ntype status =\n  | Open\n  | Closed;\n\ntype content('a) = {\n  data: 'a,\n  id: int,\n  status,\n};\n\n/*\n   A multiway tree, each node can have a variable number of siblings\n   each of which can have a variable number of siblings etc.\n\n   more info: https://ocaml.org/learn/tutorials/99problems.html#Multiway-Trees\n */\ntype tree('a) =\n  | Empty\n  | Node(content('a), list(tree('a)));\n\nlet defaultNodeStyles = Style.[flexDirection(`Row), marginVertical(5)];\n\nlet rec findNode = (nodeID, tr) =>\n  switch (tr) {\n  | Empty => None\n  | Node({id, _} as x, _) when nodeID == id => Some(x)\n  | Node(_, []) => None\n  | Node(_, [n, ...rest]) =>\n    let found = findNode(nodeID, n);\n    switch (found) {\n    | Some(n) => Some(n)\n    | None =>\n      List.fold_left(\n        (accum, item) =>\n          switch (accum) {\n          | Some(a) => Some(a)\n          | None => findNode(nodeID, item)\n          },\n        None,\n        rest,\n      )\n    };\n  };\n\nlet default = (~indent, {data, status, _}) => {\n  let isOpen =\n    switch (status) {\n    | Open => true\n    | Closed => false\n    };\n  open Style;\n  let textStyles = [color(Colors.black)];\n  let indentStr = String.make(indent * 2, ' ');\n  let arrow = isOpen ? {||} : {||};\n  <Clickable>\n    <View style=defaultNodeStyles>\n      <Text\n        text={indentStr ++ arrow ++ \" \"}\n        style=textStyles\n        fontSize=10.\n        fontFamily={Family.system(\"FontAwesome5FreeSolid.otf\")}\n      />\n      <Text\n        text=data\n        style=textStyles\n        fontSize=10.\n        fontFamily={Family.system(\"FontAwesome5FreeSolid.otf\")}\n      />\n    </View>\n  </Clickable>;\n};\n\n/**\n * @param ~indent How much to indent the current (sub)tree.\n * @param ~renderer is a function which determines how each node is rendered\n * @param t The tree to convert to a tree of JSX elements.\n */\nlet rec renderTree = (~indent=0, ~nodeRenderer, ~emptyRenderer, t) => {\n  let drawNode = nodeRenderer(~indent);\n  let empty =\n    switch (emptyRenderer) {\n    | Some(r) => [r(indent)]\n    | None => []\n    };\n  let createSubtree =\n    renderTree(~indent=indent + 1, ~nodeRenderer, ~emptyRenderer);\n  switch (t) {\n  | Empty => empty\n  /* If the node is closed OR has no children\n     only draw the parent do not render its children */\n  | Node({status: Open, _} as x, [])\n  | Node({status: Closed, _} as x, _) => [drawNode(x)]\n  | Node(current, siblings) =>\n    let renderedSiblings =\n      List.fold_left(\n        (accum, next) => {\n          let grandChild = createSubtree(next);\n          List.concat([accum, grandChild]);\n        },\n        [],\n        siblings,\n      );\n    [drawNode(current), ...renderedSiblings];\n  };\n};\n\n/*\n   Cannot set a default argument for the node renderer as this will\n   narrow down the type signature of the \"tree\" to whaterver type the\n   default takes making it no longer generalisable\n */\nlet make = (~tree, ~nodeRenderer, ~emptyRenderer=None, ()) => {\n  let componentTree = renderTree(tree, ~nodeRenderer, ~emptyRenderer);\n  <View> {componentTree |> React.listToElement} </View>;\n};\n"
  },
  {
    "path": "src/UI_Components/dune",
    "content": "(library\n (name Revery_UI_Components)\n (preprocess\n  (pps brisk-reconciler.ppx lwt_ppx))\n (public_name Revery.UI_Components)\n (libraries lwt lwt.unix sdl2 flex omd Revery_Core Revery_Math Revery_UI\n   Revery_UI_Primitives Revery_UI_Hooks))\n"
  },
  {
    "path": "src/UI_Hooks/Effect.re",
    "content": "open Revery_UI;\n\nlet effect = React.Hooks.effect;\n"
  },
  {
    "path": "src/UI_Hooks/Effect.rei",
    "content": "open Revery_UI.React.Hooks;\n\n/**\n{2 Description:}\n\nThe Effect Hook lets you perform side effects in function components.\n\n{2 Usage:}\n\n{[\nlet%hook () = Hooks.effect(OnMount, () => {\n  // Some side-effect\n\n  // Clean-up function\n  None;\n});\n]}\n*/\nlet effect:\n  (\n    Effect.condition('a),\n    unit => option(unit => unit),\n    t(Effect.t('a) => 'b, 'c)\n  ) =>\n  (unit, t('b, 'c));\n"
  },
  {
    "path": "src/UI_Hooks/Reducer.re",
    "content": "open Revery_UI;\n\nlet reducer = React.Hooks.reducer;\n"
  },
  {
    "path": "src/UI_Hooks/Reducer.rei",
    "content": "open Revery_UI.React.Hooks;\n\n/**\n{2 Description:}\n\nAn alternative to Hooks.state. Accepts a reducer of type (action, state) => newState, and returns the current state paired with a dispatch function.\n\n{2 Usage:}\n\n{[\nlet%hook (state, dispatch) = Hooks.reducer(~initialState={counter:0}, reduce);\n]}\n*/\nlet reducer:\n  (~initialState: 'a, ('b, 'a) => 'a, t(Reducer.t('a) => 'c, 'd)) =>\n  (('a, 'b => unit), t('c, 'd));\n"
  },
  {
    "path": "src/UI_Hooks/Ref.re",
    "content": "open Revery_UI;\n\nlet ref = React.Hooks.ref;\n"
  },
  {
    "path": "src/UI_Hooks/Ref.rei",
    "content": "open Revery_UI.React.Hooks;\n\nlet ref: ('a, t(ref('a) => 'b, 'c)) => (ref('a), t('b, 'c));\n"
  },
  {
    "path": "src/UI_Hooks/Revery_UI_Hooks.re",
    "content": "module SpringHook = Spring;\n\nopen Revery_UI;\n\ninclude Effect;\ninclude Reducer;\ninclude Ref;\ninclude State;\ninclude Tick;\n\nlet time = Timer.time;\nlet timer = Timer.timer;\n\nlet animation = (~active=true, ~onComplete=() => (), ~name, animation) => {\n  let%hook (isCompleted, setCompleted) = state(false);\n  let%hook (time, resetTimer) =\n    timer(~name, ~active=active && !isCompleted, ());\n\n  let (value, animationState) = Animation.apply(time, animation);\n\n  // Stop timer when animation completes\n  switch (animationState) {\n  | Complete(_) =>\n    onComplete();\n    setCompleted(_ => true);\n  | _ => ()\n  };\n\n  let reset = () => {\n    setCompleted(_ => false);\n    resetTimer();\n  };\n\n  (value, animationState, reset);\n};\n\nmodule Internal = {\n  type durationCalculation =\n    | Constant(Time.t)\n    | Function({\n        initial: Time.t,\n        calculated: (~current: float, ~target: float) => Time.t,\n      });\n\n  let transition =\n      (\n        ~active=true,\n        ~duration: durationCalculation,\n        ~delay=Time.zero,\n        ~easing=Easing.linear,\n        ~initialValue=?,\n        ~name,\n        specifiedTargetValue,\n      ) => {\n    let initialValue =\n      switch (initialValue) {\n      | Some(v) => v\n      | None => specifiedTargetValue\n      };\n\n    let initialDurationTime =\n      switch (duration) {\n      | Constant(time) => time\n      | Function({initial, _}) => initial\n      };\n\n    let%hook ((startValue, targetValue, durationTime), internalSetTarget) =\n      state((initialValue, specifiedTargetValue, initialDurationTime));\n\n    let anim =\n      Animation.animate(durationTime)\n      |> Animation.delay(delay)\n      |> Animation.ease(easing)\n      |> Animation.tween(startValue, targetValue);\n\n    let%hook (value, _animationState, resetTimer) =\n      animation(~name, ~active, anim);\n\n    let value = active ? value : specifiedTargetValue;\n\n    let setTargetValue = (newTarget, durationTime) => {\n      resetTimer();\n      internalSetTarget(_ => (value, newTarget, durationTime));\n    };\n\n    let%hook () =\n      effect(\n        If((!=), specifiedTargetValue),\n        () => {\n          let durationTime =\n            switch (duration) {\n            | Function({calculated, _}) =>\n              calculated(~current=value, ~target=specifiedTargetValue)\n            | Constant(time) => time\n            };\n\n          setTargetValue(specifiedTargetValue, durationTime);\n          None;\n        },\n      );\n\n    value;\n  };\n};\n\nlet transition =\n    (\n      ~active=true,\n      ~duration=Time.seconds(1),\n      ~delay=Time.zero,\n      ~easing=Easing.linear,\n      ~initialValue=?,\n      targetValue,\n    ) =>\n  Internal.transition(\n    ~active,\n    ~duration=Internal.Constant(duration),\n    ~delay,\n    ~easing,\n    ~initialValue?,\n    targetValue,\n  );\n\nlet transitionf =\n    (\n      ~active=true,\n      ~delay=Time.zero,\n      ~easing=Easing.linear,\n      ~initialDuration,\n      ~duration,\n      ~initialValue=?,\n      targetValue,\n    ) =>\n  Internal.transition(\n    ~active,\n    ~duration=\n      Internal.Function({initial: initialDuration, calculated: duration}),\n    ~delay,\n    ~easing,\n    ~initialValue?,\n    targetValue,\n  );\n\nlet spring = SpringHook.spring;\n\n/**\n * let%hook (captureMouse, captureState) = Hooks.mouseCapture(...);\n *\n *   where [captureMouse] requires the initial state to be passed as its argument.\n *\n * Each callback is passed the current state and event data, and requires either a\n * state value to be returned or [None] to stop the capture.\n */\nlet mouseCapture =\n    (\n      ~onMouseDown=(state, _evt) => Some(state),\n      ~onMouseUp=(state, _evt) => Some(state),\n      ~onMouseMove=(state, _evt) => Some(state),\n      ~onMouseWheel=(state, _evt) => Some(state),\n      ~onRelease=_state => (),\n      (),\n    ) => {\n  let%hook state = ref(None);\n\n  let%hook () =\n    effect(OnMount, () =>\n      Some(\n        () =>\n          if (state^ != None) {\n            Mouse.releaseCapture();\n          },\n      )\n    );\n\n  let capture = initialState => {\n    state := Some(initialState);\n\n    let wrap = (f, event) => {\n      state := f(Option.get(state^), event);\n\n      if (state^ == None) {\n        Mouse.releaseCapture();\n      };\n    };\n\n    Mouse.setCapture(\n      Revery_UI.getActiveWindow() |> Option.get, // May fail if called outside rendering\n      ~onMouseDown=wrap(onMouseDown),\n      ~onMouseUp=wrap(onMouseUp),\n      ~onMouseMove=wrap(onMouseMove),\n      ~onMouseWheel=wrap(onMouseWheel),\n      ~onRelease=() =>\n      onRelease(state^)\n    );\n  };\n\n  (capture, state^);\n};\n\nlet hover = () => {\n  let%hook (isHovered, setHovered) = state(false);\n\n  let startHover = _evt => setHovered(_ => true);\n  let endHover = _evt => setHovered(_ => false);\n\n  (isHovered, startHover, endHover);\n};\n"
  },
  {
    "path": "src/UI_Hooks/Spring.re",
    "content": "module Time = Revery_Core.Time;\nmodule Spring = Revery_UI.Spring;\n\nlet spring =\n    (\n      ~enabled=true,\n      ~name,\n      ~target,\n      ~initialState=?,\n      ~restThreshold=0.1,\n      options,\n    ) => {\n  let initialState =\n    switch (initialState) {\n    | Some(state) => state\n    | None => Spring.create(target, Time.now())\n    };\n\n  let%hook previousState = Ref.ref(initialState);\n\n  let isActive =\n    enabled\n    && (\n      !Spring.isAtRest(~restThreshold, previousState^)\n      || Float.abs(target -. previousState^.value) > restThreshold\n    );\n\n  let%hook (time, _) = Timer.timer(~name, ~active=isActive, ());\n\n  let state =\n    if (enabled) {\n      Spring.tick(target, previousState^, options, time);\n    } else {\n      Spring.setPosition(target, previousState^);\n    };\n\n  previousState := state;\n\n  let setImmediately = position =>\n    previousState := Spring.setPosition(position, previousState^);\n\n  let v = isActive ? state.value : target;\n\n  (v, setImmediately);\n};\n"
  },
  {
    "path": "src/UI_Hooks/State.re",
    "content": "open Revery_UI;\n\nlet state = React.Hooks.state;\n"
  },
  {
    "path": "src/UI_Hooks/State.rei",
    "content": "open Revery_UI.React.Hooks;\n\n/**\n{2 Description:}\n\nTODO\n\n{2 Usage:}\n\n{[\nlet%hook (state, setState) = Hooks.state(0);\n]}\n*/\nlet state:\n  ('a, t(State.t('a) => 'b, 'c)) => (('a, ('a => 'a) => unit), t('b, 'c));\n"
  },
  {
    "path": "src/UI_Hooks/Tick.re",
    "content": "module Time = Revery_Core.Time;\nmodule Tick = Revery_Core.Tick;\n\nmodule Hooks = Revery_UI.React.Hooks;\n\nlet tick = (~tickRate=Time.seconds(1), ~name, onTick) => {\n  // Because Tick.interval is only called once, initiallly, with the initial\n  // onTick function, to execute the latest onTick function we either have to\n  // dispose and recreate it for every call, or use a mutable variable to replace\n  // the callback. Here we do the latter.\n  let%hook onTickRef = Hooks.ref(onTick);\n  onTickRef := onTick;\n\n  let%hook () =\n    Hooks.effect(\n      OnMount,\n      () => {\n        let dispose = Tick.interval(~name, t => onTickRef^(t), tickRate);\n\n        Some(dispose);\n      },\n    );\n\n  ();\n};\n"
  },
  {
    "path": "src/UI_Hooks/Tick.rei",
    "content": "open Revery_UI.React.Hooks;\n\nmodule Time = Revery_Core.Time;\n\nlet tick:\n  (\n    ~tickRate: Time.t=?,\n    ~name: string,\n    Time.t => unit,\n    t((ref(Time.t => unit), Effect.t(Effect.onMount)) => 'a, 'b)\n  ) =>\n  (unit, t('a, 'b));\n"
  },
  {
    "path": "src/UI_Hooks/Timer.re",
    "content": "open Effect;\nopen Reducer;\nopen Tick;\n\nlet time = (~tickRate=Time.zero, ~name, ()) => {\n  let%hook (time, setTime) = reducer(~initialState=Time.now(), t => t);\n  let%hook () = tick(~tickRate, ~name, _dt => setTime(_t => Time.now()));\n\n  time;\n};\n\nlet timer = (~tickRate=Time.zero, ~active=true, ~name, ()) => {\n  let%hook (time, setTime) = reducer(~initialState=Time.now(), t => t);\n  let%hook startTime = Ref.ref(time);\n\n  // HACK: We have to manually track disposal, too, to workaround a bug with\n  // multiple timer hooks. This shouldn't be necessary - the `effect` hook\n  // without `OnMountAndIf` should be handling disposal completely for us!\n\n  // However, there is a bug when there are multiple timer hooks used -\n  // if they are toggled on and off independently, we can get into a state\n  // where a hanging ticker is left.\n\n  // This is a very bad state because it will cause the UI to constantly\n  // re-render, and be expensive for both CPU and battery life!\n\n  // See the test case in:\n  // test/UI/HooksTest.re that manifests this bug\n\n  // Ideally, we can fix the underlying bug (perhaps in the effect hook),\n  // and remove this hack while keeping all those tests green.\n\n  // It's easy to hit when there are multiple springs associated with a component,\n  // which each use an underlying timer.\n  let%hook lastDispose = Ref.ref(None);\n\n  let onTick = _dt => setTime(_t => Time.now());\n\n  let%hook () =\n    effect(OnMountAndIf((!=), active), () =>\n      if (active) {\n        startTime := Time.(now() - (time - startTime^));\n\n        Option.iter(dispose => dispose(), lastDispose^);\n\n        let stopInterval = Revery_Core.Tick.interval(~name, onTick, tickRate);\n        lastDispose := Some(stopInterval);\n\n        Some(stopInterval);\n      } else {\n        Option.iter(dispose => dispose(), lastDispose^);\n\n        lastDispose := None;\n\n        None;\n      }\n    );\n\n  let reset = () => {\n    startTime := time;\n  };\n\n  (Time.(time - startTime^), reset);\n};\n"
  },
  {
    "path": "src/UI_Hooks/dune",
    "content": "(library\n (name Revery_UI_Hooks)\n (public_name Revery.UI_Hooks)\n (preprocess\n  (pps brisk-reconciler.ppx lwt_ppx))\n (libraries lwt lwt.unix sdl2 flex Revery_Core Revery_Math Revery_UI))\n"
  },
  {
    "path": "src/UI_Primitives/AllowPointer.re",
    "content": "open Revery_UI;\nopen React;\n\nlet ignorePointerStyle =\n  Style.make(~pointerEvents=Style.PointerEvents.Allow, ());\n\nlet%nativeComponent make = (~children, (), hooks) => (\n  {\n    make: () => {\n      let node = PrimitiveNodeFactory.get().createNode();\n      node#setStyle(ignorePointerStyle);\n      node;\n    },\n    configureInstance: (~isFirstRender as _, node) => {\n      node;\n    },\n    children,\n    insertNode,\n    deleteNode,\n    moveNode,\n  },\n  hooks,\n);\n"
  },
  {
    "path": "src/UI_Primitives/BoxShadow.re",
    "content": "open Revery_UI;\nopen React;\n\nlet%nativeComponent make =\n                    (~boxShadow=Style.BoxShadow.make(), ~children, (), hooks) => (\n  {\n    make: () => {\n      let styles = Style.make(~boxShadow, ());\n      let node = PrimitiveNodeFactory.get().createViewNode();\n      node#setStyle(styles);\n      node;\n    },\n    configureInstance: (~isFirstRender as _, node) => {\n      let styles = Style.make(~boxShadow, ());\n      node#setStyle(styles);\n      node;\n    },\n    children,\n    insertNode,\n    deleteNode,\n    moveNode,\n  },\n  hooks,\n);\n"
  },
  {
    "path": "src/UI_Primitives/Canvas.re",
    "content": "open Revery_UI;\nopen React;\n\nlet%nativeComponent make =\n                    (\n                      ~onMouseDown=?,\n                      ~onMouseMove=?,\n                      ~onMouseUp=?,\n                      ~onMouseWheel=?,\n                      ~onMouseEnter=?,\n                      ~onMouseLeave=?,\n                      ~onMouseOver=?,\n                      ~onMouseOut=?,\n                      ~onBlur=?,\n                      ~onFocus=?,\n                      ~ref=?,\n                      ~style=Style.emptyViewStyle,\n                      ~children=React.empty,\n                      ~onKeyDown=?,\n                      ~onKeyUp=?,\n                      ~onDimensionsChanged=?,\n                      ~onBoundingBoxChanged=?,\n                      ~render=?,\n                      (),\n                      hooks,\n                    ) => (\n  {\n    make: () => {\n      let styles = Style.create(~style, ());\n      let events =\n        NodeEvents.make(\n          ~ref?,\n          ~onMouseDown?,\n          ~onMouseMove?,\n          ~onMouseUp?,\n          ~onMouseWheel?,\n          ~onMouseEnter?,\n          ~onMouseLeave?,\n          ~onMouseOver?,\n          ~onMouseOut?,\n          ~onBlur?,\n          ~onFocus?,\n          ~onKeyDown?,\n          ~onKeyUp?,\n          ~onDimensionsChanged?,\n          ~onBoundingBoxChanged?,\n          (),\n        );\n      let node = (new canvasNode)();\n      node#setEvents(events);\n      node#setStyle(styles);\n      node#setRender(render);\n      Obj.magic(node);\n    },\n    configureInstance: (~isFirstRender as _, node) => {\n      let styles = Style.create(~style, ());\n      let events =\n        NodeEvents.make(\n          ~ref?,\n          ~onMouseDown?,\n          ~onMouseMove?,\n          ~onMouseUp?,\n          ~onMouseWheel?,\n          ~onMouseEnter?,\n          ~onMouseLeave?,\n          ~onMouseOver?,\n          ~onMouseOut?,\n          ~onBlur?,\n          ~onFocus?,\n          ~onKeyDown?,\n          ~onKeyUp?,\n          ~onDimensionsChanged?,\n          ~onBoundingBoxChanged?,\n          (),\n        );\n      let canvasNode: canvasNode = Obj.magic(node);\n      node#setEvents(events);\n      node#setStyle(styles);\n      canvasNode#setRender(render);\n      node;\n    },\n    children,\n    insertNode,\n    deleteNode,\n    moveNode,\n  },\n  hooks,\n);\n"
  },
  {
    "path": "src/UI_Primitives/IgnorePointer.re",
    "content": "open Revery_UI;\nopen React;\nlet ignorePointerStyle =\n  Style.make(~pointerEvents=Style.PointerEvents.Ignore, ());\n\nlet%nativeComponent make = (~children, (), hooks) => (\n  {\n    make: () => {\n      let node = PrimitiveNodeFactory.get().createNode();\n      node#setStyle(ignorePointerStyle);\n      node;\n    },\n    configureInstance: (~isFirstRender as _, node) => {\n      node;\n    },\n    children,\n    insertNode,\n    deleteNode,\n    moveNode,\n  },\n  hooks,\n);\n"
  },
  {
    "path": "src/UI_Primitives/Image.re",
    "content": "open Revery_UI;\nopen React;\n//open Revery_IO.LwtLetOperators;\n\nlet getStyles: (option(int), option(int), Style.t) => Style.t =\n  (w, h, style) => {\n    let style =\n      switch (w) {\n      | Some(v) => {...style, width: v}\n      | None => style\n      };\n\n    let style =\n      switch (h) {\n      | Some(v) => {...style, height: v}\n      | None => style\n      };\n    style;\n  };\n\nlet%nativeComponent make =\n                    (\n                      ~onMouseDown=?,\n                      ~onMouseMove=?,\n                      ~onMouseUp=?,\n                      ~onMouseWheel=?,\n                      ~ref=?,\n                      ~resizeMode=ImageResizeMode.Stretch,\n                      ~opacity=1.0,\n                      ~width=?,\n                      ~height=?,\n                      ~quality=`high,\n                      ~src: [ | `File(string)],\n                      ~style=Style.emptyImageStyle,\n                      ~children=React.empty,\n                      (),\n                      hooks,\n                    ) => (\n  {\n    make: () => {\n      let styles = Style.create(~style, ()) |> getStyles(width, height);\n      let events =\n        NodeEvents.make(\n          ~ref?,\n          ~onMouseDown?,\n          ~onMouseMove?,\n          ~onMouseUp?,\n          ~onMouseWheel?,\n          (),\n        );\n      let node = PrimitiveNodeFactory.get().createImageNode(None);\n      node#setOpacity(opacity);\n      node#setEvents(events);\n      node#setStyle(styles);\n      node#setResizeMode(resizeMode);\n      node#setQuality(quality);\n      Obj.magic(node);\n    },\n    configureInstance: (~isFirstRender as _, node) => {\n      let styles = Style.create(~style, ()) |> getStyles(width, height);\n      let events =\n        NodeEvents.make(\n          ~ref?,\n          ~onMouseDown?,\n          ~onMouseMove?,\n          ~onMouseUp?,\n          ~onMouseWheel?,\n          (),\n        );\n\n      let imgNode: imageNode = Obj.magic(node);\n      imgNode#setResizeMode(resizeMode);\n      imgNode#setOpacity(opacity);\n      imgNode#setQuality(quality);\n\n      ignore(\n        switch (src) {\n        //        | `Url(url) =>\n        //          let.flatMap image = Revery_IO.Image.fromUrl(url);\n        //          imgNode#setData(image);\n        //          Lwt.return();\n        | `File(path) =>\n          let maybeSkiaImage = Revery_IO.Image.fromAssetPath(path);\n          imgNode#setData(maybeSkiaImage);\n          Lwt.return();\n        },\n      );\n\n      node#setEvents(events);\n      node#setStyle(styles);\n      node;\n    },\n    children,\n    insertNode,\n    deleteNode,\n    moveNode,\n  },\n  hooks,\n);\n"
  },
  {
    "path": "src/UI_Primitives/Image.rei",
    "content": "/**\n * Image\n *\n * Used for rendering images. The `src`-property takes a `File(string) or a `Url(string)\n *\n * Examples:\n *   <Image src=`File(\"example.png\") opacity=1.0 width=64 height=64 />\n *   <Image src=`Url(\"https://example.com/image.png\") opacity=1.0 width=64 height=64 />\n */\nlet make:\n  (\n    ~key: Brisk_reconciler.Key.t=?,\n    ~onMouseDown: Revery_UI.NodeEvents.mouseDownHandler=?,\n    ~onMouseMove: Revery_UI.NodeEvents.mouseMoveHandler=?,\n    ~onMouseUp: Revery_UI.NodeEvents.mouseUpHandler=?,\n    ~onMouseWheel: Revery_UI.NodeEvents.mouseWheelHandler=?,\n    ~ref: Revery_UI.NodeEvents.refCallback(Revery_UI__.Node.node)=?,\n    ~resizeMode: Revery_UI.ImageResizeMode.t=?,\n    ~opacity: float=?,\n    ~width: int=?,\n    ~height: int=?,\n    ~quality: [ | `none | `low | `medium | `high]=?,\n    ~src: [ | `File(string)],\n    ~style: list(Revery_UI.Style.imageStyleProps)=?,\n    ~children: Revery_UI.React.React.element(Revery_UI.React.node)=?,\n    unit\n  ) =>\n  Brisk_reconciler.element(Revery_UI.React.node);\n"
  },
  {
    "path": "src/UI_Primitives/Layer.re",
    "content": "open Revery_UI;\nopen React;\n\nmodule Condition = RenderCondition;\n\nlet%nativeComponent make =\n                    (\n                      ~backgroundColor: Revery_Core.Color.t,\n                      ~condition: Condition.t,\n                      ~onMouseDown=?,\n                      ~onMouseMove=?,\n                      ~onMouseUp=?,\n                      ~onMouseWheel=?,\n                      ~onMouseEnter=?,\n                      ~onMouseLeave=?,\n                      ~onMouseOver=?,\n                      ~onMouseOut=?,\n                      ~style=Style.emptyViewStyle,\n                      ~children=React.empty,\n                      ~onDimensionsChanged=?,\n                      ~onBoundingBoxChanged=?,\n                      ~onFileDropped=?,\n                      ~mouseBehavior=Revery_UI.Normal,\n                      (),\n                      hooks,\n                    ) => (\n  {\n    make: () => {\n      let styles = Style.create(~style, ());\n      let events =\n        NodeEvents.make(\n          ~onMouseDown?,\n          ~onMouseMove?,\n          ~onMouseUp?,\n          ~onMouseWheel?,\n          ~onMouseEnter?,\n          ~onMouseLeave?,\n          ~onMouseOver?,\n          ~onMouseOut?,\n          ~onDimensionsChanged?,\n          ~onBoundingBoxChanged?,\n          ~onFileDropped?,\n          (),\n        );\n      let node = PrimitiveNodeFactory.get().createLayerNode(condition);\n      node#setBackgroundColor(backgroundColor);\n      node#setEvents(events);\n      node#setStyle(styles);\n      (node :> node);\n    },\n    configureInstance: (~isFirstRender as _, node) => {\n      let styles = Style.create(~style, ());\n      let events =\n        NodeEvents.make(\n          ~onMouseDown?,\n          ~onMouseMove?,\n          ~onMouseUp?,\n          ~onMouseWheel?,\n          ~onMouseEnter?,\n          ~onMouseLeave?,\n          ~onMouseOver?,\n          ~onMouseOut?,\n          ~onBoundingBoxChanged?,\n          ~onDimensionsChanged?,\n          ~onFileDropped?,\n          (),\n        );\n      // HACK: We should switch from using objects and set this up properly\n      let layerNode: layerNode = Obj.magic(node);\n      node#setEvents(events);\n      node#setStyle(styles);\n      node#setMouseBehavior(mouseBehavior);\n      layerNode#setBackgroundColor(backgroundColor);\n      layerNode#setCondition(condition);\n      node;\n    },\n    children,\n    insertNode,\n    deleteNode,\n    moveNode,\n  },\n  hooks,\n);\n"
  },
  {
    "path": "src/UI_Primitives/NativeButton.re",
    "content": "open Revery_UI;\nopen React;\n\nlet%nativeComponent make =\n                    (\n                      ~onMouseMove=?,\n                      ~onMouseEnter=?,\n                      ~onMouseLeave=?,\n                      ~onMouseOver=?,\n                      ~onMouseOut=?,\n                      ~ref=?,\n                      ~title=\"\",\n                      ~onClick=() => (),\n                      ~style=Style.emptyViewStyle,\n                      (),\n                      hooks,\n                    ) => (\n  {\n    make: () => {\n      let styles = Style.create(~style, ());\n      let events =\n        NodeEvents.make(\n          ~ref?,\n          ~onMouseMove?,\n          ~onMouseEnter?,\n          ~onMouseLeave?,\n          ~onMouseOver?,\n          ~onMouseOut?,\n          (),\n        );\n      let node =\n        PrimitiveNodeFactory.get().createNativeButtonNode(title, onClick);\n      node#setStyle(styles);\n      node#setEvents(events);\n      Obj.magic(node);\n    },\n    configureInstance: (~isFirstRender as _, node) => {\n      let styles = Style.create(~style, ());\n      node#setStyle(styles);\n      node;\n    },\n    children: React.empty,\n    insertNode,\n    deleteNode,\n    moveNode,\n  },\n  hooks,\n);\n"
  },
  {
    "path": "src/UI_Primitives/Opacity.re",
    "content": "open Revery_UI;\nopen React;\n\nlet%nativeComponent make = (~opacity=1.0, ~children, (), hooks) => (\n  {\n    make: () => {\n      let styles = Style.make(~opacity, ());\n      let node = PrimitiveNodeFactory.get().createNode();\n      node#setStyle(styles);\n      node;\n    },\n    configureInstance: (~isFirstRender as _, node) => {\n      let styles = Style.make(~opacity, ());\n      node#setStyle(styles);\n      node;\n    },\n    children,\n    insertNode,\n    deleteNode,\n    moveNode,\n  },\n  hooks,\n);\n"
  },
  {
    "path": "src/UI_Primitives/Padding.re",
    "content": "open Revery_UI;\nopen React;\n\nlet%nativeComponent make = (~padding, ~children=React.empty, (), hooks) => (\n  {\n    make: () => {\n      let styles = Style.make(~padding, ());\n      let node = PrimitiveNodeFactory.get().createViewNode();\n      node#setStyle(styles);\n      node;\n    },\n    configureInstance: (~isFirstRender as _, node) => {\n      let styles = Style.make(~padding, ());\n      node#setStyle(styles);\n      node;\n    },\n    children,\n    insertNode,\n    deleteNode,\n    moveNode,\n  },\n  hooks,\n);\n"
  },
  {
    "path": "src/UI_Primitives/PrimitiveNodeFactory.re",
    "content": "open Revery_UI;\n\ntype nodeFactory = {\n  createNode: unit => node,\n  createViewNode: unit => viewNode,\n  createTextNode: string => textNode,\n  createImageNode: option(Skia.Image.t) => imageNode,\n  createLayerNode: 'a. RenderCondition.t => layerNode,\n  createNativeButtonNode: (string, unit => unit) => nativeButtonNode,\n};\n\nlet defaultNodeFactory: nodeFactory = {\n  createNode: () => (new node)(),\n  createViewNode: () => (new viewNode)(),\n  createTextNode: text => (new textNode)(text),\n  createImageNode: data => (new imageNode)(data),\n  createLayerNode: condition => (new layerNode)(condition),\n  createNativeButtonNode: (title, onClick) =>\n    (new nativeButtonNode)(title, onClick),\n};\n\nlet _nodeFactory = ref(defaultNodeFactory);\n\nlet set = factory => _nodeFactory := factory;\n\nlet get = () => _nodeFactory^;\n"
  },
  {
    "path": "src/UI_Primitives/Revery_UI_Primitives.re",
    "content": "/*\n * Revery_UI_Primitives.re\n *\n * Top-level API exposed for Revery.UI Primitives\n */\n\nmodule BoxShadow = BoxShadow;\nmodule PrimitiveNodeFactory = PrimitiveNodeFactory;\nmodule AllowPointer = AllowPointer;\nmodule IgnorePointer = IgnorePointer;\nmodule Image = Image;\nmodule Canvas = Canvas;\nmodule Layer = Layer;\nmodule Opacity = Opacity;\nmodule Padding = Padding;\nmodule Text = Text;\nmodule NativeButton = NativeButton;\nmodule View = View;\n"
  },
  {
    "path": "src/UI_Primitives/Text.re",
    "content": "open Revery_UI;\nopen Revery_Font;\nopen Style;\nopen React;\n\nlet%nativeComponent make =\n                    (\n                      ~onMouseDown=?,\n                      ~onMouseMove=?,\n                      ~onMouseUp=?,\n                      ~onMouseWheel=?,\n                      ~onMouseEnter=?,\n                      ~onMouseLeave=?,\n                      ~onMouseOver=?,\n                      ~onMouseOut=?,\n                      ~ref=?,\n                      ~style=emptyTextStyle,\n                      ~fontFamily=Family.default,\n                      ~fontWeight=Weight.Normal,\n                      ~italic=false,\n                      ~fontSize=14.,\n                      ~underlined=false,\n                      ~features=[],\n                      ~text=\"\",\n                      ~smoothing=Smoothing.default,\n                      ~children=React.empty,\n                      ~mouseBehavior=Revery_UI.Normal,\n                      (),\n                      hooks,\n                    ) => (\n  {\n    make: () => {\n      let styles = create(~style, ());\n      let events =\n        NodeEvents.make(\n          ~ref?,\n          ~onMouseDown?,\n          ~onMouseMove?,\n          ~onMouseUp?,\n          ~onMouseWheel?,\n          ~onMouseEnter?,\n          ~onMouseLeave?,\n          ~onMouseOver?,\n          ~onMouseOut?,\n          (),\n        );\n      let node = PrimitiveNodeFactory.get().createTextNode(text);\n      node#setEvents(events);\n      node#setStyle(styles);\n      node#setSmoothing(smoothing);\n      (node :> node);\n    },\n    configureInstance: (~isFirstRender as _, node) => {\n      let styles = create(~style, ());\n      let events =\n        NodeEvents.make(\n          ~ref?,\n          ~onMouseDown?,\n          ~onMouseMove?,\n          ~onMouseUp?,\n          ~onMouseWheel?,\n          ~onMouseEnter?,\n          ~onMouseLeave?,\n          ~onMouseOver?,\n          ~onMouseOut?,\n          (),\n        );\n\n      /* TODO: Proper way to downcast? */\n      let tn: textNode = Obj.magic(node);\n      tn#setEvents(events);\n      tn#setStyle(styles);\n      tn#setText(text);\n      tn#setSmoothing(smoothing);\n      tn#setMouseBehavior(mouseBehavior);\n      tn#setFontFamily(fontFamily);\n      tn#setFontWeight(fontWeight);\n      tn#setItalicized(italic);\n      tn#setFontSize(fontSize);\n      tn#setFeatures(features);\n      tn#setUnderlined(underlined);\n      node;\n    },\n    children,\n    insertNode,\n    deleteNode,\n    moveNode,\n  },\n  hooks,\n);\n"
  },
  {
    "path": "src/UI_Primitives/View.re",
    "content": "open Revery_UI;\nopen React;\n\nlet%nativeComponent make =\n                    (\n                      ~onMouseDown=?,\n                      ~onMouseMove=?,\n                      ~onMouseUp=?,\n                      ~onMouseWheel=?,\n                      ~onMouseEnter=?,\n                      ~onMouseLeave=?,\n                      ~onMouseOver=?,\n                      ~onMouseOut=?,\n                      ~onBlur=?,\n                      ~onFocus=?,\n                      ~ref=?,\n                      ~style=Style.emptyViewStyle,\n                      ~tabindex=None,\n                      ~children=React.empty,\n                      ~onKeyDown=?,\n                      ~onKeyUp=?,\n                      ~onTextInput=?,\n                      ~onTextEdit=?,\n                      ~onDimensionsChanged=?,\n                      ~onBoundingBoxChanged=?,\n                      ~onFileDropped=?,\n                      ~mouseBehavior=Revery_UI.Normal,\n                      (),\n                      hooks,\n                    ) => (\n  {\n    make: () => {\n      let styles = Style.create(~style, ());\n      let events =\n        NodeEvents.make(\n          ~ref?,\n          ~onMouseDown?,\n          ~onMouseMove?,\n          ~onMouseUp?,\n          ~onMouseWheel?,\n          ~onMouseEnter?,\n          ~onMouseLeave?,\n          ~onMouseOver?,\n          ~onMouseOut?,\n          ~onBlur?,\n          ~onFocus?,\n          ~onKeyDown?,\n          ~onKeyUp?,\n          ~onTextEdit?,\n          ~onTextInput?,\n          ~onDimensionsChanged?,\n          ~onBoundingBoxChanged?,\n          ~onFileDropped?,\n          (),\n        );\n      let node = PrimitiveNodeFactory.get().createViewNode();\n      node#setEvents(events);\n      node#setStyle(styles);\n      node#setTabIndex(tabindex);\n      node;\n    },\n    configureInstance: (~isFirstRender as _, node) => {\n      let styles = Style.create(~style, ());\n      let events =\n        NodeEvents.make(\n          ~ref?,\n          ~onMouseDown?,\n          ~onMouseMove?,\n          ~onMouseUp?,\n          ~onMouseWheel?,\n          ~onMouseEnter?,\n          ~onMouseLeave?,\n          ~onMouseOver?,\n          ~onMouseOut?,\n          ~onBlur?,\n          ~onFocus?,\n          ~onKeyDown?,\n          ~onKeyUp?,\n          ~onTextEdit?,\n          ~onTextInput?,\n          ~onBoundingBoxChanged?,\n          ~onDimensionsChanged?,\n          ~onFileDropped?,\n          (),\n        );\n      node#setEvents(events);\n      node#setStyle(styles);\n      node#setTabIndex(tabindex);\n      node#setMouseBehavior(mouseBehavior);\n      node;\n    },\n    children,\n    insertNode,\n    deleteNode,\n    moveNode,\n  },\n  hooks,\n);\n"
  },
  {
    "path": "src/UI_Primitives/dune",
    "content": "(library\n (name Revery_UI_Primitives)\n (public_name Revery.UI_Primitives)\n (preprocess\n  (pps brisk-reconciler.ppx))\n (libraries lwt lwt.unix sdl2 flex Revery_Core Revery_Math Revery_UI\n   Revery_IO))\n"
  },
  {
    "path": "src/Utility/HeadlessWindow.re",
    "content": "open Revery_Core;\nopen Revery_Draw;\nopen Revery_UI;\n\ntype t = {\n  backgroundColor: Color.t,\n  width: int,\n  height: int,\n  surface: Skia.Surface.t,\n  canvasContext: CanvasContext.t,\n  rootNode: viewNode,\n  container: ref(Container.t),\n};\n\nlet create = (options: WindowCreateOptions.t) => {\n  let imageInfo =\n    Skia.ImageInfo.make(\n      options.width |> Int32.of_int,\n      options.height |> Int32.of_int,\n      Rgba8888,\n      Premul,\n      None,\n    );\n  let surface = Skia.Surface.makeRaster(imageInfo, 0, None) |> Option.get;\n  let canvasContext = CanvasContext.createFromSurface(surface);\n\n  let rootNode = (new viewNode)();\n\n  let container = ref(Container.create(rootNode));\n\n  {\n    backgroundColor: options.backgroundColor,\n    width: options.width,\n    height: options.height,\n    surface,\n    canvasContext,\n    rootNode,\n    container,\n  };\n};\n\nlet render = (window: t, elem) => {\n  let {backgroundColor, width, height, rootNode, container, canvasContext, _} = window;\n\n  container := Container.update(container^, elem);\n\n  CanvasContext.clear(~color=backgroundColor |> Color.toSkia, canvasContext);\n\n  rootNode#setStyle(\n    Style.make(~position=LayoutTypes.Relative, ~width, ~height, ()),\n  );\n\n  Layout.layout(~force=false, rootNode);\n  rootNode#recalculate();\n  rootNode#flushCallbacks();\n\n  let skiaRoot = Skia.Matrix.makeScale(1.0, 1.0, 0., 0.);\n  CanvasContext.setRootTransform(skiaRoot, canvasContext);\n  let drawContext =\n    NodeDrawContext.create(\n      ~canvasScalingFactor=1.0,\n      ~dpi=1.0,\n      ~debug=false,\n      ~canvas=canvasContext,\n      ~zIndex=0,\n      ~opacity=1.0,\n      (),\n    );\n\n  rootNode#draw(drawContext);\n};\n\nlet takeScreenshot = ({surface, _}, path) => {\n  let image = Skia.Surface.makeImageSnapshot(surface);\n  let data = Skia.Image.encodeToData(image);\n  let dataString = Skia.Data.makeString(data);\n  let fileOutputChannel = open_out_bin(path);\n  output_string(fileOutputChannel, dataString);\n  close_out(fileOutputChannel);\n};\n"
  },
  {
    "path": "src/Utility/HeadlessWindow.rei",
    "content": "type t;\n\nlet create: Revery_Core.WindowCreateOptions.t => t;\n\nlet render: (t, Revery_UI.element) => unit;\n\nlet takeScreenshot: (t, string) => unit;\n"
  },
  {
    "path": "src/Utility/Revery_Utility.re",
    "content": "module HeadlessWindow = HeadlessWindow;\n"
  },
  {
    "path": "src/Utility/dune",
    "content": "(library\n (name Revery_Utility)\n (public_name Revery.Utility)\n (preprocess\n  (pps ppx_deriving.show))\n (libraries threads str lwt sdl2 skia flex Rench re Revery_Core Revery_UI\n   Revery_TextWrap timber))\n"
  },
  {
    "path": "src/dune",
    "content": "(library\n (name Revery)\n (public_name Revery)\n (preprocess\n  (pps brisk-reconciler.ppx lwt_ppx))\n (libraries lwt lwt.unix sdl2 Revery_Core Revery_Font Revery_Draw Revery_Math\n   Revery_UI Revery_UI_Components Revery_Native Revery_UI_Primitives\n   Revery_UI_Hooks Revery_Utility))\n\n(documentation\n (package Revery)\n (mld_files index))\n"
  },
  {
    "path": "src/index.mld",
    "content": "{%html:\n<a href=\"https://github.com/revery-ui/revery/blob/master/src/index.mld\" class=\"github-corner\" aria-label=\"View source on GitHub\"><svg width=\"80\" height=\"80\" viewBox=\"0 0 250 250\" style=\"fill:#70B7FD; color:#fff; position: absolute; top: 0; border: 0; right: 0;\" aria-hidden=\"true\"><path d=\"M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z\"></path><path d=\"M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2\" fill=\"currentColor\" style=\"transform-origin: 130px 106px;\" class=\"octo-arm\"></path><path d=\"M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z\" fill=\"currentColor\" class=\"octo-body\"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>\n%}\n\n{1 Revery}\n\nFramework for fast, native code, cross-platform GUI applications.\n\n{%html:\n<nav class=\"toc\">\n%}\n{ul\n  {li The Basics}\n  {li\n    {ul\n                {li {{:#Overview} Overview}}\n                {li {{:#Quickstart} Quickstart}}\n                {li {{:#ComponentModel} Component Model}}\n                {li\n                    {ul\n                        {li {{:#BasicFunctionalComponent} Basic Components}}\n                        {li {{:#HooksComponent} Components with Hooks}}\n                    }\n                }\n            }\n    }\n  {li API}\n  {li\n    {ul\n                {li {{:../Revery/Revery_Core/App/index.html} App}}\n                {li {{:../Revery/Revery_Core/Colors/index.html} Colors}}\n                {li {{:../Revery/Revery_Core/Environment/index.html} Environment}}\n                {li {{:../Revery/Revery_Core/Key/index.html} Key}}\n                {li {{:../Revery/Revery_Core/MouseButton/index.html} MouseButton}}\n                {li {{:../Revery/Revery_Core/MouseCursors/index.html} MouseCursors}}\n                {li {{:../Revery/Revery_Core/Tick/index.html} Tick}}\n                {li {{:../Revery/Revery_Core/Time/index.html} Time}}\n                {li {{:../Revery/Revery_Core/Window/index.html} Window}}\n                {li {{:../Revery/Revery_Core/WindowCreateOptions/index.html} WindowCreateOptions}}\n            }\n    }\n  {li Components}\n  {li\n    {ul\n                {li {{:../Revery/Revery_UI_Components/Button/index.html} Button}}\n                {li {{:../Revery/Revery_UI_Components/Checkbox/index.html} Checkbox}}\n                {li {{:../Revery/Revery_UI_Components/Clickable/index.html} Clickable}}\n                {li {{:../Revery/Revery_UI_Components/ClipContainer/index.html} ClipContainer}}\n                {li {{:../Revery/Revery_UI_Components/Container/index.html} Container}}\n                {li {{:../Revery/Revery_UI_Components/Dropdown/index.html} Dropdown}}\n                {li {{:../Revery/Revery_UI_Components/Input/index.html} Input}}\n                {li {{:../Revery/Revery_UI_Components/Positioned/index.html} Positioned}}\n                {li {{:../Revery/Revery_UI_Components/RadioButtons/index.html} RadioButtons}}\n                {li {{:../Revery/Revery_UI_Components/ScrollView/index.html} ScrollView}}\n                {li {{:../Revery/Revery_UI_Components/Slider/index.html} Slider}}\n                {li {{:../Revery/Revery_UI_Components/Stack/index.html} Stack}}\n                {li {{:../Revery/Revery_UI_Components/Ticker/index.html} Ticker}}\n                {li {{:../Revery/Revery_UI_Components/Tree/index.html} Tree}}\n            }\n    }\n  {li Hooks}\n  {li\n    {ul\n                {li {{:../Revery/Revery_UI_Hooks/index.html#val-animation} animation}}\n                {li {{:../Revery/Revery_UI_Hooks__/Effect/index.html} effect}}\n                {li {{:../Revery/Revery_UI_Hooks__/Reducer/index.html} reducer}}\n                {li {{:../Revery/Revery_UI_Hooks__/Ref/index.html} ref}}\n                {li {{:../Revery/Revery_UI_Hooks__/State/index.html} state}}\n                {li {{:../Revery/Revery_UI_Hooks__/Tick/index.html} tick}}\n                {li {{:../Revery/Revery_UI_Hooks/index.html#val-transition} transition}}\n    }\n  }\n  {li Math}\n  {li\n    {ul\n                {li {{:../Revery/Revery_Math/BoundingBox2d/index.html} BoundingBox2d}}\n    }\n  }\n  {li Platform}\n  {li\n    {ul\n                {li {{:../Revery/Revery/Platform/index.html} Dialog}}\n    }\n  }\n}\n{%html:\n</nav>\n%}\n\n{2:Overview Overview}\n\nRevery is a framework for building cross-platform GUI applications. Revery provides a React-like, functional approach for modeling UI, as well as scaffolding for managing the application lifecycle.\n\nRevery started as the foundation of {{:https://v2.onivim.io} Onivim 2}, but was factored out into a general toolkit for {{:https://reasonml.github.io} ReasonML} user interfaces.\n\n{2:Quickstart Quickstart}\n\nThere are two ways to get started:\n{ul\n{li  Try out the revery {{:https://www.outrunlabs.com/revery/playground} Playground}}\n{li Clone and run {{:https://github.com/revery-ui/revery-quick-start} revery-quick-start}\n\n        [git clone https://github.com/revery-ui/revery-quick-start]\n\n        [cd revery-quick-start]\n\n        [esy install]\n\n        [esy build]\n\n        [esy run]\n}\n}\n\n{2:ComponentModel Component Model}\n\n{3:BasicFunctionalComponent Basic Components}\n\nThe basic component building block is simply a {b pure function}, that returns an {e element}.\n\nExample:\n\n{[\n    let squareBox = () => <Container width=10 height=10 color=Colors.red />;\n]}\n\nProperties can be specified as {b named arguments}.\n\nExample:\n\n{[\n    let wideBox = (~width: int, ()) => <Container width height=10 color=Colors.red />;\n]}\n\nYou can accept children through through the [~children] property, for example:\n\n{[\n    let boxWithChildren = (~children, ()) => <Container width=10 height=10 color=Colors.red>children</Container>;\n]}\n\n{3:HooksComponent Components with Hooks}\n\nFor components that manage state, or have side effects, you'll want to encapsulate those using {b hooks}!\n\nA component that uses hooks can be created using [let%component], for example:\n\n{[\n    let%component componentWithHooks = (~children, ()) => {\n      let%hook (state, setState) = Hooks.state(0);\n\n      <Button text=string_of_int(state) onClick={(_) => setState(state => state + 1)}/>;\n    };\n]}\n\nHooks components still return a function with named arguments representing the properties, but behind the scenes a hooks component is a {i function that takes a hooks object}, and then {i returns a tuple of [(hooks, element)]}. This complexity is hidden away for you using syntax sugar in a manner similar to how async/await hides promises in JavaScript for example.\n\nIn effect, Revery components should always be pure functions - given a set of props, and a hooks, the output should always be the same.\n"
  },
  {
    "path": "test/Core/ColorTests.re",
    "content": "open Revery_Core;\n\nopen TestFramework;\n\ndescribe(\"Color\", ({describe, _}) => {\n  describe(\"hex parsing\", ({test, _}) => {\n    test(\"16-bit RGB cases\", ({expect, _}) => {\n      let color1 = Color.hex(\"#000\");\n      let color2 = Color.hex(\"#F00\");\n      let color3 = Color.hex(\"#0F0\");\n      let color4 = Color.hex(\"#00F\");\n      let color5 = Color.hex(\"#FFF\");\n\n      expect.equal(color1, Color.rgb(0., 0., 0.));\n      expect.equal(color2, Color.rgb(1., 0., 0.));\n      expect.equal(color3, Color.rgb(0., 1., 0.));\n      expect.equal(color4, Color.rgb(0., 0., 1.));\n      expect.equal(color5, Color.rgb(1., 1., 1.));\n    });\n\n    test(\"16-bit RGB w/ alpha\", ({expect, _}) => {\n      let color1 = Color.hex(\"#0000\");\n      let color2 = Color.hex(\"#000F\");\n      let color3 = Color.hex(\"#000FF\");\n\n      expect.equal(color1, Color.rgba(0., 0., 0., 0.));\n      expect.equal(color2, Color.rgba(0., 0., 0., 1.));\n      expect.equal(color3, Color.rgba(0., 0., 0., 1.0));\n    });\n\n    test(\"256-bit RGB cases\", ({expect, _}) => {\n      let color1 = Color.hex(\"#000000\");\n      let color2 = Color.hex(\"#FF0000\");\n      let color3 = Color.hex(\"#00FF00\");\n      let color4 = Color.hex(\"#0000FF\");\n      let color5 = Color.hex(\"#FFFFFF\");\n\n      expect.equal(color1, Color.rgb(0., 0., 0.));\n      expect.equal(color2, Color.rgb(1., 0., 0.));\n      expect.equal(color3, Color.rgb(0., 1., 0.));\n      expect.equal(color4, Color.rgb(0., 0., 1.));\n      expect.equal(color5, Color.rgb(1., 1., 1.));\n    });\n\n    test(\"256-bit RGB cases w/ alpha\", ({expect, _}) => {\n      let color1 = Color.hex(\"#000000F\");\n      let color2 = Color.hex(\"#FF0000FF\");\n      let color3 = Color.hex(\"#0000000\");\n      let color4 = Color.hex(\"#FF000000\");\n\n      expect.equal(color1, Color.rgba(0., 0., 0., 1.0));\n      expect.equal(color2, Color.rgba(1., 0., 0., 1.0));\n      expect.equal(color3, Color.rgba(0., 0., 0., 0.));\n      expect.equal(color4, Color.rgba(1., 0., 0., 0.0));\n    });\n  });\n\n  describe(\"mix\", ({test, _}) => {\n    test(\"amount = 0\", ({expect, _}) => {\n      let start = Colors.red;\n      let stop = Colors.lime;\n\n      expect.equal(Color.mix(~start, ~stop, ~amount=0.), Colors.red);\n    });\n\n    test(\"amount = 1\", ({expect, _}) => {\n      let start = Colors.red;\n      let stop = Colors.lime;\n\n      expect.equal(Color.mix(~start, ~stop, ~amount=1.), Colors.lime);\n    });\n\n    test(\"amount = 0.5\", ({expect, _}) => {\n      let start = Colors.red;\n      let stop = Colors.lime;\n\n      let mix = Color.mix(~start, ~stop, ~amount=0.5);\n      let rgba = Color.rgba(0.5, 0.5, 0., 1.);\n      expect.bool(Color.equals(mix, rgba)).toBe(true);\n    });\n  });\n});\n"
  },
  {
    "path": "test/Core/EnvironmentTests.re",
    "content": "open Revery_Core;\n\nopen TestFramework;\n\ndescribe(\"Environment\", ({test, _}) => {\n  test(\"executingDirectory\", _ =>\n    test(\n      \"validate we can load a file adjacent to the executable\", ({expect, _}) => {\n      expect.bool(\n        Sys.file_exists(Environment.executingDirectory ++ \"test-asset.txt\"),\n      ).\n        toBeTrue()\n    })\n  );\n  test(\"userLocale\", ({expect, _}) => {\n    expect.equal(~equals=(!=), Environment.userLocale, \"\")\n  });\n});\n"
  },
  {
    "path": "test/Core/TestFramework.re",
    "content": "include Rely.Make({\n  let config =\n    Rely.TestFrameworkConfig.initialize({\n      snapshotDir: \"test/Core/__snapshots__\",\n      projectDir: \"\",\n    });\n});\n"
  },
  {
    "path": "test/Core/TextTests.re",
    "content": "open Revery_Core;\nopen TestFramework;\n\nlet font =\n  Revery_Font.Family.fromFile(\"Roboto-Regular.ttf\")\n  |> Revery_Font.Family.toSkia(Revery_Font.Weight.Normal)\n  |> Revery_Font.load\n  |> Result.get_ok;\n\ndescribe(\"TextOverflow\", ({test, _}) => {\n  test(\"handle overflow UTF-8\", ({expect, _}) => {\n    let measure = str =>\n      Revery_Font.measure(\n        ~smoothing=Revery_Font.Smoothing.default,\n        font,\n        12.0,\n        str,\n      ).\n        width;\n\n    let abcSize = measure(\"ABC\");\n    let overflown =\n      TextOverflow.handleOverflow(\n        ~maxWidth=abcSize,\n        ~text=\"ABC😀\",\n        ~measure,\n        (),\n      );\n    expect.equal(\"A…\", overflown);\n  })\n});\n"
  },
  {
    "path": "test/Core/TickTest.re",
    "content": "open Revery_Core;\n\nopen TestFramework;\n\nmodule TestTicker = {\n  let _time: ref(Time.t) = ref(Time.zero);\n\n  let incrementTime = (time: Time.t) => {\n    _time := Time.(_time^ + time);\n  };\n\n  let time = () => _time^;\n};\n\nmodule Tick = Revery_Core.Internal.Tick.Make(TestTicker);\n\ndescribe(\"Ticker\", ({describe, _}) => {\n  describe(\"timeout\", ({test, _}) => {\n    test(\"calls once after tick time\", ({expect, _}) => {\n      let callCount = ref(0);\n      let _ignore: unit => unit =\n        Tick.timeout(\n          ~name=\"Test timeout\",\n          () => incr(callCount),\n          Time.seconds(1),\n        );\n      TestTicker.incrementTime(Time.ms(1010));\n      Tick.pump();\n\n      expect.int(callCount^).toBe(1);\n\n      // Incrementing again, the timeout should've expired - so no additional increment\n      TestTicker.incrementTime(Time.ms(1010));\n      Tick.pump();\n\n      expect.int(callCount^).toBe(1);\n    });\n    test(\n      \"nested tick - tick gets scheduled when called from inside tick\",\n      ({expect, _}) => {\n      let outerCallCount = ref(0);\n      let innerCallCount = ref(0);\n\n      let _: unit => unit =\n        Tick.timeout(\n          ~name=\"Outer timeout\",\n          () => {\n            incr(outerCallCount);\n            let _: unit => unit =\n              Tick.timeout(\n                ~name=\"Inner timeout\",\n                () => incr(innerCallCount),\n                Time.ms(110),\n              );\n            ();\n          },\n          Time.zero,\n        );\n      TestTicker.incrementTime(Time.ms(110));\n      Tick.pump();\n\n      expect.int(outerCallCount^).toBe(1);\n      expect.int(innerCallCount^).toBe(0);\n\n      TestTicker.incrementTime(Time.ms(110));\n      Tick.pump();\n\n      expect.int(outerCallCount^).toBe(1);\n      expect.int(innerCallCount^).toBe(1);\n\n      // Incrementing again, the timeout should've expired - so no additional increment\n      TestTicker.incrementTime(Time.ms(1010));\n      Tick.pump();\n\n      expect.int(outerCallCount^).toBe(1);\n      expect.int(innerCallCount^).toBe(1);\n    });\n    test(\"doesn't call if canceled\", ({expect, _}) => {\n      let callCount = ref(0);\n      let cancel =\n        Tick.timeout(\n          ~name=\"TestTick\",\n          () => incr(callCount),\n          Time.seconds(1),\n        );\n      TestTicker.incrementTime(Time.ms(500));\n      Tick.pump();\n\n      cancel();\n      TestTicker.incrementTime(Time.ms(510));\n      Tick.pump();\n\n      expect.int(callCount^).toBe(0);\n    });\n  });\n  describe(\"interval\", ({test, _}) => {\n    test(\"calls after tick time\", ({expect, _}) => {\n      let callCount = ref(0);\n\n      let _: unit => unit =\n        Tick.interval(\n          ~name=\"TestInterval\",\n          _ => callCount := callCount^ + 1,\n          Time.seconds(1),\n        );\n\n      TestTicker.incrementTime(Time.ms(1010));\n\n      expect.int(callCount^).toBe(0);\n      Tick.pump();\n      expect.int(callCount^).toBe(1);\n\n      Tick.pump();\n      expect.int(callCount^).toBe(1);\n\n      TestTicker.incrementTime(Time.ms(900));\n      Tick.pump();\n      expect.int(callCount^).toBe(1);\n\n      TestTicker.incrementTime(Time.ms(110));\n      Tick.pump();\n      expect.int(callCount^).toBe(2);\n    });\n\n    test(\"disposing tick subscription stops the tick\", ({expect, _}) => {\n      let callCount = ref(0);\n\n      let stop: unit => unit =\n        Tick.interval(\n          ~name=\"TestTick\",\n          _ => callCount := callCount^ + 1,\n          Time.seconds(1),\n        );\n\n      TestTicker.incrementTime(Time.ms(1010));\n\n      expect.int(callCount^).toBe(0);\n      Tick.pump();\n      expect.int(callCount^).toBe(1);\n\n      stop();\n\n      TestTicker.incrementTime(Time.seconds(2));\n      Tick.pump();\n      expect.int(callCount^).toBe(1);\n    });\n  });\n});\n"
  },
  {
    "path": "test/Core/dune",
    "content": "(library\n (name Revery_Core_Test)\n (library_flags\n  (-linkall -g))\n (modules (:standard))\n (libraries Revery_Core Revery_Font rely.lib))\n"
  },
  {
    "path": "test/Font/FamilyTest.re",
    "content": "open Revery_Font;\nopen TestFramework;\nopen Skia;\n\nlet getInfoFromSkia = family => {\n  let maybeSkia = family |> Family.toSkia(Normal);\n\n  let maybeName = maybeSkia |> Option.map(tf => Typeface.getFamilyName(tf));\n\n  (maybeSkia, maybeName);\n};\n\ndescribe(\"Family\", ({test, _}) => {\n  test(\"fromFile\", ({expect, _}) => {\n    let family = Family.fromFile(\"Inconsolata.otf\");\n    let (maybeSkia, maybeName) = getInfoFromSkia(family);\n\n    expect.option(maybeSkia).toBeSome();\n    expect.option(maybeName).toBe(Some(\"Inconsolata\"));\n  });\n\n  test(\"default\", ({expect, _}) => {\n    let (maybeSkia, _) = getInfoFromSkia(Family.default);\n\n    expect.option(maybeSkia).toBeSome();\n  });\n\n  test(\"defaultMono\", ({expect, _}) => {\n    let (maybeSkia, _) = getInfoFromSkia(Family.defaultMono);\n\n    expect.option(maybeSkia).toBeSome();\n  });\n\n  test(\"defaultSerif\", ({expect, _}) => {\n    let (maybeSkia, _) = getInfoFromSkia(Family.defaultSerif);\n\n    expect.option(maybeSkia).toBeSome();\n  });\n});\n"
  },
  {
    "path": "test/Font/FontCacheTest.re",
    "content": "open Revery_Font;\nopen TestFramework;\n\nlet glyphId = (index, (_typeface, glyphs)) => {\n  // Each glyph is 2 bytes\n  let b0 = glyphs.[index * 2] |> Char.code;\n  let b1 = glyphs.[index * 2 + 1] |> Char.code;\n  b1 lsl 8 + b0;\n};\n\nlet runCount = glyphStrings => List.length(glyphStrings);\n\nlet glyphCount = ((_typeface, glyphs)) =>\n  // Each glyph is 2 bytes\n  String.length(glyphs) / 2;\n\nlet run = (index, runs) => List.nth(runs, index);\n\nlet typefaceId = ((typeface, _glyphs)) =>\n  typeface |> Skia.Typeface.getUniqueID |> Int32.to_int;\n\ndescribe(\"FontCache\", ({describe, test, _}) => {\n  // TODO: This font is dependent on system defaults,\n  // so is not really reliable for testing, especially\n  // for extended character sets.\n  let defaultFont =\n    Family.default\n    |> Family.resolve(~italic=false, Weight.Normal)\n    |> Result.get_ok;\n\n  let defaultFontId =\n    defaultFont\n    |> FontCache.getSkiaTypeface\n    |> Skia.Typeface.getUniqueID\n    |> Int32.to_int;\n\n  let firaCodeFont = Family.fromFile(\"FiraCode-Regular.ttf\");\n\n  let jetBrainsMonoFont = Family.fromFile(\"JetBrainsMono-Regular.ttf\");\n\n  test(\"empty string has empty shapes\", ({expect, _}) => {\n    let {glyphStrings}: ShapeResult.t = \"\" |> FontCache.shape(defaultFont);\n\n    expect.int(glyphStrings |> runCount).toBe(0);\n  });\n\n  test(\n    \"fallback for all ASCII characters - including non-printable characters\",\n    ({expect, _}) => {\n    for (ascii in 0 to 255) {\n      let asciiCharacter = Zed_utf8.make(1, Uchar.of_int(ascii));\n      let {glyphStrings}: ShapeResult.t =\n        asciiCharacter |> FontCache.shape(defaultFont);\n\n      expect.int(glyphStrings |> runCount).toBe(1);\n      // TODO: Investigate why we sometimes get 2 glyph strings here?\n      // expect.int(glyphStrings |> run(0) |> glyphCount).toBe(1);\n    }\n  });\n\n  test(\"shape simple CJK text\", ({expect, _}) => {\n    let {glyphStrings}: ShapeResult.t =\n      \"腐\" |> FontCache.shape(defaultFont);\n\n    // TODO: This test fails on Ubuntu CI, because it the default\n    // font does not support CJK shaping:\n    if (Sys.unix) {\n      ();\n    } else {\n      expect.int(glyphStrings |> runCount).toBe(1);\n      expect.int(glyphStrings |> run(0) |> glyphCount).toBe(1);\n    };\n  });\n\n  test(\"shape simple ASCII string\", ({expect, _}) => {\n    let {glyphStrings}: ShapeResult.t =\n      \"abc\" |> FontCache.shape(defaultFont);\n\n    expect.int(glyphStrings |> runCount).toBe(1);\n    expect.int(glyphStrings |> run(0) |> glyphCount).toBe(3);\n  });\n\n  test(\"shape string w/ fallback\", ({expect, _}) => {\n    let {glyphStrings}: ShapeResult.t =\n      \"a⌋\" |> FontCache.shape(defaultFont);\n\n    expect.int(glyphStrings |> runCount).toBe(2);\n    expect.int(glyphStrings |> run(0) |> glyphCount).toBe(1);\n    expect.int(glyphStrings |> run(0) |> typefaceId).toBe(defaultFontId);\n\n    expect.int(glyphStrings |> run(1) |> glyphCount).toBe(1);\n    expect.int(glyphStrings |> run(1) |> typefaceId).not.toBe(\n      defaultFontId,\n    );\n  });\n\n  test(\"fallback first, then shape\", ({expect, _}) => {\n    let {glyphStrings}: ShapeResult.t =\n      \"⌋a\" |> FontCache.shape(defaultFont);\n\n    expect.int(glyphStrings |> runCount).toBe(2);\n    expect.int(glyphStrings |> run(0) |> glyphCount).toBe(1);\n    expect.int(glyphStrings |> run(0) |> typefaceId).not.toBe(\n      defaultFontId,\n    );\n\n    expect.int(glyphStrings |> run(1) |> glyphCount).toBe(1);\n    expect.int(glyphStrings |> run(1) |> typefaceId).toBe(defaultFontId);\n  });\n\n  test(\"non-fallback surrounded by holes (onivim/oni2#2178)\", ({expect, _}) => {\n    let {glyphStrings}: ShapeResult.t =\n      \"⌋a⌋\" |> FontCache.shape(defaultFont);\n\n    expect.int(glyphStrings |> runCount).toBe(3);\n    expect.int(glyphStrings |> run(0) |> glyphCount).toBe(1);\n    expect.int(glyphStrings |> run(0) |> typefaceId).not.toBe(\n      defaultFontId,\n    );\n\n    expect.int(glyphStrings |> run(1) |> glyphCount).toBe(1);\n    expect.int(glyphStrings |> run(1) |> typefaceId).toBe(defaultFontId);\n\n    expect.int(glyphStrings |> run(2) |> glyphCount).toBe(1);\n    expect.int(glyphStrings |> run(2) |> typefaceId).not.toBe(\n      defaultFontId,\n    );\n  });\n\n  // Test two fonts with known glyph ids to exercise fallback and hole resolution\n  // This is useful because FiraCode supports some glyphs that JetBrains does not,\n  // and gives us known glyphIds to verify.\n  describe(\"JetBrains-Mono -> FiraCode fallback\", ({test, _}) => {\n    let fallbackFont =\n      firaCodeFont\n      |> Family.toSkia(~italic=false, Weight.Normal)\n      |> Option.get;\n\n    let firaCodeFontId =\n      firaCodeFont\n      |> Family.resolve(~italic=false, Weight.Normal)\n      |> Result.get_ok\n      |> FontCache.getSkiaTypeface\n      |> Skia.Typeface.getUniqueID\n      |> Int32.to_int;\n\n    // Use a fallback strategy that _always_ uses FiraCode,\n    // so we can test specific fallback cases + glyph resolution.\n    let fallback = FontCache.Fallback.constant(fallbackFont);\n\n    let font =\n      jetBrainsMonoFont\n      |> Family.resolve(~italic=false, Weight.Normal)\n      |> Result.get_ok;\n\n    let jetBrainsFontId =\n      font\n      |> FontCache.getSkiaTypeface\n      |> Skia.Typeface.getUniqueID\n      |> Int32.to_int;\n\n    test(\n      \"onivim/oni2#2286: fallback for tab character - handle case where fallback font also does not have a glyph for tab\",\n      ({expect, _}) => {\n        let asciiCharacter = Zed_utf8.make(1, Uchar.of_int(9));\n        let {glyphStrings}: ShapeResult.t =\n          asciiCharacter |> FontCache.shape(~fallback, font);\n\n        expect.int(glyphStrings |> runCount).toBe(1);\n      },\n    );\n\n    test(\"κόσμε\", ({expect, _}) => {\n      let {glyphStrings}: ShapeResult.t =\n        \"κόσμε\" |> FontCache.shape(~fallback, font);\n\n      expect.int(glyphStrings |> runCount).toBe(3);\n      expect.int(glyphStrings |> run(0) |> glyphCount).toBe(3);\n      expect.int(glyphStrings |> run(0) |> glyphId(0)).toBe(724); //κ\n      expect.int(glyphStrings |> run(0) |> glyphId(1)).toBe(854); //ό\n      expect.int(glyphStrings |> run(0) |> glyphId(2)).toBe(733); //σ\n      expect.int(glyphStrings |> run(0) |> typefaceId).toBe(firaCodeFontId);\n\n      expect.int(glyphStrings |> run(1) |> glyphCount).toBe(1);\n      expect.int(glyphStrings |> run(1) |> glyphId(0)).toBe(526); //μ\n      expect.int(glyphStrings |> run(1) |> typefaceId).toBe(\n        jetBrainsFontId,\n      );\n\n      expect.int(glyphStrings |> run(2) |> glyphCount).toBe(1);\n      expect.int(glyphStrings |> run(2) |> glyphId(0)).toBe(719); //ε\n      expect.int(glyphStrings |> run(2) |> typefaceId).toBe(firaCodeFontId);\n    });\n    test(\"alternate fall-back\", ({expect, _}) => {\n      let {glyphStrings}: ShapeResult.t =\n        \"aκaκaκaκaκ\" |> FontCache.shape(~fallback, font);\n\n      // Validate each aκ pair - there are 5 pairs (10 characters)\n      expect.int(glyphStrings |> runCount).toBe(10);\n      for (i in 0 to 4) {\n        expect.int(glyphStrings |> run(i * 2) |> glyphCount).toBe(1);\n        expect.int(glyphStrings |> run(i * 2) |> glyphId(0)).toBe(42); //a\n        expect.int(glyphStrings |> run(i * 2) |> typefaceId).toBe(\n          jetBrainsFontId,\n        );\n\n        expect.int(glyphStrings |> run(i * 2 + 1) |> glyphCount).toBe(1);\n        expect.int(glyphStrings |> run(i * 2 + 1) |> glyphId(0)).toBe(724); //κ\n        expect.int(glyphStrings |> run(i * 2 + 1) |> typefaceId).toBe(\n          firaCodeFontId,\n        );\n      };\n    });\n    test(\"shouldn't fall-back for ascii characters\", ({expect, _}) => {\n      // Use a ref as a sensor to see if fallback is requested\n      let fallbackSensor = ref(0);\n      let fallback =\n        FontCache.Fallback.custom(_uchar => {\n          incr(fallbackSensor);\n          None;\n        });\n\n      for (ascii in 0 to 255) {\n        let asciiCharacter = Zed_utf8.make(1, Uchar.of_int(ascii));\n        let {glyphStrings}: ShapeResult.t =\n          asciiCharacter |> FontCache.shape(~fallback, font);\n\n        expect.int(glyphStrings |> runCount).toBe(1);\n\n        expect.int(fallbackSensor^).toBe(0);\n      };\n    });\n    test(\n      \"heart-on-fire case: hole with unresolved glyph in middle shouldn't hang\",\n      ({expect, _}) => {\n      // Regression Test:\n      // From the heart-on-fire test case: https://unicode.org/Public/emoji/13.1/emoji-test.txt\n      // We don't handle it quite correctly currently, but we certainly shouldn't hang!\n\n      let str =\n        Zed_utf8.make(1, Uchar.of_int(0x2764))\n        ++ Zed_utf8.make(1, Uchar.of_int(0xFE0F))\n        ++ Zed_utf8.make(1, Uchar.of_int(0x200D))\n        ++ Zed_utf8.make(1, Uchar.of_int(0x1F525));\n\n      let {glyphStrings}: ShapeResult.t =\n        FontCache.shape(~fallback, font, str);\n\n      // ...really just making sure we didn't hang.\n      expect.equal(true, glyphStrings |> runCount > 1);\n    });\n  });\n});\n"
  },
  {
    "path": "test/Font/TestFramework.re",
    "content": "include Rely.Make({\n  let config =\n    Rely.TestFrameworkConfig.initialize({\n      snapshotDir: \"./__snapshots__\",\n      projectDir: \"\",\n    });\n});\n"
  },
  {
    "path": "test/Font/dune",
    "content": "(library\n (name Revery_Font_Test)\n (library_flags\n  (-linkall -g))\n (modules (:standard))\n (libraries Revery_Font skia rely.lib))\n"
  },
  {
    "path": "test/Math/BoundingBox2dTests.re",
    "content": "open Revery_Math;\n\nopen TestFramework;\n\ndescribe(\"BoundingBox2d\", ({test, _}) => {\n  test(\"isPointInside\", ({expect, _}) => {\n    let bbox = BoundingBox2d.create(0., 0., 400., 600.);\n\n    expect.bool(BoundingBox2d.isPointInside(~x=1., ~y=1., bbox)).toBeTrue();\n    expect.bool(BoundingBox2d.isPointInside(~x=400., ~y=600., bbox)).toBeTrue();\n    expect.bool(BoundingBox2d.isPointInside(~x=399., ~y=599., bbox)).toBeTrue();\n\n    expect.bool(BoundingBox2d.isPointInside(~x=401., ~y=599., bbox)).\n      toBeFalse();\n    expect.bool(BoundingBox2d.isPointInside(~x=399., ~y=601., bbox)).\n      toBeFalse();\n    expect.bool(BoundingBox2d.isPointInside(~x=-1., ~y=-1., bbox)).toBeFalse();\n  });\n\n  describe(\"intersection\", ({test, _}) => {\n    test(\"intersects\", ({expect, _}) => {\n      let bbox1 = BoundingBox2d.create(5., 5., 10., 10.);\n      let bbox2 = BoundingBox2d.create(0., 0., 4., 4.);\n\n      expect.bool(BoundingBox2d.intersects(bbox1, bbox2)).toBeFalse();\n\n      let leftIntersect = BoundingBox2d.create(4., 4., 6., 6.);\n\n      expect.bool(BoundingBox2d.intersects(bbox1, leftIntersect)).toBeTrue();\n\n      let topIntersect = BoundingBox2d.create(6., 4., 8., 6.);\n\n      expect.bool(BoundingBox2d.intersects(bbox1, topIntersect)).toBeTrue();\n\n      let rightIntersect = BoundingBox2d.create(7., 6., 11., 8.);\n\n      expect.bool(BoundingBox2d.intersects(bbox1, rightIntersect)).toBeTrue();\n\n      let bottomIntersect = BoundingBox2d.create(6., 9., 7., 11.);\n      expect.bool(BoundingBox2d.intersects(bbox1, bottomIntersect)).toBeTrue();\n    });\n    test(\"intersect\", ({expect, _}) => {\n      let areBboxEqual = (b1, b2) => {\n        BoundingBox2d.equals(b1, b2);\n      };\n      let bbox = BoundingBox2d.create(1., 1., 5., 10.);\n\n      let bbox2 = BoundingBox2d.create(2., 2., 7., 8.);\n      let intersectBbox = BoundingBox2d.create(2., 2., 5., 8.);\n\n      expect.bool(\n        areBboxEqual(BoundingBox2d.intersect(bbox, bbox2), intersectBbox),\n      ).\n        toBeTrue();\n\n      let insideBbox = BoundingBox2d.create(3., 4., 4., 7.);\n\n      expect.bool(\n        areBboxEqual(BoundingBox2d.intersect(bbox, insideBbox), insideBbox),\n      ).\n        toBeTrue();\n\n      let outsideBbox = BoundingBox2d.create(6., 11., 7., 12.);\n      let intersectOutsideBbox = BoundingBox2d.create(0., 0., 0., 0.);\n\n      expect.bool(\n        areBboxEqual(\n          BoundingBox2d.intersect(bbox, outsideBbox),\n          intersectOutsideBbox,\n        ),\n      ).\n        toBeTrue();\n    });\n  });\n\n  describe(\"transform\", ({test, _}) => {\n    test(\"translate\", ({expect, _}) => {\n      let bbox = BoundingBox2d.create(0., 0., 400., 600.);\n\n      let translate = Skia.Matrix.makeTranslate(100., 200.);\n      let tbbox = BoundingBox2d.transform(bbox, translate);\n\n      let (minX, minY, maxX, maxY) = BoundingBox2d.getBounds(tbbox);\n\n      expect.float(minX).toBeCloseTo(100.);\n      expect.float(minY).toBeCloseTo(200.);\n\n      expect.float(maxX).toBeCloseTo(500.);\n      expect.float(maxY).toBeCloseTo(800.);\n    });\n\n    test(\"scale\", ({expect, _}) => {\n      let bbox = BoundingBox2d.create(0., 0., 100., 200.);\n      let scale = Skia.Matrix.makeScale(-2., -3., 0.0, 0.0);\n\n      let tbbox = BoundingBox2d.transform(bbox, scale);\n\n      let (minX, minY, maxX, maxY) = BoundingBox2d.getBounds(tbbox);\n\n      expect.float(minX).toBeCloseTo(-200.);\n      expect.float(minY).toBeCloseTo(-600.);\n\n      expect.float(maxX).toBeCloseTo(0.);\n      expect.float(maxY).toBeCloseTo(0.);\n    });\n  });\n});\n"
  },
  {
    "path": "test/Math/ClampTest.re",
    "content": "open Revery_Math;\n\nopen TestFramework;\n\ndescribe(\"clamp\", ({test, _}) => {\n  test(\"basic cases\", ({expect, _}) => {\n    expect.float(clamp(-1., 0., 1.)).toBeCloseTo(0.);\n    expect.float(clamp(2., 0., 1.)).toBeCloseTo(1.);\n    expect.float(clamp(0.5, 0., 1.)).toBeCloseTo(0.5);\n  })\n});\n"
  },
  {
    "path": "test/Math/InterpolateTest.re",
    "content": "open Revery_Math;\n\nopen TestFramework;\n\ndescribe(\"interpolate\", ({test, _}) => {\n  test(\"basic cases\", ({expect, _}) => {\n    expect.float(interpolate(100., 200., 0.)).toBeCloseTo(100.);\n    expect.float(interpolate(100., 200., 0.5)).toBeCloseTo(150.);\n    expect.float(interpolate(-2., 0., 1.)).toBeCloseTo(0.);\n  })\n});\n"
  },
  {
    "path": "test/Math/TestFramework.re",
    "content": "include Rely.Make({\n  let config =\n    Rely.TestFrameworkConfig.initialize({\n      snapshotDir: \"test/Math/__snapshots__\",\n      projectDir: \"\",\n    });\n});\n"
  },
  {
    "path": "test/Math/dune",
    "content": "(library\n (name Revery_Math_Test)\n (library_flags\n  (-linkall -g))\n (modules (:standard))\n (libraries Revery_Math rely.lib))\n"
  },
  {
    "path": "test/TestRunner.re",
    "content": "// On Windows, because this is linked against the '-mwindows' flag\n// (for a GUI app), we need to manually allocate a console.\nif (Sys.win32) {\n  let _: int = Sdl2.Platform.win32AttachConsole();\n  // Unfortunately, the colors aren't showing up correctly with the\n  // attached console - so I'll disable them for now.\n  Pastel.setMode(Pastel.Disabled);\n};\n\nRevery_Core_Test.TestFramework.cli();\nRevery_Math_Test.TestFramework.cli();\nRevery_UI_Test.TestFramework.cli();\nRevery_Font_Test.TestFramework.cli();\n\nSkia_Test.TestFramework.cli();\n\nHarfbuzz_Test.TestFramework.cli();\n"
  },
  {
    "path": "test/UI/AnimationTest.re",
    "content": "open Revery_Core;\nopen Revery_UI;\n\nopen TestFramework;\n\n// This is just to avoid having to coerce to float for every test that uses NormalizedTime.t\nlet floatify = Animation.tween(0., 1.);\n\ndescribe(\"Animation\", ({describe, _}) => {\n  describe(\"const\", ({test, _}) => {\n    assertMany(\n      \"examples\",\n      test,\n      Animation.[\n        const(3) |> valueAt(Time.zero) == 3,\n        const(3) |> valueAt(Time.seconds(-42)) == 3,\n        const(3) |> valueAt(Time.seconds(42)) == 3,\n        const(3) |> stateAt(Time.seconds(-42)) == Complete(Time.zero),\n        const(3) |> stateAt(Time.seconds(42)) == Complete(Time.zero),\n        const(\"rhino\") |> valueAt(Time.zero) == \"rhino\",\n      ],\n    )\n  });\n\n  describe(\"animate\", ({test, _}) => {\n    assertMany(\n      \"examples\",\n      test,\n      Animation.[\n        (animate(Time.seconds(5)) |> valueAt(Time.zero) :> float) == 0.0,\n        (animate(Time.seconds(5)) |> valueAt(Time.seconds(-42)) :> float)\n        == 0.0,\n        (animate(Time.seconds(5)) |> valueAt(Time.seconds(1)) :> float)\n        == 0.2,\n        (animate(Time.seconds(5)) |> valueAt(Time.seconds(42)) :> float)\n        == 1.0,\n        animate(Time.seconds(5)) |> stateAt(Time.zero) == Running,\n        animate(Time.seconds(5)) |> stateAt(Time.seconds(-42)) == Delayed,\n        animate(Time.seconds(5)) |> stateAt(Time.seconds(1)) == Running,\n        animate(Time.seconds(5))\n        |> stateAt(Time.seconds(42)) == Complete(Time.seconds(5)),\n      ],\n    )\n  });\n\n  describe(\"delay\", ({test, _}) => {\n    test(\"basics\", ({expect, _}) => {\n      open Animation;\n\n      let anim =\n        animate(Time.seconds(1)) |> delay(Time.seconds(1)) |> floatify;\n\n      expect.float(anim |> valueAt(Time.seconds(-2))).toBeCloseTo(0.);\n      expect.float(anim |> valueAt(Time.ms(750))).toBeCloseTo(0.);\n      expect.float(anim |> valueAt(Time.ms(1500))).toBeCloseTo(0.5);\n      expect.float(anim |> valueAt(Time.ms(4660))).toBeCloseTo(1.);\n    });\n\n    assertMany(\n      \"examples\",\n      test,\n      Animation.[\n        (\n          animate(Time.seconds(1))\n          |> delay(Time.seconds(1))\n          |> valueAt(Time.seconds(-2)) :> float\n        )\n        == 0.0,\n        (\n          animate(Time.seconds(1))\n          |> delay(Time.seconds(1))\n          |> valueAt(Time.ms(750)) :> float\n        )\n        == 0.0,\n        (\n          animate(Time.seconds(1))\n          |> delay(Time.seconds(1))\n          |> valueAt(Time.ms(1500)) :> float\n        )\n        == 0.5,\n        (\n          animate(Time.seconds(1))\n          |> delay(Time.seconds(1))\n          |> valueAt(Time.ms(4660)) :> float\n        )\n        == 1.0,\n        animate(Time.seconds(1))\n        |> delay(Time.seconds(1))\n        |> stateAt(Time.ms(-2)) == Delayed,\n        animate(Time.seconds(1))\n        |> delay(Time.seconds(1))\n        |> stateAt(Time.ms(750)) == Delayed,\n        animate(Time.seconds(1))\n        |> delay(Time.seconds(1))\n        |> stateAt(Time.ms(1500)) == Running,\n        animate(Time.seconds(1))\n        |> delay(Time.seconds(1))\n        |> stateAt(Time.ms(4660)) == Complete(Time.seconds(2)),\n      ],\n    );\n\n    test(\"should accumulate\", ({expect, _}) => {\n      open Animation;\n\n      let anim =\n        animate(Time.seconds(1))\n        |> delay(Time.ms(1200))\n        |> delay(Time.ms(2300))\n        |> floatify;\n\n      Animation.(\n        {\n          expect.ext.animationState(stateAt(Time.ms(3499), anim)).toEqual(\n            Delayed,\n          );\n          expect.ext.animationState(stateAt(Time.ms(3500), anim)).toEqual(\n            Running,\n          );\n          expect.ext.animationState(stateAt(Time.seconds(4), anim)).toEqual(\n            Running,\n          );\n          expect.ext.animationState(stateAt(Time.seconds(5), anim)).toEqual(\n            Complete(Time.ms(4500)),\n          );\n        }\n      );\n    });\n  });\n\n  describe(\"repeat\", ({test, _}) => {\n    test(\"basics\", ({expect, _}) => {\n      open Animation;\n\n      let anim = animate(Time.seconds(1)) |> repeat |> floatify;\n\n      expect.float(anim |> valueAt(Time.seconds(-2))).toBeCloseTo(0.);\n      expect.float(anim |> valueAt(Time.ms(750))).toBeCloseTo(0.75);\n      expect.float(anim |> valueAt(Time.ms(1750))).toBeCloseTo(0.75);\n      expect.float(anim |> valueAt(Time.ms(3660))).toBeCloseTo(0.66);\n      expect.ext.animationState(anim |> stateAt(Time.ms(3660))).toEqual(\n        Running,\n      );\n      expect.float(anim |> valueAt(Time.ms(4660))).toBeCloseTo(0.66);\n    });\n\n    assertMany(\n      \"examples\",\n      test,\n      Animation.[\n        (\n          animate(Time.seconds(1)) |> repeat |> valueAt(Time.seconds(-2)) :> float\n        )\n        == 0.0,\n        (\n          animate(Time.seconds(1)) |> repeat |> valueAt(Time.ms(750)) :> float\n        )\n        == 0.75,\n        (\n          animate(Time.seconds(1)) |> repeat |> valueAt(Time.ms(1750)) :> float\n        )\n        == 0.75,\n        (\n          animate(Time.seconds(1)) |> repeat |> valueAt(Time.ms(3660)) :> float\n        )\n        =~. 0.66,\n        (\n          animate(Time.seconds(1)) |> repeat |> valueAt(Time.ms(4660)) :> float\n        )\n        =~. 0.66,\n        animate(Time.seconds(1))\n        |> repeat\n        |> stateAt(Time.seconds(-2)) == Delayed,\n        animate(Time.seconds(1))\n        |> repeat\n        |> stateAt(Time.ms(4660)) == Running,\n      ],\n    );\n  });\n\n  describe(\"alternatingRepeat\", ({test, _}) => {\n    test(\"nbasics\", ({expect, _}) => {\n      open Animation;\n\n      let anim = animate(Time.seconds(1)) |> alternatingRepeat |> floatify;\n\n      expect.float(anim |> valueAt(Time.seconds(-2))).toBeCloseTo(0.);\n      expect.float(anim |> valueAt(Time.ms(750))).toBeCloseTo(0.75);\n      expect.float(anim |> valueAt(Time.ms(1750))).toBeCloseTo(0.25);\n      expect.float(anim |> valueAt(Time.ms(3660))).toBeCloseTo(0.34);\n      expect.float(anim |> valueAt(Time.ms(4660))).toBeCloseTo(0.66);\n    });\n\n    assertMany(\n      \"examples\",\n      test,\n      Animation.[\n        (\n          animate(Time.seconds(1))\n          |> alternatingRepeat\n          |> valueAt(Time.seconds(-2)) :> float\n        )\n        == 0.0,\n        (\n          animate(Time.seconds(1))\n          |> alternatingRepeat\n          |> valueAt(Time.ms(750)) :> float\n        )\n        == 0.75,\n        (\n          animate(Time.seconds(1))\n          |> alternatingRepeat\n          |> valueAt(Time.ms(1750)) :> float\n        )\n        == 0.25,\n        (\n          animate(Time.seconds(1))\n          |> alternatingRepeat\n          |> valueAt(Time.ms(3660)) :> float\n        )\n        =~. 0.34,\n        (\n          animate(Time.seconds(1))\n          |> alternatingRepeat\n          |> valueAt(Time.ms(4660)) :> float\n        )\n        =~. 0.66,\n        animate(Time.seconds(1))\n        |> alternatingRepeat\n        |> stateAt(Time.seconds(-2)) == Delayed,\n        animate(Time.seconds(1))\n        |> alternatingRepeat\n        |> stateAt(Time.ms(4660)) == Running,\n      ],\n    );\n  });\n\n  describe(\"ease\", ({test, _}) => {\n    test(\"basics\", ({expect, _}) => {\n      open Animation;\n\n      let anim =\n        animate(Time.seconds(1)) |> ease(Easing.quadratic) |> floatify;\n\n      expect.float(anim |> valueAt(Time.seconds(-2))).toBeCloseTo(0.);\n      expect.float(anim |> valueAt(Time.ms(250))).toBeCloseTo(0.0625);\n      expect.float(anim |> valueAt(Time.ms(500))).toBeCloseTo(0.25);\n      expect.float(anim |> valueAt(Time.ms(750))).toBeCloseTo(0.5625);\n      expect.float(anim |> valueAt(Time.seconds(2))).toBeCloseTo(1.);\n    });\n\n    assertMany(\n      \"examples\",\n      test,\n      Animation.[\n        (\n          animate(Time.seconds(1))\n          |> ease(Easing.quadratic)\n          |> valueAt(Time.seconds(-2)) :> float\n        )\n        == 0.0,\n        (\n          animate(Time.seconds(1))\n          |> ease(Easing.quadratic)\n          |> valueAt(Time.ms(250)) :> float\n        )\n        == 0.0625,\n        (\n          animate(Time.seconds(1))\n          |> ease(Easing.quadratic)\n          |> valueAt(Time.ms(500)) :> float\n        )\n        == 0.25,\n        (\n          animate(Time.seconds(1))\n          |> ease(Easing.quadratic)\n          |> valueAt(Time.ms(750)) :> float\n        )\n        =~. 0.5625,\n        (\n          animate(Time.seconds(1))\n          |> ease(Easing.quadratic)\n          |> valueAt(Time.ms(4660)) :> float\n        )\n        =~. 1.0,\n      ],\n    );\n  });\n\n  describe(\"tween\", ({test, _}) => {\n    test(\"basics\", ({expect, _}) => {\n      open Animation;\n\n      let anim = animate(Time.seconds(1)) |> tween(100., 110.);\n\n      expect.float(anim |> valueAt(Time.seconds(-2))).toBeCloseTo(100.);\n      expect.float(anim |> valueAt(Time.ms(250))).toBeCloseTo(102.5);\n      expect.float(anim |> valueAt(Time.ms(500))).toBeCloseTo(105.);\n      expect.float(anim |> valueAt(Time.ms(750))).toBeCloseTo(107.5);\n      expect.float(anim |> valueAt(Time.seconds(2))).toBeCloseTo(110.);\n    });\n\n    assertMany(\n      \"examples\",\n      test,\n      Animation.[\n        animate(Time.seconds(1))\n        |> tween(2., 5.)\n        |> valueAt(Time.zero) == 2.0,\n        animate(Time.seconds(1))\n        |> tween(2., 5.)\n        |> valueAt(Time.ms(500)) == 3.5,\n        animate(Time.seconds(1))\n        |> tween(2., 5.)\n        |> valueAt(Time.seconds(-42)) == 2.0,\n        animate(Time.seconds(1))\n        |> tween(2., 5.)\n        |> valueAt(Time.seconds(42)) == 5.0,\n      ],\n    );\n  });\n\n  describe(\"map\", ({test, _}) => {\n    test(\"basics\", ({expect, _}) => {\n      open Animation;\n\n      let anim =\n        animate(Time.seconds(1)) |> floatify |> map(string_of_float);\n\n      expect.string(anim |> valueAt(Time.seconds(-2))).toEqual(\"0.\");\n      expect.string(anim |> valueAt(Time.ms(750))).toEqual(\"0.75\");\n      expect.string(anim |> valueAt(Time.ms(1500))).toEqual(\"1.\");\n    });\n\n    assertMany(\n      \"examples\",\n      test,\n      Animation.[\n        animate(Time.seconds(1))\n        |> map(n => string_of_float((n: NormalizedTime.t :> float)))\n        |> valueAt(Time.ms(330)) == \"0.33\",\n      ],\n    );\n  });\n\n  describe(\"andThen\", ({test, _}) => {\n    test(\"basics\", ({expect, _}) => {\n      open Animation;\n\n      let first = animate(Time.seconds(1)) |> tween(0., 10.);\n      let second = animate(Time.seconds(1)) |> tween(30., 20.);\n      let anim = andThen(first, ~next=second);\n\n      expect.float(anim |> valueAt(Time.seconds(-2))).toBeCloseTo(0.);\n      expect.float(anim |> valueAt(Time.ms(750))).toBeCloseTo(7.5);\n      expect.float(anim |> valueAt(Time.ms(1500))).toBeCloseTo(25.);\n      expect.float(anim |> valueAt(Time.ms(4660))).toBeCloseTo(20.);\n    });\n\n    assertMany(\n      \"examples\",\n      test,\n      Animation.[\n        (\n          animate(Time.seconds(1))\n          |> andThen(~next=animate(Time.seconds(2)))\n          |> valueAt(Time.seconds(1)) :> float\n        )\n        == 1.0,\n        (\n          animate(Time.seconds(1))\n          |> andThen(~next=animate(Time.seconds(2)))\n          |> valueAt(Time.seconds(2)) :> float\n        )\n        == 0.5,\n      ],\n    );\n  });\n\n  describe(\"zip\", ({test, _}) => {\n    test(\"basics\", ({expect, _}) => {\n      open Animation;\n\n      let first = animate(Time.seconds(1)) |> tween(0., 10.);\n      let second = animate(Time.seconds(1)) |> tween(30., 20.);\n      let anim = zip((first, second));\n\n      expect.float(anim |> valueAt(Time.seconds(-2)) |> fst).toBeCloseTo(\n        0.,\n      );\n      expect.float(anim |> valueAt(Time.seconds(-2)) |> snd).toBeCloseTo(\n        30.,\n      );\n      expect.float(anim |> valueAt(Time.ms(750)) |> fst).toBeCloseTo(7.5);\n      expect.float(anim |> valueAt(Time.ms(750)) |> snd).toBeCloseTo(22.5);\n      expect.float(anim |> valueAt(Time.ms(1500)) |> fst).toBeCloseTo(10.);\n      expect.float(anim |> valueAt(Time.ms(1500)) |> snd).toBeCloseTo(20.);\n      expect.float(anim |> valueAt(Time.ms(4660)) |> fst).toBeCloseTo(10.);\n      expect.float(anim |> valueAt(Time.ms(4660)) |> snd).toBeCloseTo(20.);\n    });\n\n    assertMany(\n      \"examples\",\n      test,\n      Animation.[\n        (\n          zip((animate(Time.seconds(1)), animate(Time.seconds(2))))\n          |> valueAt(Time.seconds(1))\n          |> fst :> float\n        )\n        == 1.0,\n        (\n          zip((animate(Time.seconds(1)), animate(Time.seconds(2))))\n          |> valueAt(Time.seconds(1))\n          |> snd :> float\n        )\n        == 0.5,\n      ],\n    );\n  });\n\n  describe(\"apply\", ({test, _}) => {\n    assertMany(\n      \"examples\",\n      test,\n      Animation.[\n        (animate(Time.seconds(1)) |> apply(Time.ms(330)) |> fst :> float)\n        == 0.33,\n        animate(Time.seconds(1)) |> apply(Time.ms(330)) |> snd == Running,\n      ],\n    )\n  });\n\n  describe(\"valueAt\", ({test, _}) => {\n    assertMany(\n      \"examples\",\n      test,\n      Animation.[\n        (animate(Time.seconds(1)) |> valueAt(Time.ms(330)) :> float)\n        == 0.33,\n      ],\n    )\n  });\n\n  describe(\"stateAt\", ({test, _}) => {\n    assertMany(\n      \"examples\",\n      test,\n      Animation.[\n        animate(Time.seconds(1)) |> stateAt(Time.ms(330)) == Running,\n      ],\n    )\n  });\n});\n"
  },
  {
    "path": "test/UI/HooksTest.re",
    "content": "open Revery_Core;\nopen Revery_UI;\nopen Revery_UI_Primitives;\n\nmodule Tick = Revery_Core.Tick;\nmodule Hooks = Revery_UI_Hooks;\n\nopen TestFramework;\n\ndescribe(\"Hooks\", ({describe, _}) => {\n  describe(\"Timer\", ({test, _}) => {\n    module SingleTimer = {\n      let%component make = (~timerActive, ()) => {\n        let%hook (_dt, _reset) =\n          Hooks.timer(~name=\"SingleTimer\", ~active=timerActive, ());\n\n        <View />;\n      };\n    };\n\n    module DoubleTimer = {\n      let%component make = (~timer1Active, ~timer2Active, ()) => {\n        let%hook (_dt, _reset) =\n          Hooks.timer(~name=\"DoubleTimer1\", ~active=timer1Active, ());\n        let%hook (_dt, _reset) =\n          Hooks.timer(~name=\"DoubleTimer2\", ~active=timer2Active, ());\n        <View />;\n      };\n    };\n\n    let getTickerCount = () => Tick.getActiveTickers() |> List.length;\n\n    test(\"Single: Timer starts and stops\", ({expect, _}) => {\n      expect.int(getTickerCount()).toBe(0);\n\n      let rootNode = (new viewNode)();\n      let container = Container.create(rootNode);\n\n      let container =\n        Container.update(container, <SingleTimer timerActive=true />);\n      Tick.pump();\n      expect.int(getTickerCount()).toBe(1);\n      let container =\n        Container.update(container, <SingleTimer timerActive=false />);\n      Tick.pump();\n      expect.int(getTickerCount()).toBe(0);\n\n      let container =\n        Container.update(container, <SingleTimer timerActive=true />);\n      Tick.pump();\n      expect.int(getTickerCount()).toBe(1);\n\n      let _container =\n        Container.update(container, <SingleTimer timerActive=false />);\n      Tick.pump();\n      expect.int(getTickerCount()).toBe(0);\n    });\n\n    test(\"Single: Timer starts and stops, in between pumps\", ({expect, _}) => {\n      expect.int(getTickerCount()).toBe(0);\n\n      let rootNode = (new viewNode)();\n      let container = Container.create(rootNode);\n\n      let container =\n        Container.update(container, <SingleTimer timerActive=true />);\n      Tick.pump();\n      expect.int(getTickerCount()).toBe(1);\n\n      let container =\n        Container.update(container, <SingleTimer timerActive=false />);\n      let container =\n        Container.update(container, <SingleTimer timerActive=true />);\n\n      let _container =\n        Container.update(container, <SingleTimer timerActive=false />);\n      Tick.pump();\n      expect.int(getTickerCount()).toBe(0);\n    });\n\n    test(\"Double: Timer starts and stops\", ({expect, _}) => {\n      expect.int(getTickerCount()).toBe(0);\n\n      let rootNode = (new viewNode)();\n      let container = Container.create(rootNode);\n\n      let container =\n        Container.update(\n          container,\n          <DoubleTimer timer1Active=true timer2Active=true />,\n        );\n      Tick.pump();\n      expect.int(getTickerCount()).toBe(2);\n      let container =\n        Container.update(\n          container,\n          <DoubleTimer timer1Active=false timer2Active=true />,\n        );\n      Tick.pump();\n      expect.int(getTickerCount()).toBe(1);\n      let container =\n        Container.update(\n          container,\n          <DoubleTimer timer1Active=true timer2Active=false />,\n        );\n      Tick.pump();\n      expect.int(getTickerCount()).toBe(1);\n\n      // Without the 'hack' to force disposal of the timers in Timer.re,\n      // this particular case fails - it will leaving a hanging timer.\n      let _container =\n        Container.update(\n          container,\n          <DoubleTimer timer1Active=false timer2Active=false />,\n        );\n      Tick.pump();\n      expect.int(getTickerCount()).toBe(0);\n    });\n  });\n\n  describe(\"Tick\", ({test, _}) => {\n    module Ticker = {\n      let%component make = (~f, ()) => {\n        let%hook () = Hooks.tick(~name=\"Ticker\", ~tickRate=Time.zero, f);\n\n        <View />;\n      };\n    };\n\n    test(\"Callback does not go stale\", ({expect, _}) => {\n      let rootNode = (new viewNode)();\n      let container = Container.create(rootNode);\n      let count = ref(0);\n\n      let container =\n        Container.update(container, <Ticker f={_ => count := 1} />);\n      Tick.pump();\n      expect.int(count^).toBe(1);\n\n      let _container =\n        Container.update(container, <Ticker f={_ => count := 2} />);\n      Tick.pump();\n      expect.int(count^).toBe(2);\n    });\n  });\n});\n"
  },
  {
    "path": "test/UI/MouseTest.re",
    "content": "open Revery_Core;\nopen Revery_UI;\nopen UiEvents;\n\nopen TestFramework;\n\nlet createNodeWithStyle = style => {\n  let node = (new node)();\n  node#setStyle(style);\n  Layout.layout(node);\n  node#recalculate();\n  node;\n};\n\ndescribe(\"Mouse\", ({describe, test, _}) => {\n  describe(\"pointer events\", ({test, _}) => {\n    test(\"ignore allows pointer events to pass through\", ({expect, _}) => {\n      /* We'll create a few nodes:\n           - Root\n             - Node 1\n               - Node 2\n             - Node 3\n              - Node 4\n\n            Node 3 will be set to ignore pointer events, but Node 4\n            will be set to accept pointer events, so the event\n            should make it to node 4.\n         */\n\n      let createChildNode = () => {\n        let _node = (new node)();\n        _node#setStyle(\n          Style.make(\n            ~position=Absolute,\n            ~top=0,\n            ~left=0,\n            ~width=100,\n            ~height=100,\n            (),\n          ),\n        );\n        _node;\n      };\n\n      let rootNode = (new node)();\n      rootNode#setStyle(Style.make(~width=100, ~height=100, ()));\n\n      let node1 = createChildNode();\n      let node2 = createChildNode();\n      let node3 = createChildNode();\n      let node4 = createChildNode();\n\n      node3#setStyle(\n        Style.make(\n          ~position=Absolute,\n          ~top=0,\n          ~left=0,\n          ~width=100,\n          ~height=100,\n          ~pointerEvents=Style.PointerEvents.Ignore,\n          (),\n        ),\n      );\n      node4#setStyle(\n        Style.make(\n          ~position=Absolute,\n          ~top=0,\n          ~left=0,\n          ~width=100,\n          ~height=100,\n          ~pointerEvents=Style.PointerEvents.Allow,\n          (),\n        ),\n      );\n\n      node1#addChild(node2, 0);\n      node3#addChild(node4, 0);\n      rootNode#addChild(node1, 0);\n      rootNode#addChild(node3, 1);\n\n      Layout.layout(rootNode);\n      rootNode#recalculate();\n\n      let child2HitCount = ref(0);\n      let child4HitCount = ref(0);\n\n      let child2MouseDown = _evt => {\n        incr(child2HitCount);\n      };\n      let child4MouseDown = _evt => {\n        incr(child4HitCount);\n      };\n\n      node2#setEvents(NodeEvents.make(~onMouseDown=child2MouseDown, ()));\n      node4#setEvents(NodeEvents.make(~onMouseDown=child4MouseDown, ()));\n\n      let cursor = Mouse.Cursor.make();\n      Mouse.dispatch(\n        cursor,\n        InternalMouseMove({mouseX: 50., mouseY: 50., keymod: 0}),\n        rootNode,\n      );\n      Mouse.dispatch(\n        cursor,\n        InternalMouseDown({button: BUTTON_LEFT, keymod: 0}),\n        rootNode,\n      );\n\n      expect.int(child2HitCount^).toBe(0);\n      expect.int(child4HitCount^).toBe(1);\n    });\n\n    test(\"ignore allows pointer events to pass through\", ({expect, _}) => {\n      /* We'll create a few nodes:\n           - Root\n             - Node 1\n               - Node 2\n             - Node 3\n              - Node 4\n\n            Node 3 will be set to ignore pointer events, so the event\n            shouldn't make it to node 4\n         */\n\n      let createChildNode = () => {\n        let _node = (new node)();\n        _node#setStyle(\n          Style.make(\n            ~position=Absolute,\n            ~top=0,\n            ~left=0,\n            ~width=100,\n            ~height=100,\n            (),\n          ),\n        );\n        _node;\n      };\n\n      let rootNode = (new node)();\n      rootNode#setStyle(Style.make(~width=100, ~height=100, ()));\n\n      let node1 = createChildNode();\n      let node2 = createChildNode();\n      let node3 = createChildNode();\n      let node4 = createChildNode();\n\n      node3#setStyle(\n        Style.make(\n          ~position=Absolute,\n          ~top=0,\n          ~left=0,\n          ~width=100,\n          ~height=100,\n          ~pointerEvents=Style.PointerEvents.Ignore,\n          (),\n        ),\n      );\n\n      node1#addChild(node2, 0);\n      node3#addChild(node4, 0);\n      rootNode#addChild(node1, 0);\n      rootNode#addChild(node3, 1);\n\n      Layout.layout(rootNode);\n      rootNode#recalculate();\n\n      let child2HitCount = ref(0);\n      let child4HitCount = ref(0);\n\n      let child2MouseDown = _evt => {\n        incr(child2HitCount);\n      };\n      let child4MouseDown = _evt => {\n        incr(child4HitCount);\n      };\n\n      node2#setEvents(NodeEvents.make(~onMouseDown=child2MouseDown, ()));\n      node4#setEvents(NodeEvents.make(~onMouseDown=child4MouseDown, ()));\n\n      let cursor = Mouse.Cursor.make();\n      Mouse.dispatch(\n        cursor,\n        InternalMouseMove({mouseX: 50., mouseY: 50., keymod: 0}),\n        rootNode,\n      );\n      Mouse.dispatch(\n        cursor,\n        InternalMouseDown({button: BUTTON_LEFT, keymod: 0}),\n        rootNode,\n      );\n      expect.int(child2HitCount^).toBe(1);\n      expect.int(child4HitCount^).toBe(0);\n    });\n  });\n  describe(\"layers\", ({test, _})\n    // Regression test for: https://github.com/onivim/oni2/issues/665\n    =>\n      test(\"event is dispatched to topmost node\", ({expect, _}) => {\n        /* We'll create a few nodes:\n             - Root\n               - Node 1\n                 - Node 2\n               - Node 3\n\n             All these nodes are absolutely positioned, so Node 4 would be rendered on top.\n             However, a bug in the old logic would prefer Node 3 (the deepest node) over\n             Node 4 (the topmost node).\n           */\n\n        let createChildNode = () => {\n          let _node = (new node)();\n          _node#setStyle(\n            Style.make(\n              ~position=Absolute,\n              ~top=0,\n              ~left=0,\n              ~width=100,\n              ~height=100,\n              (),\n            ),\n          );\n          _node;\n        };\n\n        let rootNode = (new node)();\n        rootNode#setStyle(Style.make(~width=100, ~height=100, ()));\n\n        let node1 = createChildNode();\n        let node2 = createChildNode();\n        let node3 = createChildNode();\n\n        node1#addChild(node2, 0);\n        rootNode#addChild(node1, 0);\n        rootNode#addChild(node3, 1);\n\n        Layout.layout(rootNode);\n        rootNode#recalculate();\n\n        let child2HitCount = ref(0);\n        let child3HitCount = ref(0);\n\n        let child2MouseDown = _evt => {\n          incr(child2HitCount);\n        };\n        let child3MouseDown = _evt => {\n          incr(child3HitCount);\n        };\n\n        node2#setEvents(NodeEvents.make(~onMouseDown=child2MouseDown, ()));\n        node3#setEvents(NodeEvents.make(~onMouseDown=child3MouseDown, ()));\n\n        let cursor = Mouse.Cursor.make();\n        Mouse.dispatch(\n          cursor,\n          InternalMouseMove({mouseX: 50., mouseY: 50., keymod: 0}),\n          rootNode,\n        );\n        Mouse.dispatch(\n          cursor,\n          InternalMouseDown({button: BUTTON_LEFT, keymod: 0}),\n          rootNode,\n        );\n\n        expect.int(child2HitCount^).toBe(0);\n        expect.int(child3HitCount^).toBe(1);\n      })\n    );\n  describe(\"dispatch\", ({test, _}) => {\n    test(\"triggers onMouseDown event for node\", ({expect, _}) => {\n      let cursor = Mouse.Cursor.make();\n\n      let count = ref(0);\n      let onMouseDown = _evt => {\n        count := count^ + 1;\n      };\n      let node =\n        createNodeWithStyle(Style.make(~width=100, ~height=100, ()));\n      node#setEvents(NodeEvents.make(~onMouseDown, ()));\n\n      Mouse.dispatch(\n        cursor,\n        InternalMouseMove({mouseX: 50., mouseY: 50., keymod: 0}),\n        node,\n      );\n      Mouse.dispatch(\n        cursor,\n        InternalMouseDown({button: BUTTON_LEFT, keymod: 0}),\n        node,\n      );\n\n      expect.int(count^).toBe(1);\n    });\n    test(\n      \"does not trigger onMouseUp event for node if outside node\",\n      ({expect, _}) => {\n      let cursor = Mouse.Cursor.make();\n\n      let count = ref(0);\n      let onMouseDown = _evt => {\n        count := count^ + 1;\n      };\n      let node =\n        createNodeWithStyle(Style.make(~width=100, ~height=100, ()));\n      node#setEvents(NodeEvents.make(~onMouseDown, ()));\n\n      Mouse.dispatch(\n        cursor,\n        InternalMouseMove({mouseX: 150., mouseY: 150., keymod: 0}),\n        node,\n      );\n      Mouse.dispatch(\n        cursor,\n        InternalMouseUp({button: BUTTON_LEFT, keymod: 0}),\n        node,\n      );\n\n      expect.int(count^).toBe(0);\n    });\n    test(\"does trigger onFocus for node\", ({expect, _}) => {\n      let cursor = Mouse.Cursor.make();\n      Mouse.Cursor.set(~x=50., ~y=50., cursor);\n\n      let count = ref(0);\n      let onFocus = _evt => count := count^ + 1;\n      let node =\n        createNodeWithStyle(Style.make(~width=100, ~height=100, ()));\n      node#setEvents(NodeEvents.make(~onFocus, ()));\n      node#setTabIndex(Some(1));\n\n      Mouse.dispatch(\n        cursor,\n        InternalMouseDown({button: BUTTON_LEFT, keymod: 0}),\n        node,\n      );\n\n      expect.int(count^).toBe(1);\n    });\n    test(\n      \"does trigger onBlur for node after cursor is pressed outside the node\",\n      ({expect, _}) => {\n      let cursor = Mouse.Cursor.make();\n      Mouse.Cursor.set(~x=50.0, ~y=50.0, cursor);\n\n      let count = ref(0);\n      let onBlur = _evt => count := count^ + 1;\n      let node =\n        createNodeWithStyle(Style.make(~width=100, ~height=100, ()));\n      node#setEvents(NodeEvents.make(~onBlur, ()));\n      node#setTabIndex(Some(1));\n      Mouse.dispatch(\n        cursor,\n        InternalMouseDown({button: BUTTON_LEFT, keymod: 0}),\n        node,\n      );\n      Mouse.Cursor.set(~x=200., ~y=200., cursor);\n      Mouse.dispatch(\n        cursor,\n        InternalMouseDown({button: BUTTON_LEFT, keymod: 0}),\n        node,\n      );\n      expect.int(count^).toBe(1);\n    });\n\n    test(\"triggers onMouseEnter event for node\", ({expect, _}) => {\n      let cursor = Mouse.Cursor.make();\n      let count = ref(0);\n      let onMouseEnter = _evt => count := count^ + 1;\n      let node =\n        createNodeWithStyle(Style.make(~width=100, ~height=100, ()));\n      node#setEvents(NodeEvents.make(~onMouseEnter, ()));\n\n      Mouse.dispatch(\n        cursor,\n        InternalMouseMove({mouseX: 50., mouseY: 50., keymod: 0}),\n        node,\n      );\n\n      expect.int(count^).toBe(1);\n    });\n\n    test(\"triggers onMouseLeave event for node\", ({expect, _}) => {\n      let cursor = Mouse.Cursor.make();\n      let count = ref(0);\n      let onMouseLeave = _evt => count := count^ + 1;\n      let node =\n        createNodeWithStyle(Style.make(~width=100, ~height=100, ()));\n      node#setEvents(NodeEvents.make(~onMouseLeave, ()));\n\n      Mouse.dispatch(\n        cursor,\n        InternalMouseMove({mouseX: 50., mouseY: 50., keymod: 0}),\n        node,\n      );\n\n      Mouse.dispatch(\n        cursor,\n        InternalMouseMove({mouseX: 200., mouseY: 200., keymod: 0}),\n        node,\n      );\n\n      expect.int(count^).toBe(1);\n    });\n\n    test(\n      \"triggers both onMouseEnter and onMouseLeave event for node\",\n      ({expect, _}) => {\n      let cursor = Mouse.Cursor.make();\n      let count = ref(0);\n      let f = _evt => count := count^ + 1;\n      let node =\n        createNodeWithStyle(Style.make(~width=100, ~height=100, ()));\n      node#setEvents(NodeEvents.make(~onMouseLeave=f, ~onMouseEnter=f, ()));\n\n      Mouse.dispatch(\n        cursor,\n        InternalMouseMove({mouseX: 50., mouseY: 50., keymod: 0}),\n        node,\n      );\n\n      Mouse.dispatch(\n        cursor,\n        InternalMouseMove({mouseX: 200., mouseY: 200., keymod: 0}),\n        node,\n      );\n\n      expect.int(count^).toBe(2);\n    });\n\n    test(\"triggers onMouseOver event for node\", ({expect, _}) => {\n      let cursor = Mouse.Cursor.make();\n      let count = ref(0);\n      let onMouseOver = _evt => count := count^ + 1;\n      let node =\n        createNodeWithStyle(Style.make(~width=100, ~height=100, ()));\n      node#setEvents(NodeEvents.make(~onMouseOver, ()));\n\n      Mouse.dispatch(\n        cursor,\n        InternalMouseMove({mouseX: 50., mouseY: 50., keymod: 0}),\n        node,\n      );\n\n      expect.int(count^).toBe(1);\n    });\n\n    test(\"triggers onMouseOut event for node\", ({expect, _}) => {\n      let cursor = Mouse.Cursor.make();\n      let count = ref(0);\n      let onMouseOut = _evt => count := count^ + 1;\n      let node =\n        createNodeWithStyle(Style.make(~width=100, ~height=100, ()));\n      node#setEvents(NodeEvents.make(~onMouseOut, ()));\n\n      Mouse.dispatch(\n        cursor,\n        InternalMouseMove({mouseX: 50., mouseY: 50., keymod: 0}),\n        node,\n      );\n\n      Mouse.dispatch(\n        cursor,\n        InternalMouseMove({mouseX: 150., mouseY: 150., keymod: 0}),\n        node,\n      );\n\n      expect.int(count^).toBe(1);\n    });\n    test(\n      \"triggers both onMouseOver and onMouseOut event for node\",\n      ({expect, _}) => {\n      let cursor = Mouse.Cursor.make();\n      let count = ref(0);\n      let f = _evt => count := count^ + 1;\n\n      let node =\n        createNodeWithStyle(Style.make(~width=100, ~height=100, ()));\n      node#setEvents(NodeEvents.make(~onMouseOut=f, ~onMouseOver=f, ()));\n\n      Mouse.dispatch(\n        cursor,\n        InternalMouseMove({mouseX: 50., mouseY: 50., keymod: 0}),\n        node,\n      );\n\n      Mouse.dispatch(\n        cursor,\n        InternalMouseMove({mouseX: 200., mouseY: 200., keymod: 0}),\n        node,\n      );\n\n      expect.int(count^).toBe(2);\n    });\n\n    test(\"onMouseOver and onMouseOut should bubble\", ({expect, _}) => {\n      let cursor = Mouse.Cursor.make();\n      let count = ref(0);\n      let f = _evt => count := count^ + 1;\n\n      let parentNode =\n        createNodeWithStyle(\n          Style.make(\n            ~width=100,\n            ~height=100,\n            ~flexDirection=LayoutTypes.Row,\n            ~justifyContent=LayoutTypes.JustifyCenter,\n            ~alignItems=LayoutTypes.AlignCenter,\n            (),\n          ),\n        );\n      parentNode#setEvents(\n        NodeEvents.make(~onMouseOut=f, ~onMouseOver=f, ()),\n      );\n\n      let childNode =\n        createNodeWithStyle(Style.make(~width=50, ~height=50, ()));\n      childNode#setEvents(\n        NodeEvents.make(~onMouseOut=f, ~onMouseOver=f, ()),\n      );\n\n      parentNode#addChild(childNode, 0);\n\n      Mouse.dispatch(\n        cursor,\n        InternalMouseMove({mouseX: 25., mouseY: 25., keymod: 0}),\n        parentNode,\n      );\n\n      Mouse.dispatch(\n        cursor,\n        InternalMouseMove({mouseX: 60., mouseY: 60., keymod: 0}),\n        childNode,\n      );\n\n      /**\n       * It should call mouseOver parent, mouseOut parent, mouseOver child and mouseOver parent\n       */\n      expect.int(count^).toBe(4);\n    });\n  });\n\n  describe(\"bubbleEvent\", ({test, _}) => {\n    test(\n      \"test that state is updated per event when stop propagation is called\",\n      ({expect, _}) => {\n      let evt =\n        BubbleEvent.make(\n          MouseMove({\n            mouseX: 50.,\n            mouseY: 50.,\n            ctrlKey: false,\n            altKey: false,\n            shiftKey: false,\n            guiKey: false,\n          }),\n        );\n\n      BubbleEvent.stopPropagation(evt);\n\n      expect.bool(evt.shouldPropagate).toBeFalse();\n    });\n\n    test(\n      \"test that state is updated per event when prevent default is called\",\n      ({expect, _}) => {\n      let evt =\n        BubbleEvent.make(\n          MouseMove({\n            mouseX: 50.,\n            mouseY: 50.,\n            ctrlKey: false,\n            altKey: false,\n            shiftKey: false,\n            guiKey: false,\n          }),\n        );\n\n      BubbleEvent.preventDefault(evt);\n\n      expect.bool(evt.defaultPrevented).toBeTrue();\n    });\n  });\n\n  describe(\"setCapture/releaseCapture\", ({test, _}) =>\n    test(\"captured events override dispatching to node\", ({expect, _}) => {\n      let cursor = Mouse.Cursor.make();\n\n      let uncapturedMoves = ref(0);\n      let capturedMoves = ref(0);\n      let onMouseMove = _evt => incr(uncapturedMoves);\n\n      let node =\n        createNodeWithStyle(Style.make(~width=100, ~height=100, ()));\n\n      node#setEvents(NodeEvents.make(~onMouseMove, ()));\n\n      Mouse.dispatch(\n        cursor,\n        InternalMouseMove({mouseX: 200., mouseY: 200., keymod: 0}),\n        node,\n      );\n\n      expect.int(uncapturedMoves^).toBe(0);\n      expect.int(capturedMoves^).toBe(0);\n\n      Mouse.dispatch(\n        cursor,\n        InternalMouseMove({mouseX: 50., mouseY: 50., keymod: 0}),\n        node,\n      );\n\n      expect.int(uncapturedMoves^).toBe(1);\n      expect.int(capturedMoves^).toBe(0);\n\n      Mouse.setCapture(\n        ~onMouseMove=_ => incr(capturedMoves),\n        Window.create(\"\", WindowCreateOptions.default),\n      );\n\n      Mouse.dispatch(\n        cursor,\n        InternalMouseMove({mouseX: 200., mouseY: 200., keymod: 0}),\n        node,\n      );\n\n      expect.int(uncapturedMoves^).toBe(1);\n      expect.int(capturedMoves^).toBe(1);\n\n      Mouse.releaseCapture();\n\n      Mouse.dispatch(\n        cursor,\n        InternalMouseMove({mouseX: 200., mouseY: 200., keymod: 0}),\n        node,\n      );\n\n      expect.int(uncapturedMoves^).toBe(1);\n      expect.int(capturedMoves^).toBe(1);\n    })\n  );\n\n  test(\n    \"onCursorChangedEvent gets dispatched with proper cursor\", ({expect, _}) => {\n    module Cursors = Revery_Core.MouseCursors;\n\n    let count = ref(0);\n    let cursorType = ref(Cursors.arrow);\n    let f = cur => {\n      count := count^ + 1;\n      cursorType := cur;\n    };\n    let node =\n      createNodeWithStyle(\n        Style.make(~width=100, ~cursor=Cursors.text, ~height=100, ()),\n      );\n\n    let _ignore = Revery_Core.Event.subscribe(Mouse.onCursorChanged, f);\n\n    Mouse.dispatch(\n      Mouse.Cursor.make(),\n      InternalMouseMove({mouseX: 50., mouseY: 50., keymod: 0}),\n      node,\n    );\n\n    expect.int(count^).toBe(1);\n    expect.same(cursorType^, Cursors.text);\n  });\n});\n"
  },
  {
    "path": "test/UI/NodeTests.re",
    "content": "open Revery_UI;\n\nopen TestFramework;\n\ndescribe(\"NodeTests\", ({test, _}) => {\n  test(\"no children initially\", ({expect, _}) => {\n    let node = (new node)();\n\n    expect.int(List.length(node#getChildren())).toBe(0);\n  });\n\n  test(\"add / remove child\", ({expect, _}) => {\n    let parentNode = (new node)();\n    let childNode = (new node)();\n\n    expect.option(childNode#getParent()).toBeNone();\n\n    parentNode#addChild(childNode, 0);\n\n    expect.int(List.length(parentNode#getChildren())).toBe(1);\n    expect.option(childNode#getParent()).toBe(\n      ~equals=(===),\n      Some(parentNode),\n    );\n\n    parentNode#removeChild(childNode);\n\n    expect.int(List.length(parentNode#getChildren())).toBe(0);\n    expect.option(childNode#getParent()).toBeNone();\n  });\n\n  describe(\"hitTest\", ({test, _}) => {\n    test(\"simple hitTest returns true case\", ({expect, _}) => {\n      let node = (new node)();\n      node#setStyle(Style.make(~width=400, ~height=500, ()));\n      Layout.layout(node);\n      node#recalculate();\n\n      expect.bool(node#hitTest(200., 250.)).toBeTrue();\n    });\n\n    test(\"simple hitTest returns false case\", ({expect, _}) => {\n      let node = (new node)();\n      node#setStyle(Style.make(~width=400, ~height=500, ()));\n\n      Layout.layout(node);\n      node#recalculate();\n\n      expect.bool(node#hitTest(401., 250.)).toBeFalse();\n    });\n\n    test(\"left / top are taken into account\", ({expect, _}) => {\n      let node = (new node)();\n      node#setStyle(Style.make(~top=5, ~left=5, ~height=2, ~width=2, ()));\n      Layout.layout(node);\n      node#recalculate();\n\n      expect.bool(node#hitTest(1., 1.)).toBeFalse();\n      expect.bool(node#hitTest(6., 6.)).toBeTrue();\n    });\n\n    test(\"parent transforms are taken into account\", ({expect, _}) => {\n      let parentNode = (new node)();\n      parentNode#setStyle(\n        Style.make(~top=50, ~left=50, ~height=100, ~width=100, ()),\n      );\n\n      let childNode = (new node)();\n      childNode#setStyle(Style.make(~width=25, ~height=25, ()));\n      parentNode#addChild(childNode, 0);\n\n      Layout.layout(parentNode);\n      parentNode#recalculate();\n\n      expect.bool(childNode#hitTest(0., 0.)).toBeFalse();\n      expect.bool(childNode#hitTest(60., 60.)).toBeTrue();\n    });\n  });\n});\n"
  },
  {
    "path": "test/UI/ReconcilerTests.re",
    "content": "open Revery_UI;\nopen Revery_UI_Primitives;\n\nopen TestFramework;\n\n/*\n * This module tests the integration of the Brisk reconciler - `React` -\n * with our Node tree.\n */\n\nmodule TestRefComponent = {\n  let%component make = (~latestRef, ()) => {\n    let%hook (refFromState, setRef) = React.Hooks.state(None);\n\n    latestRef := refFromState;\n\n    let setRefInState = r => {\n      setRef(_prevRef => Some(r));\n    };\n\n    <View ref=setRefInState />;\n  };\n};\n\ndescribe(\"Reconciler\", ({test, _}) => {\n  test(\"reconcile adds a child\", ({expect, _}) => {\n    let rootNode = (new viewNode)();\n\n    let container = Container.create(rootNode);\n    Container.update(container, <View />) |> ignore;\n\n    expect.int(List.length(rootNode#getChildren())).toBe(1);\n  });\n\n  test(\"ref: returns value of node\", ({expect, _}) => {\n    let rootNode = (new viewNode)();\n\n    let container = Container.create(rootNode);\n\n    /* Use a ref to track the latest value of the `ref={..}` callback */\n    let referenceNode = ref(None);\n\n    let refCallback = r => referenceNode := Some(r);\n\n    Container.update(container, <View ref=refCallback />) |> ignore;\n    rootNode#flushCallbacks();\n\n    /* And validate that we actually got the right one, based on the node ID! */\n    switch (referenceNode^) {\n    | Some(r) =>\n      let actualChild = rootNode#firstChild();\n      expect.same(actualChild#getInternalId(), r#getInternalId());\n    | None => expect.int(0).toBe(1)\n    };\n  });\n\n  test(\"ref: validate ref gets passed back to component\", ({expect, _}) => {\n    let rootNode = (new viewNode)();\n\n    let container = Container.create(rootNode);\n\n    /*\n     * Hold a `ref` that tracks the last refNode that comes from RENDER -\n     * this also exercises the interplay between the `ref` callback and `useState`\n     */\n    let latestRef: ref(option(viewNode)) = ref(None);\n\n    /* First update - this will end up 'mounting' the node and dispatching the 'ref' callback */\n    /* However - the state won't be updated - it will just be queued up */\n    let update1 = Container.update(container, <TestRefComponent latestRef />);\n\n    rootNode#flushCallbacks();\n\n    /* We need to update again to pick up the state update */\n    Container.update(update1, <TestRefComponent latestRef />) |> ignore;\n\n    switch (latestRef^) {\n    | Some(_) => expect.int(1).toBe(1)\n    | None => expect.int(0).toBe(1)\n    };\n  });\n\n  test(\"layout: onDimensionsChanged gets dispatched\", ({expect, _}) => {\n    let rootNode = (new viewNode)();\n    let container = Container.create(rootNode);\n    rootNode#setStyle(\n      Style.create(~style=Style.[width(100), height(200)], ()),\n    );\n\n    let callCount = ref(0);\n    let lastWidth = ref(0);\n    let lastHeight = ref(0);\n\n    let style =\n      Style.[position(`Absolute), top(0), left(0), right(0), bottom(0)];\n\n    let onDimensionsChanged =\n        ({width, height}: NodeEvents.DimensionsChangedEventParams.t) => {\n      lastWidth := width;\n      lastHeight := height;\n      callCount := callCount^ + 1;\n    };\n\n    let update1 =\n      Container.update(container, <View onDimensionsChanged style />);\n\n    Layout.layout(rootNode);\n    rootNode#recalculate();\n    rootNode#flushCallbacks();\n\n    expect.int(callCount^).toBe(1);\n    expect.int(lastWidth^).toBe(100);\n    expect.int(lastHeight^).toBe(200);\n\n    /* Shouldn't dispatch if width/height hasn't change! */\n    let update2 =\n      Container.update(update1, <View onDimensionsChanged style />);\n\n    Layout.layout(rootNode);\n    rootNode#recalculate();\n    rootNode#flushCallbacks();\n\n    expect.int(callCount^).toBe(1);\n\n    /* Should update if the size was somehow changed.. */\n    rootNode#setStyle(\n      Style.create(~style=Style.[width(300), height(400)], ()),\n    );\n    Container.update(update2, <View onDimensionsChanged style />) |> ignore;\n\n    Layout.layout(rootNode);\n    rootNode#recalculate();\n    rootNode#flushCallbacks();\n\n    expect.int(callCount^).toBe(2);\n    expect.int(lastWidth^).toBe(300);\n    expect.int(lastHeight^).toBe(400);\n  });\n});\n"
  },
  {
    "path": "test/UI/StyleTest.re",
    "content": "open Revery_UI;\nopen Revery_Core.Colors;\nopen Style;\n\nopen TestFramework;\n\ndescribe(\"Style API tests\", ({test, _}) => {\n  test(\"that a style record is created correctly\", ({expect, _}) => {\n    let styles = create(~style=[height(1), width(2)], ());\n    expect.int(styles.height).toBe(1);\n    expect.int(styles.width).toBe(2);\n  });\n\n  test(\"defaults should be correctly set\", ({expect, _}) => {\n    let styles = create(~style=[height(1), width(2)], ());\n    // Just a variant: so not expect.same\n    // https://github.com/jordwalke/flex/blob/master/src/lib/LayoutTypes.re#L42\n    expect.equal(styles.position, LayoutTypes.Relative);\n    expect.equal(styles.border, Border.make());\n  });\n\n  test(\"it correctly sets a margin\", ({expect, _}) => {\n    let styles =\n      create(\n        ~style=[\n          margin(1),\n          marginLeft(7),\n          marginTop(4),\n          marginRight(8),\n          marginBottom(9),\n        ],\n        (),\n      );\n    expect.int(styles.margin).toBe(1);\n    expect.int(styles.marginBottom).toBe(9);\n    expect.int(styles.marginTop).toBe(4);\n    expect.int(styles.marginRight).toBe(8);\n    expect.int(styles.marginBottom).toBe(9);\n  });\n\n  test(\"It correctly sets a margin in the x an y axis\", ({expect, _}) => {\n    let styles = create(~style=[margin2(~vertical=2, ~horizontal=4)], ());\n    expect.int(styles.marginVertical).toBe(2);\n    expect.int(styles.marginHorizontal).toBe(4);\n  });\n\n  test(\"it correctly sets a border\", ({expect, _}) => {\n    let styles =\n      create(\n        ~style=[\n          border(~color=black, ~width=2),\n          borderLeft(~color=rebeccaPurple, ~width=2),\n          borderTop(~color=red, ~width=2),\n          borderRight(~color=blue, ~width=2),\n          borderBottom(~color=orange, ~width=2),\n          borderHorizontal(~color=paleVioletRed, ~width=12),\n          borderVertical(~color=paleTurquoise, ~width=18),\n        ],\n        (),\n      );\n    expect.equal(styles.border, {color: black, width: 2});\n    expect.equal(styles.borderBottom, {color: orange, width: 2});\n    expect.equal(styles.borderTop, {color: red, width: 2});\n    expect.equal(styles.borderRight, {color: blue, width: 2});\n    expect.equal(styles.borderLeft, {color: rebeccaPurple, width: 2});\n    expect.equal(styles.borderHorizontal, {color: paleVioletRed, width: 12});\n    expect.equal(styles.borderVertical, {color: paleTurquoise, width: 18});\n  });\n\n  test(\n    \"Should correctly overwrite the source style if the target exists\",\n    ({expect, _}) => {\n    let result =\n      merge(\n        ~source=[margin(10), color(red), backgroundColor(paleTurquoise)],\n        ~target=[margin(4), color(blue)],\n      );\n    let styles = create(~style=result, ());\n    expect.int(styles.margin).toBe(4);\n    let found = List.find(style => style == `Margin(4), result);\n    expect.equal(found, `Margin(4));\n    expect.equal(styles.color, Some(blue));\n  });\n\n  test(\n    \"Should keep old styles when merging if they do not conflict with new ones\",\n    ({expect, _}) => {\n    let result =\n      merge(\n        ~source=[margin(10), color(red), backgroundColor(paleTurquoise)],\n        ~target=[margin(4), color(blue)],\n      );\n\n    let found =\n      List.find(style => style == `BackgroundColor(paleTurquoise), result);\n    expect.equal(found, `BackgroundColor(paleTurquoise));\n  });\n\n  test(\n    \"Should use a fallback if the correct style is not present\",\n    ({expect, _}) => {\n    let l = [color(black), height(20)];\n    let fb = Selector.select(l, Top, 10);\n    expect.int(fb).toBe(10);\n  });\n});\n"
  },
  {
    "path": "test/UI/TestFramework.re",
    "content": "open Revery_Core;\nopen Revery_UI;\n\nopen Rely.MatcherTypes;\n\n// Approximate equality for floats\nlet (=~.) = (~tolerance=0.0001, a, b) => abs_float(a -. b) < tolerance;\n\ntype animationStateExtensions = {toEqual: Animation.state => unit};\n\nlet animationStateExtensions = (actual, {createMatcher}) => {\n  let pass = (() => \"\", true);\n  let fail = message => (() => message, false);\n\n  let toString =\n    Animation.(\n      fun\n      | Delayed => \"Delayed\"\n      | Running => \"Running\"\n      | Complete(elapsed) =>\n        Printf.sprintf(\"Complete(%s)\", Time.toString(elapsed))\n    );\n\n  let createStateMatcher =\n    createMatcher(\n      ({formatReceived, formatExpected, _}, actualThunk, expectedThunk) => {\n      let actual = actualThunk();\n      let expected = expectedThunk();\n\n      Animation.(\n        switch (actual, expected) {\n        | (Delayed, Delayed)\n        | (Running, Running) => pass\n        | (Complete(a), Complete(b))\n            when Time.toFloatSeconds(a) =~. Time.toFloatSeconds(b) => pass\n        | _ =>\n          let failureMessage =\n            Printf.sprintf(\n              \"Expected: %s\\nReceived: %s\",\n              formatExpected(toString(expected)),\n              formatReceived(toString(actual)),\n            );\n          fail(failureMessage);\n        }\n      );\n    });\n\n  {toEqual: expected => createStateMatcher(() => actual, () => expected)};\n};\n\ntype customMatchers = {\n  animationState: Animation.state => animationStateExtensions,\n};\n\nlet customMatchers = createMatcher => {\n  animationState: actual => animationStateExtensions(actual, createMatcher),\n};\n\ninclude Rely.Make({\n  let config =\n    Rely.TestFrameworkConfig.initialize({\n      snapshotDir: \"test/UI/__snapshots__\",\n      projectDir: \"\",\n    });\n});\n\nlet {describe, describeOnly, describeSkip} =\n  describeConfig |> withCustomMatchers(customMatchers) |> build;\n\nlet assertOne = (label, test, assertion) =>\n  test(label, ({expect, _}) =>\n    expect.bool(assertion).toBeTrue()\n  );\n\nlet assertMany = (label, test) =>\n  List.iteri((i, assertion) =>\n    assertOne(Printf.sprintf(\"%s %n\", label, i), test, assertion)\n  );\n"
  },
  {
    "path": "test/UI/dune",
    "content": "(library\n (name Revery_UI_Test)\n (library_flags\n  (-linkall -g))\n (modules (:standard))\n (preprocess\n  (pps brisk-reconciler.ppx))\n (libraries Revery_UI Revery_UI_Primitives Revery_UI_Hooks Revery_Math\n   rely.lib))\n"
  },
  {
    "path": "test/collateral/FiraCode-LICENSE.txt",
    "content": "Copyright (c) 2014, The Fira Code Project Authors (https://github.com/tonsky/FiraCode)\n\nThis Font Software is licensed under the SIL Open Font License, Version 1.1.\nThis license is copied below, and is also available with a FAQ at:\nhttp://scripts.sil.org/OFL\n\n\n-----------------------------------------------------------\nSIL OPEN FONT LICENSE Version 1.1 - 26 February 2007\n-----------------------------------------------------------\n\nPREAMBLE\nThe goals of the Open Font License (OFL) are to stimulate worldwide\ndevelopment of collaborative font projects, to support the font creation\nefforts of academic and linguistic communities, and to provide a free and\nopen framework in which fonts may be shared and improved in partnership\nwith others.\n\nThe OFL allows the licensed fonts to be used, studied, modified and\nredistributed freely as long as they are not sold by themselves. The\nfonts, including any derivative works, can be bundled, embedded,\nredistributed and/or sold with any software provided that any reserved\nnames are not used by derivative works. The fonts and derivatives,\nhowever, cannot be released under any other type of license. The\nrequirement for fonts to remain under this license does not apply\nto any document created using the fonts or their derivatives.\n\nDEFINITIONS\n\"Font Software\" refers to the set of files released by the Copyright\nHolder(s) under this license and clearly marked as such. This may\ninclude source files, build scripts and documentation.\n\n\"Reserved Font Name\" refers to any names specified as such after the\ncopyright statement(s).\n\n\"Original Version\" refers to the collection of Font Software components as\ndistributed by the Copyright Holder(s).\n\n\"Modified Version\" refers to any derivative made by adding to, deleting,\nor substituting -- in part or in whole -- any of the components of the\nOriginal Version, by changing formats or by porting the Font Software to a\nnew environment.\n\n\"Author\" refers to any designer, engineer, programmer, technical\nwriter or other person who contributed to the Font Software.\n\nPERMISSION & CONDITIONS\nPermission is hereby granted, free of charge, to any person obtaining\na copy of the Font Software, to use, study, copy, merge, embed, modify,\nredistribute, and sell modified and unmodified copies of the Font\nSoftware, subject to the following conditions:\n\n1) Neither the Font Software nor any of its individual components,\nin Original or Modified Versions, may be sold by itself.\n\n2) Original or Modified Versions of the Font Software may be bundled,\nredistributed and/or sold with any software, provided that each copy\ncontains the above copyright notice and this license. These can be\nincluded either as stand-alone text files, human-readable headers or\nin the appropriate machine-readable metadata fields within text or\nbinary files as long as those fields can be easily viewed by the user.\n\n3) No Modified Version of the Font Software may use the Reserved Font\nName(s) unless explicit written permission is granted by the corresponding\nCopyright Holder. This restriction only applies to the primary font name as\npresented to the users.\n\n4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font\nSoftware shall not be used to promote, endorse or advertise any\nModified Version, except to acknowledge the contribution(s) of the\nCopyright Holder(s) and the Author(s) or with their explicit written\npermission.\n\n5) The Font Software, modified or unmodified, in part or in whole,\nmust be distributed entirely under this license, and must not be\ndistributed under any other license. The requirement for fonts to\nremain under this license does not apply to any document created\nusing the Font Software.\n\nTERMINATION\nThis license becomes null and void if any of the above conditions are\nnot met.\n\nDISCLAIMER\nTHE FONT SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT\nOF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE\nCOPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\nINCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL\nDAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM\nOTHER DEALINGS IN THE FONT SOFTWARE.\n"
  },
  {
    "path": "test/collateral/JetBrainsMono-LICENSE.txt",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "test/collateral/dune",
    "content": "(install\n (section bin)\n (package ReveryTest)\n (files FiraCode-Regular.ttf JetBrainsMono-Regular.ttf test-asset.txt))\n"
  },
  {
    "path": "test/collateral/test-asset.txt",
    "content": "(executable\n    (name TestRunner)\n    (libraries\n        rejest\n        Revery_Core_Test\n        Revery_Math_Test\n        Revery_UI_Test\n            ))\n\n(alias\n    (name runtest)\n    (action (run ./TestRunner.exe)))\n"
  },
  {
    "path": "test/dune",
    "content": "(executable\n (name TestRunner)\n (public_name ReveryTestRunner)\n (package ReveryTest)\n (libraries Revery_Core_Test Revery_Math_Test Revery_UI_Test Revery_Font_Test\n   Skia_Test Harfbuzz_Test))\n"
  },
  {
    "path": "test.json",
    "content": "{\n  \"source\": \"./package.json\",\n  \"scripts\": {\n      \"run\": \"esy '@test' x ReveryTestRunner\"\n  },\n  \"override\": {\n      \"build\": [\"dune build -p reason-harfbuzz,reason-skia,reason-sdl2,Revery,ReveryTest -j4\"],\n      \"dependencies\": {\n        \"@reason-native/rely\": \"*\"\n      },\n      \"install\": [\n          \"esy-installer Revery.install\",\n          \"esy-installer ReveryTest.install\"\n      ]\n  }\n}\n"
  },
  {
    "path": "update-lockfiles.sh",
    "content": "esy install\nesy '@test' install\nesy '@bench' install\nesy '@doc' install\nesy '@examples' install\nesy '@js' install\ngit add *esy.lock\n"
  }
]