├── .github └── workflows │ └── build.yaml ├── .gitignore ├── .idea ├── .gitignore ├── modules.xml ├── pobfrontend.iml └── vcs.xml ├── Info.plist ├── Info.plist.sh ├── LICENSE ├── Makefile ├── Makefile.linux ├── README.md ├── editLuaCurlMakefile.sh ├── editPathOfBuildingBuild.sh ├── fonts ├── Bitstream Vera License.txt ├── LiberationSans-Bold.ttf ├── LiberationSans-Regular.ttf ├── SIL Open Font License.txt └── VeraMono.ttf ├── main.cpp ├── main.h ├── meson.build ├── mesonInstaller.sh ├── pobLogo.icns ├── pobWrapper.sh ├── pobwindow.hpp └── subscript.hpp /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | schedule: 9 | - cron: '30 13 * * *' 10 | 11 | jobs: 12 | build: 13 | 14 | runs-on: macos-12 15 | 16 | steps: 17 | - uses: actions/checkout@v3 18 | - run: brew install make ninja 19 | - run: make tools 20 | - run: make clean 21 | - run: make 22 | - run: zip -r PathOfBuilding.zip PathOfBuilding.app 23 | 24 | # Based on https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-output-parameter 25 | - id: get-version 26 | run: | 27 | version=$(sed -n 's/^.*number="\([0-9.]*\)".*$/\1/p' PathOfBuilding/manifest.xml) 28 | echo "VERSION=$version" >> "$GITHUB_OUTPUT" 29 | - name: Upload MacOS artifacts into Github 30 | uses: ncipollo/release-action@v1 31 | with: 32 | artifacts: "PathOfBuilding.zip" 33 | token: ${{ secrets.GITHUB_TOKEN }} 34 | allowUpdates: true 35 | tag: v${{ steps.get-version.outputs.VERSION }} 36 | if: github.ref == 'refs/heads/master' 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lcurl.so 2 | Lua-cURLv3 3 | /PathOfBuilding 4 | /PathOfBuildingBuild 5 | /PathOfBuilding.app 6 | /PathOfBuilding.* 7 | .DS_Store 8 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/pobfrontend.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleExecutable 6 | PathOfBuilding 7 | CFBundleGetInfoString 8 | Created by Qt/QMake 9 | CFBundleIconFile 10 | pobLogo.icns 11 | CFBundleIdentifier 12 | com.yourcompany.PathOfBuilding 13 | CFBundleName 14 | PathOfBuilding 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | LSMinimumSystemVersion 22 | 10.11 23 | NOTE 24 | This file was generated by Qt/QMake. 25 | NSPrincipalClass 26 | NSApplication 27 | NSHighResolutionCapable 28 | 29 | NSSupportsAutomaticGraphicsSwitching 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Info.plist.sh: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleExecutable 6 | pobWrapper.sh 7 | CFBundleGetInfoString 8 | Created by Qt/QMake 9 | CFBundleIconFile 10 | pobLogo.icns 11 | CFBundleIdentifier 12 | com.yourcompany.PathOfBuilding 13 | CFBundleName 14 | PathOfBuilding 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | LSMinimumSystemVersion 22 | 10.11 23 | NOTE 24 | This file was generated by Qt/QMake. 25 | NSPrincipalClass 26 | NSApplication 27 | NSHighResolutionCapable 28 | 29 | NSSupportsAutomaticGraphicsSwitching 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Phil Roberts 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | DIR := ${CURDIR} 2 | export PATH := /usr/local/opt/qt@5/bin:$(PATH) 3 | # Some users on old versions of MacOS 10.13 run into the error: 4 | # dyld: cannot load 'PathOfBuilding' (load command 0x80000034 is unknown) 5 | # 6 | # It looks like 0x80000034 is associated with the fixup_chains optimization 7 | # that improves startup time: 8 | # https://www.emergetools.com/blog/posts/iOS15LaunchTime 9 | # 10 | # For compatibility, we disable that using the flag from this thread: 11 | # https://github.com/python/cpython/issues/97524 12 | export LDFLAGS := -L/usr/local/opt/qt@5/lib -Wl,-no_fixup_chains 13 | export CPPFLAGS := -I/usr/local/opt/qt@5/include 14 | export PKG_CONFIG_PATH := /usr/local/opt/qt@5/lib/pkgconfig 15 | 16 | all: frontend pob 17 | pushd build; \ 18 | ninja install; \ 19 | popd; \ 20 | macdeployqt ${DIR}/PathOfBuilding.app; \ 21 | cp ${DIR}/Info.plist.sh ${DIR}/PathOfBuilding.app/Contents/Info.plist; \ 22 | echo 'Finished' 23 | 24 | pob: load_pob luacurl frontend 25 | rm -rf PathOfBuildingBuild; \ 26 | cp -rf PathOfBuilding PathOfBuildingBuild; \ 27 | pushd PathOfBuildingBuild; \ 28 | bash ../editPathOfBuildingBuild.sh; \ 29 | popd 30 | 31 | frontend: 32 | arch=x86_64 meson -Dbuildtype=release --prefix=${DIR}/PathOfBuilding.app --bindir=Contents/MacOS build 33 | 34 | # We checkout the latest version. 35 | load_pob: 36 | git clone https://github.com/PathOfBuildingCommunity/PathOfBuilding.git; \ 37 | pushd PathOfBuilding; \ 38 | git fetch; \ 39 | git add . && git reset --hard HEAD && git checkout $$(git describe --tags $$(git rev-list --tags --max-count=1)); \ 40 | popd 41 | 42 | luacurl: 43 | git clone --depth 1 https://github.com/Lua-cURL/Lua-cURLv3.git; \ 44 | bash editLuaCurlMakefile.sh; \ 45 | pushd Lua-cURLv3; \ 46 | make; \ 47 | mv lcurl.so ../lcurl.so; \ 48 | popd 49 | 50 | # curl is used since mesonInstaller.sh copies over the shared library dylib 51 | # dylibbundler is used to copy over dylibs that lcurl.so uses 52 | tools: 53 | arch --x86_64 brew install qt@5 luajit zlib meson curl dylibbundler gcc@12 54 | 55 | # We don't usually modify the PathOfBuilding directory, so there's rarely a 56 | # need to delete it. We separate it out to a separate task. 57 | fullyclean: clean 58 | rm -rf PathOfBuilding 59 | 60 | clean: 61 | rm -rf PathOfBuildingBuild PathOfBuilding.app Lua-cURLv3 lcurl.so build 62 | -------------------------------------------------------------------------------- /Makefile.linux: -------------------------------------------------------------------------------- 1 | DIR := ${CURDIR} 2 | SHELL = /bin/bash 3 | 4 | all: frontend pob 5 | pushd build; \ 6 | ninja; \ 7 | popd; \ 8 | mv build/PathOfBuilding PathOfBuilding/src; \ 9 | echo 'Finished' 10 | 11 | pob: load_pob luacurl frontend 12 | pushd PathOfBuilding; \ 13 | unzip runtime-win32.zip lua/xml.lua lua/base64.lua lua/sha1.lua; \ 14 | mv lua/*.lua src/; \ 15 | rmdir lua; \ 16 | cp ../lcurl.so src/; \ 17 | popd 18 | 19 | frontend: 20 | meson -Dbuildtype=release build 21 | 22 | load_pob: 23 | git clone --depth 1 https://github.com/PathOfBuildingCommunity/PathOfBuilding.git; \ 24 | pushd PathOfBuilding; \ 25 | rm -rf .git; \ 26 | popd 27 | 28 | luacurl: 29 | git clone --depth 1 https://github.com/Lua-cURL/Lua-cURLv3.git; \ 30 | pushd Lua-cURLv3; \ 31 | sed -i -e's/\?= lua/\?= luajit/' Makefile; \ 32 | make; \ 33 | mv lcurl.so ../lcurl.so; \ 34 | popd 35 | 36 | tools: qt lua zlib meson 37 | 38 | qt: 39 | sudo apt -y install qt5-default; \ 40 | sudo apt -y install qtcreator 41 | 42 | lua: 43 | sudo apt -y install luajit; \ 44 | sudo apt -y install libluajit-5.1-dev 45 | 46 | zlib: 47 | sudo apt -y install zlib1g; \ 48 | sudo apt -y install zlib1g-dev 49 | 50 | meson: 51 | sudo apt -y install meson 52 | 53 | clean: 54 | rm -rf PathOfBuilding Lua-cURLv3 lcurl.so build 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PoBFrontend 2 | =========== 3 | 4 | A cross-platform [Path of Building](https://github.com/Openarl/PathOfBuilding) driver. 5 | 6 | Building 7 | -------- 8 | 9 | ### Steps to build an x86_64 binary on M1 Macs 10 | 11 | Before starting, you need to install Homebrew for x86_64. 12 | 13 | 1. Install Rosetta if you haven't via `softwareupdate --install-rosetta` 14 | 2. Run the [Homebrew installation command](https://docs.brew.sh/Installation), 15 | but prepend `arch --x86_64`. The command will be `arch --x86_64 bash -c ...` 16 | 3. Create a `~/.intelbrew` file with these contents: 17 | ```sh 18 | eval "$(/usr/local/bin/brew shellenv)" 19 | alias brew='arch --x86_64 /usr/local/bin/brew' 20 | ``` 21 | 4. Include it to update your environment variables by running `. ~/.intelbrew` 22 | 5. Edit Homebrew to fetch packages for Ventura (10.13), since the Qt package 23 | for Sonoma (10.14) breaks compatibility with libc++: see [this error](https://www.pathofexile.com/forum/view-thread/3009944/page/34#:~:text=__ZTVNSt3__13pmr25monotonic_buffer_resourceE) 24 | 25 | ```sh 26 | vim /usr/local/Homebrew/Library/Homebrew/brew.sh 27 | # Edit the file to comment out the version and hardcode it: 28 | # #HOMEBREW_MACOS_VERSION="$(/usr/bin/sw_vers -productVersion)" 29 | # HOMEBREW_MACOS_VERSION="12.0.0" 30 | 31 | # Run this only once after installing Homebrew to install dependencies 32 | make tools 33 | 34 | # Build the entire app 35 | export PATH="/usr/local/opt/qt@5/bin:$PATH" 36 | make 37 | 38 | # Optionally sign it for distribution 39 | make sign 40 | ``` 41 | 42 | ### Dependencies: 43 | 44 | - Qt5 45 | - luajit 46 | - zlib 47 | - opengl 48 | - lua-curl (see below) 49 | - Bitstream-Vera and Liberation TTF fonts. Will work without these but most likely look terrible. 50 | 51 | ### Build dependencies: 52 | 53 | - meson 54 | - pkg-config 55 | - ninja (optional, can tell meson to generate makefiles if you prefer) 56 | 57 | ### Ensuring old versions of Mac are compatible: 58 | 59 | By default, the built lcurl.so links to the local version of cURL. Old 60 | versions of cURL on old Macs may be too old to include the relevant functions and run into this error: 61 | 62 | ``` 63 | Error loading main script: error loading module 'lcurl.safe' from file './lcurl.so': 64 | dlopen(./lcurl.so, 6): Symbol not found: _curl_easy_option_by_id 65 | Referenced from: ./lcurl.so (which was built for Mac OS X 13.0) 66 | Expected in: /usr/lib/libcurl.4.dylib 67 | ``` 68 | 69 | To try to fix this issue, we include the libcurl.4.dylib in our app 70 | using dylibbundler. 71 | 72 | We change it in mesonInstaller.sh based on https://stackoverflow.com/a/38709580/319066 73 | 74 | - `otool -L lcurl.so` can be used to debug the paths. 75 | - `install_name_tool ...` is used to change the paths behind the scenes, but 76 | dylibbundler does all the work for us. 77 | 78 | ## Old manual steps to build: 79 | 80 | ### Build Lua-Curl: 81 | 82 | You need to build [Lua-Curl](https://github.com/Lua-cURL/Lua-cURLv3) for luajit. 83 | 84 | Edit the Lua-Curl Makefile: 85 | 86 | ```diff 87 | @@ -7,7 +7,7 @@ DESTDIR ?= / 88 | PKG_CONFIG ?= pkg-config 89 | INSTALL ?= install 90 | RM ?= rm 91 | -LUA_IMPL ?= lua 92 | +LUA_IMPL ?= luajit 93 | CC ?= $(MAC_ENV) gcc 94 | 95 | LUA_VERSION = $(shell $(PKG_CONFIG) --print-provides --silence-errors $(LUA_IMPL)) 96 | ``` 97 | 98 | Run make. You should get `lcurl.so`. 99 | 100 | ### Get the PoBFrontend sources: 101 | 102 | `git clone https://github.com/philroberts/pobfrontend.git` 103 | 104 | ### Build: 105 | 106 | ```bash 107 | meson -Dbuildtype=release pobfrontend build 108 | cd build 109 | ninja 110 | ``` 111 | 112 | Run the thing: 113 | 114 | ```bash 115 | cd /path/to/PathOfBuilding # <- a pathofbuilding git clone 116 | for f in tree*.zip; do unzip $f;done # <- use the provided tree data because reasons 117 | unzip runtime-win32.zip lua/xml.lua lua/base64.lua lua/sha1.lua 118 | mv lua/*.lua . 119 | rmdir lua 120 | cp /path/to/lcurl.so . # our lcurl.so from earlier 121 | /path/to/build/pobfrontend 122 | ``` 123 | 124 | You can adjust the font size up or down with a command line argument: 125 | 126 | ```bash 127 | pobfrontend -2 128 | ``` 129 | 130 | ### Notes: 131 | 132 | I have the following edit in my PathOfBuilding clone, stops it from saving builds even when I tell it not to: 133 | 134 | ```diff 135 | --- a/Modules/Build.lua 136 | +++ b/Modules/Build.lua 137 | @@ -599,7 +599,7 @@ function buildMode:CanExit(mode) 138 | end 139 | 140 | function buildMode:Shutdown() 141 | - if launch.devMode and self.targetVersion and not self.abortSave then 142 | + if false then --launch.devMode and self.targetVersion and not self.abortSave then 143 | if self.dbFileName then 144 | self:SaveDBFile() 145 | elseif self.unsaved then 146 | ``` 147 | 148 | ###### OS X 149 | 150 | On mac you need to invoke meson with some extra flags, per the luajit documentation: 151 | 152 | ```bash 153 | LDFLAGS="-pagezero_size 10000 -image_base 100000000" meson pobfrontend build 154 | ``` 155 | 156 | 157 | -------------------------------------------------------------------------------- /editLuaCurlMakefile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | pushd Lua-cURLv3 3 | 4 | # We only replace `lua` with `luajit` once 5 | sed -i '' 's/?= lua$/?= luajit/' Makefile 6 | # We use pkg-config to find the right path for curl libraries 7 | sed -i '' 's@shell .* --libs libcurl@shell PKG_CONFIG_PATH=$$(arch --x86_64 brew --prefix --installed curl)/lib/pkgconfig $(PKG_CONFIG) --libs libcurl@' Makefile 8 | # We use the Homebrew installed GCC and build for x86_64 to ensure we build the 9 | # x86_64 library, even on ARM systems 10 | sed -i '' 's@?= \$(MAC_ENV) gcc$@ = \$(MAC_ENV) arch=x86_64 gcc-12@' Makefile 11 | # We target only MacOS 10.8 or later; otherwise, we get an error. We get an 12 | # error for targeting <10.5 and a warning for <10.8 13 | sed -i '' "s@MACOSX_DEPLOYMENT_TARGET='10.3'@MACOSX_DEPLOYMENT_TARGET='10.8'@" Makefile 14 | 15 | # Some users on old versions of MacOS 10.13 run into the error: 16 | # dyld: cannot load 'PathOfBuilding' (load command 0x80000034 is unknown) 17 | # 18 | # It looks like 0x80000034 is associated with the fixup_chains optimization 19 | # that improves startup time: 20 | # https://www.emergetools.com/blog/posts/iOS15LaunchTime 21 | # 22 | # For compatibility, we disable that using the flag from this thread: 23 | # https://github.com/python/cpython/issues/97524 24 | if ! grep -q "LDFLAGS =" Makefile; then 25 | sed -i '' '/LIBS =/a\ 26 | LDFLAGS = -Wl,-no_fixup_chains 27 | ' Makefile 28 | fi 29 | popd 30 | -------------------------------------------------------------------------------- /editPathOfBuildingBuild.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 3 | 4 | # We remove the `launch.devMode or` to ensure the user's builds are stored not in 5 | # the binary, but within their user directory 6 | sed -i '' 's/if launch.devMode or .*then/if false then/' src/Modules/Main.lua 7 | # Remove the dev-mode notice 8 | sed -i '' 's/if launch.devMode and GetTime.*then/if false then/' src/Modules/Main.lua 9 | 10 | # Remove SSL checks. This fixes a weird problem where cURL doesn't find the 11 | # certificates on M1 Macs. The risk is really low, since the information this 12 | # transfers isn't really sensitive 13 | SED_COMMAND='s/(easy:setopt_url\(.*\))$/\1; easy:setopt(curl.OPT_SSL_VERIFYPEER, false)/' 14 | sed -E -i '' "$SED_COMMAND" src/Launch.lua 15 | sed -E -i '' "$SED_COMMAND" src/Classes/PassiveTree.lua 16 | sed -E -i '' "$SED_COMMAND" src/Classes/TradeQueryGenerator.lua 17 | sed -E -i '' "$SED_COMMAND" src/Classes/TreeTab.lua 18 | sed -E -i '' "$SED_COMMAND" src/Modules/BuildSiteTools.lua 19 | # Do not remove SSL for LaunchInstall and Update as that's more sensitive, but 20 | # also unused. 21 | 22 | # Add nil checks for grantedEffect. Without it, we got an error on 2023-12-18: 23 | # "Error loading main script: Data/Uniques/Special/Generated.lua:239 attempt 24 | # to index local 'grantedEffect' (a nil value) 25 | # ModParser.lua got a similar error 26 | sed -i '' 's/if grantedEffect.support/if grantedEffect ~= nil and grantedEffect.support/' src/Data/Uniques/Special/Generated.lua 27 | sed -i '' 's/if not grantedEffect.hidden/if grantedEffect ~= nil and not grantedEffect.hidden/' src/Modules/ModParser.lua 28 | 29 | # Run remaining setup 30 | unzip runtime-win32.zip lua/xml.lua lua/base64.lua lua/sha1.lua 31 | mv lua/*.lua . 32 | rmdir lua 33 | cp ../lcurl.so . 34 | mv src/* . 35 | rmdir src 36 | -------------------------------------------------------------------------------- /fonts/Bitstream Vera License.txt: -------------------------------------------------------------------------------- 1 | Bitstream Vera Fonts Copyright 2 | 3 | The fonts have a generous copyright, allowing derivative works (as 4 | long as "Bitstream" or "Vera" are not in the names), and full 5 | redistribution (so long as they are not *sold* by themselves). They 6 | can be be bundled, redistributed and sold with any software. 7 | 8 | The fonts are distributed under the following copyright: 9 | 10 | Copyright 11 | ========= 12 | 13 | Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream 14 | Vera is a trademark of Bitstream, Inc. 15 | 16 | Permission is hereby granted, free of charge, to any person obtaining 17 | a copy of the fonts accompanying this license ("Fonts") and associated 18 | documentation files (the "Font Software"), to reproduce and distribute 19 | the Font Software, including without limitation the rights to use, 20 | copy, merge, publish, distribute, and/or sell copies of the Font 21 | Software, and to permit persons to whom the Font Software is furnished 22 | to do so, subject to the following conditions: 23 | 24 | The above copyright and trademark notices and this permission notice 25 | shall be included in all copies of one or more of the Font Software 26 | typefaces. 27 | 28 | The Font Software may be modified, altered, or added to, and in 29 | particular the designs of glyphs or characters in the Fonts may be 30 | modified and additional glyphs or characters may be added to the 31 | Fonts, only if the fonts are renamed to names not containing either 32 | the words "Bitstream" or the word "Vera". 33 | 34 | This License becomes null and void to the extent applicable to Fonts 35 | or Font Software that has been modified and is distributed under the 36 | "Bitstream Vera" names. 37 | 38 | The Font Software may be sold as part of a larger software package but 39 | no copy of one or more of the Font Software typefaces may be sold by 40 | itself. 41 | 42 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 43 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 44 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 45 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL 46 | BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR 47 | OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, 48 | OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR 49 | OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT 50 | SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. 51 | 52 | Except as contained in this notice, the names of Gnome, the Gnome 53 | Foundation, and Bitstream Inc., shall not be used in advertising or 54 | otherwise to promote the sale, use or other dealings in this Font 55 | Software without prior written authorization from the Gnome Foundation 56 | or Bitstream Inc., respectively. For further information, contact: 57 | fonts at gnome dot org. 58 | 59 | Copyright FAQ 60 | ============= 61 | 62 | 1. I don't understand the resale restriction... What gives? 63 | 64 | Bitstream is giving away these fonts, but wishes to ensure its 65 | competitors can't just drop the fonts as is into a font sale system 66 | and sell them as is. It seems fair that if Bitstream can't make money 67 | from the Bitstream Vera fonts, their competitors should not be able to 68 | do so either. You can sell the fonts as part of any software package, 69 | however. 70 | 71 | 2. I want to package these fonts separately for distribution and 72 | sale as part of a larger software package or system. Can I do so? 73 | 74 | Yes. A RPM or Debian package is a "larger software package" to begin 75 | with, and you aren't selling them independently by themselves. 76 | See 1. above. 77 | 78 | 3. Are derivative works allowed? 79 | Yes! 80 | 81 | 4. Can I change or add to the font(s)? 82 | Yes, but you must change the name(s) of the font(s). 83 | 84 | 5. Under what terms are derivative works allowed? 85 | 86 | You must change the name(s) of the fonts. This is to ensure the 87 | quality of the fonts, both to protect Bitstream and Gnome. We want to 88 | ensure that if an application has opened a font specifically of these 89 | names, it gets what it expects (though of course, using fontconfig, 90 | substitutions could still could have occurred during font 91 | opening). You must include the Bitstream copyright. Additional 92 | copyrights can be added, as per copyright law. Happy Font Hacking! 93 | 94 | 6. If I have improvements for Bitstream Vera, is it possible they might get 95 | adopted in future versions? 96 | 97 | Yes. The contract between the Gnome Foundation and Bitstream has 98 | provisions for working with Bitstream to ensure quality additions to 99 | the Bitstream Vera font family. Please contact us if you have such 100 | additions. Note, that in general, we will want such additions for the 101 | entire family, not just a single font, and that you'll have to keep 102 | both Gnome and Jim Lyles, Vera's designer, happy! To make sense to add 103 | glyphs to the font, they must be stylistically in keeping with Vera's 104 | design. Vera cannot become a "ransom note" font. Jim Lyles will be 105 | providing a document describing the design elements used in Vera, as a 106 | guide and aid for people interested in contributing to Vera. 107 | 108 | 7. I want to sell a software package that uses these fonts: Can I do so? 109 | 110 | Sure. Bundle the fonts with your software and sell your software 111 | with the fonts. That is the intent of the copyright. 112 | 113 | 8. If applications have built the names "Bitstream Vera" into them, 114 | can I override this somehow to use fonts of my choosing? 115 | 116 | This depends on exact details of the software. Most open source 117 | systems and software (e.g., Gnome, KDE, etc.) are now converting to 118 | use fontconfig (see www.fontconfig.org) to handle font configuration, 119 | selection and substitution; it has provisions for overriding font 120 | names and subsituting alternatives. An example is provided by the 121 | supplied local.conf file, which chooses the family Bitstream Vera for 122 | "sans", "serif" and "monospace". Other software (e.g., the XFree86 123 | core server) has other mechanisms for font substitution. -------------------------------------------------------------------------------- /fonts/LiberationSans-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hsource/pobfrontend/f0b601b07915b10878f9ec5eb5e9334265692476/fonts/LiberationSans-Bold.ttf -------------------------------------------------------------------------------- /fonts/LiberationSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hsource/pobfrontend/f0b601b07915b10878f9ec5eb5e9334265692476/fonts/LiberationSans-Regular.ttf -------------------------------------------------------------------------------- /fonts/SIL Open Font License.txt: -------------------------------------------------------------------------------- 1 | Digitized data copyright (c) 2010 Google Corporation 2 | with Reserved Font Arimo, Tinos and Cousine. 3 | Copyright (c) 2012 Red Hat, Inc. 4 | with Reserved Font Name Liberation. 5 | 6 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 7 | This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL 8 | 9 | ----------------------------------------------------------- 10 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 11 | ----------------------------------------------------------- 12 | 13 | PREAMBLE 14 | The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others. 15 | 16 | The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives. 17 | 18 | DEFINITIONS 19 | "Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation. 20 | 21 | "Reserved Font Name" refers to any names specified as such after the copyright statement(s). 22 | 23 | "Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s). 24 | 25 | "Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment. 26 | 27 | "Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software. 28 | 29 | PERMISSION & CONDITIONS 30 | Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions: 31 | 32 | 1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself. 33 | 34 | 2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user. 35 | 36 | 3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users. 37 | 38 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission. 39 | 40 | 5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software. 41 | 42 | TERMINATION 43 | This license becomes null and void if any of the above conditions are not met. 44 | 45 | DISCLAIMER 46 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. -------------------------------------------------------------------------------- /fonts/VeraMono.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hsource/pobfrontend/f0b601b07915b10878f9ec5eb5e9334265692476/fonts/VeraMono.ttf -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | #include 12 | #include 13 | #include 14 | #include "main.h" 15 | #include "pobwindow.hpp" 16 | #include "subscript.hpp" 17 | #include 18 | #include 19 | 20 | lua_State *L; 21 | 22 | int dscount; 23 | 24 | POBWindow *pobwindow; 25 | 26 | QRegularExpression colourCodes{R"((\^x.{6})|(\^\d))"}; 27 | 28 | void pushCallback(const char* name) { 29 | lua_getfield(L, LUA_REGISTRYINDEX, "uicallbacks"); 30 | lua_getfield(L, -1, "MainObject"); 31 | lua_remove(L, -2); 32 | lua_getfield(L, -1, name); 33 | lua_insert(L, -2); 34 | } 35 | 36 | void POBWindow::triggerUpdate() { 37 | if (isActive()) { 38 | update(); 39 | } 40 | } 41 | 42 | void POBWindow::initializeGL() { 43 | QImage wimg{1, 1, QImage::Format_Mono}; 44 | wimg.fill(1); 45 | white.reset(new QOpenGLTexture(wimg)); 46 | glClearColor(0.0, 0.0, 0.0, 1.0); 47 | glEnable(GL_TEXTURE_2D); 48 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 49 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 50 | // glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); 51 | // glDisable(GL_LIGHTING); 52 | glDepthMask(GL_FALSE); 53 | glDisable(GL_DEPTH_TEST); 54 | glDisable(GL_ALPHA_TEST); 55 | // glAlphaFunc(GL_GREATER, 0); 56 | glEnable(GL_BLEND); 57 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 58 | } 59 | 60 | void POBWindow::resizeGL(int w, int h) { 61 | width = w; 62 | height = h; 63 | } 64 | 65 | void POBWindow::paintGL() { 66 | isDrawing = true; 67 | glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); 68 | glColor4f(0, 0, 0, 0); 69 | 70 | for (auto& layer : layers) { 71 | layer.second.clear(); 72 | } 73 | dscount = 0; 74 | curLayer = 0; 75 | curSubLayer = 0; 76 | 77 | pushCallback("OnFrame"); 78 | int result = lua_pcall(L, 1, 0, 0); 79 | if (result != 0) { 80 | lua_error(L); 81 | } 82 | 83 | if (dscount > pobwindow->stringCache.maxCost()) { 84 | pobwindow->stringCache.setMaxCost(dscount); 85 | } 86 | 87 | for (auto& layer : layers) { 88 | for (auto& cmd : layer.second) { 89 | cmd->execute(); 90 | } 91 | } 92 | isDrawing = false; 93 | } 94 | 95 | void POBWindow::subScriptFinished() { 96 | bool clean = true; 97 | for (int i = 0;i < subScriptList.size();i++) { 98 | if (subScriptList[i].get()) { 99 | clean = false; 100 | if (subScriptList[i]->isFinished()) { 101 | subScriptList[i]->onSubFinished(L, i); 102 | subScriptList[i].reset(); 103 | } 104 | } 105 | } 106 | if (clean) { 107 | subScriptList.clear(); 108 | } 109 | } 110 | 111 | void POBWindow::mouseMoveEvent(QMouseEvent *event) { 112 | update(); 113 | } 114 | 115 | void pushMouseString(QMouseEvent *event) { 116 | switch (event->button()) { 117 | case Qt::LeftButton: 118 | lua_pushstring(L, "LEFTBUTTON"); 119 | break; 120 | case Qt::RightButton: 121 | lua_pushstring(L, "RIGHTBUTTON"); 122 | break; 123 | case Qt::MiddleButton: 124 | lua_pushstring(L, "MIDDLEBUTTON"); 125 | break; 126 | default: 127 | std::cout << "MOUSE STRING? " << event->button() << std::endl; 128 | } 129 | } 130 | 131 | void POBWindow::mousePressEvent(QMouseEvent *event) { 132 | pushCallback("OnKeyDown"); 133 | pushMouseString(event); 134 | lua_pushboolean(L, false); 135 | int result = lua_pcall(L, 3, 0, 0); 136 | if (result != 0) { 137 | lua_error(L); 138 | } 139 | } 140 | 141 | void POBWindow::mouseReleaseEvent(QMouseEvent *event) { 142 | pushCallback("OnKeyUp"); 143 | pushMouseString(event); 144 | int result = lua_pcall(L, 2, 0, 0); 145 | if (result != 0) { 146 | lua_error(L); 147 | } 148 | } 149 | 150 | void POBWindow::mouseDoubleClickEvent(QMouseEvent *event) { 151 | pushCallback("OnKeyDown"); 152 | pushMouseString(event); 153 | lua_pushboolean(L, true); 154 | int result = lua_pcall(L, 3, 0, 0); 155 | if (result != 0) { 156 | lua_error(L); 157 | } 158 | } 159 | 160 | void POBWindow::wheelEvent(QWheelEvent *event) { 161 | pushCallback("OnKeyUp"); 162 | if (event->angleDelta().y() > 0) { 163 | lua_pushstring(L, "WHEELUP"); 164 | } else if (event->angleDelta().y() < 0) { 165 | lua_pushstring(L, "WHEELDOWN"); 166 | } else { 167 | return; 168 | } 169 | lua_pushboolean(L, false); 170 | int result = lua_pcall(L, 3, 0, 0); 171 | if (result != 0) { 172 | lua_error(L); 173 | } 174 | } 175 | 176 | bool pushKeyString(int keycode) { 177 | switch (keycode) { 178 | case Qt::Key_Escape: 179 | lua_pushstring(L, "ESCAPE"); 180 | break; 181 | case Qt::Key_Tab: 182 | lua_pushstring(L, "TAB"); 183 | break; 184 | case Qt::Key_Return: 185 | case Qt::Key_Enter: 186 | lua_pushstring(L, "RETURN"); 187 | break; 188 | case Qt::Key_Backspace: 189 | lua_pushstring(L, "BACK"); 190 | break; 191 | case Qt::Key_Delete: 192 | lua_pushstring(L, "DELETE"); 193 | break; 194 | case Qt::Key_Home: 195 | lua_pushstring(L, "HOME"); 196 | break; 197 | case Qt::Key_End: 198 | lua_pushstring(L, "END"); 199 | break; 200 | case Qt::Key_Up: 201 | lua_pushstring(L, "UP"); 202 | break; 203 | case Qt::Key_Down: 204 | lua_pushstring(L, "DOWN"); 205 | break; 206 | case Qt::Key_Left: 207 | lua_pushstring(L, "LEFT"); 208 | break; 209 | case Qt::Key_Right: 210 | lua_pushstring(L, "RIGHT"); 211 | break; 212 | case Qt::Key_PageUp: 213 | lua_pushstring(L, "PAGEUP"); 214 | break; 215 | case Qt::Key_PageDown: 216 | lua_pushstring(L, "PAGEDOWN"); 217 | break; 218 | case Qt::Key_F6: 219 | lua_pushstring(L, "F6"); 220 | break; 221 | default: 222 | return false; 223 | } 224 | return true; 225 | } 226 | 227 | void POBWindow::keyPressEvent(QKeyEvent *event) { 228 | pushCallback("OnKeyDown"); 229 | if (!pushKeyString(event->key())) { 230 | if (event->key() >= ' ' && event->key() <= '~') { 231 | char s[2]; 232 | if (event->key() >= 'A' && event->key() <= 'Z' && !(QGuiApplication::keyboardModifiers() & Qt::ShiftModifier)) { 233 | s[0] = event->key() + 32; 234 | } else { 235 | s[0] = event->key(); 236 | } 237 | s[1] = 0; 238 | if (!(QGuiApplication::keyboardModifiers() & Qt::ControlModifier)) { 239 | lua_pop(L, 2); 240 | pushCallback("OnChar"); 241 | } 242 | lua_pushstring(L, s); 243 | } else { 244 | lua_pushstring(L, "ASDF"); 245 | //std::cout << "UNHANDLED KEYDOWN" << std::endl; 246 | } 247 | } 248 | lua_pushboolean(L, false); 249 | int result = lua_pcall(L, 3, 0, 0); 250 | if (result != 0) { 251 | lua_error(L); 252 | } 253 | } 254 | 255 | void POBWindow::keyReleaseEvent(QKeyEvent *event) { 256 | pushCallback("OnKeyUp"); 257 | if (!pushKeyString(event->key())) { 258 | lua_pushstring(L, "ASDF"); 259 | //std::cout << "UNHANDLED KEYUP" << std::endl; 260 | } 261 | int result = lua_pcall(L, 2, 0, 0); 262 | if (result != 0) { 263 | lua_error(L); 264 | } 265 | } 266 | 267 | void POBWindow::LAssert(lua_State* L, int cond, const char* fmt, ...) { 268 | if ( !cond ) { 269 | va_list va; 270 | va_start(va, fmt); 271 | lua_pushvfstring(L, fmt, va); 272 | va_end(va); 273 | lua_error(L); 274 | } 275 | } 276 | 277 | int POBWindow::IsUserData(lua_State* L, int index, const char* metaName) 278 | { 279 | if (lua_type(L, index) != LUA_TUSERDATA || lua_getmetatable(L, index) == 0) { 280 | return 0; 281 | } 282 | lua_getfield(L, LUA_REGISTRYINDEX, metaName); 283 | int ret = lua_rawequal(L, -2, -1); 284 | lua_pop(L, 2); 285 | return ret; 286 | } 287 | 288 | void POBWindow::SetDrawLayer(int layer) { 289 | SetDrawLayer(layer, 0); 290 | } 291 | 292 | void POBWindow::SetDrawLayer(int layer, int subLayer) { 293 | if (layer == curLayer && subLayer == curSubLayer) { 294 | return; 295 | } 296 | 297 | curLayer = layer; 298 | curSubLayer = subLayer; 299 | } 300 | 301 | 302 | void POBWindow::AppendCmd(std::unique_ptr cmd) { 303 | layers[{curLayer, curSubLayer}].emplace_back(std::move(cmd)); 304 | } 305 | 306 | void POBWindow::DrawColor(const float col[4]) { 307 | if (col) { 308 | drawColor[0] = col[0]; 309 | drawColor[1] = col[1]; 310 | drawColor[2] = col[2]; 311 | drawColor[3] = col[3]; 312 | } else { 313 | drawColor[0] = 1.0f; 314 | drawColor[1] = 1.0f; 315 | drawColor[2] = 1.0f; 316 | drawColor[3] = 1.0f; 317 | } 318 | AppendCmd(std::make_unique(drawColor)); 319 | } 320 | 321 | void POBWindow::DrawColor(uint32_t col) { 322 | drawColor[0] = ((col >> 16) & 0xFF) / 255.0f; 323 | drawColor[1] = ((col >> 8) & 0xFF) / 255.0f; 324 | drawColor[2] = (col & 0xFF) / 255.0f; 325 | drawColor[3] = (col >> 24) / 255.0f; 326 | } 327 | 328 | 329 | // Color escape table 330 | static const float colorEscape[10][4] = { 331 | {0.0f, 0.0f, 0.0f, 1.0f}, 332 | {1.0f, 0.0f, 0.0f, 1.0f}, 333 | {0.0f, 1.0f, 0.0f, 1.0f}, 334 | {0.0f, 0.0f, 1.0f, 1.0f}, 335 | {1.0f, 1.0f, 0.0f, 1.0f}, 336 | {1.0f, 0.0f, 1.0f, 1.0f}, 337 | {0.0f, 1.0f, 1.0f, 1.0f}, 338 | {1.0f, 1.0f, 1.0f, 1.0f}, 339 | {0.7f, 0.7f, 0.7f, 1.0f}, 340 | {0.4f, 0.4f, 0.4f, 1.0f} 341 | }; 342 | 343 | int IsColorEscape(const char* str) 344 | { 345 | if (str[0] != '^') { 346 | return 0; 347 | } 348 | if (isdigit(str[1])) { 349 | return 2; 350 | } else if (str[1] == 'x' || str[1] == 'X') { 351 | for (int c = 0; c < 6; c++) { 352 | if ( !isxdigit(str[c + 2]) ) { 353 | return 0; 354 | } 355 | } 356 | return 8; 357 | } 358 | return 0; 359 | } 360 | 361 | void ReadColorEscape(const char* str, float* out) 362 | { 363 | int len = IsColorEscape(str); 364 | switch (len) { 365 | case 2: 366 | out[0] = colorEscape[str[1] - '0'][0]; 367 | out[1] = colorEscape[str[1] - '0'][1]; 368 | out[2] = colorEscape[str[1] - '0'][2]; 369 | break; 370 | case 8: 371 | { 372 | int xr, xg, xb; 373 | sscanf(str + 2, "%2x%2x%2x", &xr, &xg, &xb); 374 | out[0] = xr / 255.0f; 375 | out[1] = xg / 255.0f; 376 | out[2] = xb / 255.0f; 377 | } 378 | break; 379 | } 380 | } 381 | 382 | // ========= 383 | // Callbacks 384 | // ========= 385 | 386 | static int l_SetCallback(lua_State* L) 387 | { 388 | int n = lua_gettop(L); 389 | pobwindow->LAssert(L, n >= 1, "Usage: SetCallback(name[, func])"); 390 | pobwindow->LAssert(L, lua_isstring(L, 1), "SetCallback() argument 1: expected string, got %t", 1); 391 | lua_pushvalue(L, 1); 392 | if (n >= 2) { 393 | pobwindow->LAssert(L, lua_isfunction(L, 2) || lua_isnil(L, 2), "SetCallback() argument 2: expected function or nil, got %t", 2); 394 | lua_pushvalue(L, 2); 395 | } else { 396 | lua_pushnil(L); 397 | } 398 | lua_settable(L, lua_upvalueindex(1)); 399 | return 0; 400 | } 401 | 402 | static int l_GetCallback(lua_State* L) 403 | { 404 | int n = lua_gettop(L); 405 | pobwindow->LAssert(L, n >= 1, "Usage: GetCallback(name)"); 406 | pobwindow->LAssert(L, lua_isstring(L, 1), "GetCallback() argument 1: expected string, got %t", 1); 407 | lua_pushvalue(L, 1); 408 | lua_gettable(L, lua_upvalueindex(1)); 409 | return 1; 410 | } 411 | 412 | static int l_SetMainObject(lua_State* L) 413 | { 414 | int n = lua_gettop(L); 415 | lua_pushstring(L, "MainObject"); 416 | if (n >= 1) { 417 | pobwindow->LAssert(L, lua_istable(L, 1) || lua_isnil(L, 1), "SetMainObject() argument 1: expected table or nil, got %t", 1); 418 | lua_pushvalue(L, 1); 419 | } else { 420 | lua_pushnil(L); 421 | } 422 | lua_settable(L, lua_upvalueindex(1)); 423 | return 0; 424 | } 425 | 426 | // ============= 427 | // Image Handles 428 | // ============= 429 | 430 | struct imgHandle_s { 431 | std::shared_ptr *hnd; 432 | QImage* img; 433 | }; 434 | 435 | static int l_NewImageHandle(lua_State* L) 436 | { 437 | auto imgHandle = (imgHandle_s*)lua_newuserdata(L, sizeof(imgHandle_s)); 438 | imgHandle->hnd = nullptr; 439 | imgHandle->img = nullptr; 440 | lua_pushvalue(L, lua_upvalueindex(1)); 441 | lua_setmetatable(L, -2); 442 | return 1; 443 | } 444 | 445 | static imgHandle_s* GetImgHandle(lua_State* L, const char* method, bool loaded) 446 | { 447 | pobwindow->LAssert(L, pobwindow->IsUserData(L, 1, "uiimghandlemeta"), "imgHandle:%s() must be used on an image handle", method); 448 | auto imgHandle = (imgHandle_s*)lua_touserdata(L, 1); 449 | lua_remove(L, 1); 450 | if (loaded) { 451 | //pobwindow->LAssert(L, imgHandle->hnd != NULL, "imgHandle:%s(): image handle has no image loaded", method); 452 | } 453 | return imgHandle; 454 | } 455 | 456 | static int l_imgHandleGC(lua_State* L) 457 | { 458 | imgHandle_s* imgHandle = GetImgHandle(L, "__gc", false); 459 | delete imgHandle->hnd; 460 | delete imgHandle->img; 461 | return 0; 462 | } 463 | 464 | static int l_imgHandleLoad(lua_State* L) 465 | { 466 | imgHandle_s* imgHandle = GetImgHandle(L, "Load", false); 467 | int n = lua_gettop(L); 468 | pobwindow->LAssert(L, n >= 1, "Usage: imgHandle:Load(fileName[, flag1[, flag2...]])"); 469 | pobwindow->LAssert(L, lua_isstring(L, 1), "imgHandle:Load() argument 1: expected string, got %t", 1); 470 | QString fileName = lua_tostring(L, 1); 471 | QString fullFileName; 472 | if (fileName.contains(':') || pobwindow->scriptWorkDir.isEmpty()) { 473 | fullFileName = fileName; 474 | } else { 475 | fullFileName = pobwindow->scriptWorkDir + QDir::separator() + fileName; 476 | } 477 | 478 | delete imgHandle->hnd; 479 | imgHandle->hnd = new std::shared_ptr(); 480 | delete imgHandle->img; 481 | int flags = TF_NOMIPMAP; 482 | for (int f = 2; f <= n; f++) { 483 | if ( !lua_isstring(L, f) ) { 484 | continue; 485 | } 486 | const char* flag = lua_tostring(L, f); 487 | if ( !strcmp(flag, "ASYNC") ) { 488 | flags|= TF_ASYNC; 489 | } else if ( !strcmp(flag, "CLAMP") ) { 490 | flags|= TF_CLAMP; 491 | } else if ( !strcmp(flag, "MIPMAP") ) { 492 | flags&= ~TF_NOMIPMAP; 493 | } else { 494 | pobwindow->LAssert(L, 0, "imgHandle:Load(): unrecognised flag '%s'", flag); 495 | } 496 | } 497 | imgHandle->img = new QImage(); 498 | imgHandle->img->load(fullFileName); 499 | imgHandle->img->setText("fname", fullFileName); 500 | //imgHandle->hnd = new QOpenGLTexture(img); 501 | //pobwindow->renderer->RegisterShader(fullFileName, flags); 502 | return 0; 503 | } 504 | 505 | static int l_imgHandleUnload(lua_State* L) 506 | { 507 | imgHandle_s* imgHandle = GetImgHandle(L, "Unload", false); 508 | delete imgHandle->hnd; 509 | imgHandle->hnd = nullptr; 510 | delete imgHandle->img; 511 | imgHandle->img = nullptr; 512 | return 0; 513 | } 514 | 515 | static int l_imgHandleIsValid(lua_State* L) 516 | { 517 | imgHandle_s* imgHandle = GetImgHandle(L, "IsValid", false); 518 | lua_pushboolean(L, imgHandle->hnd != nullptr); 519 | return 1; 520 | } 521 | 522 | static int l_imgHandleIsLoading(lua_State* L) 523 | { 524 | imgHandle_s* imgHandle = GetImgHandle(L, "IsLoading", true); 525 | // int width, height; 526 | // pobwindow->renderer->GetShaderImageSize(imgHandle->hnd, width, height); 527 | lua_pushboolean(L, false); 528 | return 1; 529 | } 530 | 531 | static int l_imgHandleSetLoadingPriority(lua_State* L) 532 | { 533 | imgHandle_s* imgHandle = GetImgHandle(L, "SetLoadingPriority", true); 534 | int n = lua_gettop(L); 535 | pobwindow->LAssert(L, n >= 1, "Usage: imgHandle:SetLoadingPriority(pri)"); 536 | pobwindow->LAssert(L, lua_isnumber(L, 1), "imgHandle:SetLoadingPriority() argument 1: expected number, got %t", 1); 537 | //pobwindow->renderer->SetShaderLoadingPriority(imgHandle->hnd, (int)lua_tointeger(L, 1)); 538 | return 0; 539 | } 540 | 541 | static int l_imgHandleImageSize(lua_State* L) 542 | { 543 | imgHandle_s* imgHandle = GetImgHandle(L, "ImageSize", true); 544 | QSize size(imgHandle->img->size()); 545 | lua_pushinteger(L, size.width()); 546 | lua_pushinteger(L, size.height()); 547 | return 2; 548 | } 549 | 550 | // ========= 551 | // Rendering 552 | // ========= 553 | 554 | static int l_RenderInit(lua_State* L) 555 | { 556 | return 0; 557 | } 558 | 559 | static int l_GetScreenSize(lua_State* L) 560 | { 561 | lua_pushinteger(L, pobwindow->width); 562 | lua_pushinteger(L, pobwindow->height); 563 | return 2; 564 | } 565 | 566 | static int l_SetClearColor(lua_State* L) 567 | { 568 | int n = lua_gettop(L); 569 | pobwindow->LAssert(L, n >= 3, "Usage: SetClearColor(red, green, blue[, alpha])"); 570 | float color[4]; 571 | for (int i = 1; i <= 3; i++) { 572 | pobwindow->LAssert(L, lua_isnumber(L, i), "SetClearColor() argument %d: expected number, got %t", i, i); 573 | color[i-1] = (float)lua_tonumber(L, i); 574 | } 575 | if (n >= 4 && !lua_isnil(L, 4)) { 576 | pobwindow->LAssert(L, lua_isnumber(L, 4), "SetClearColor() argument 4: expected number or nil, got %t", 4); 577 | color[3] = (float)lua_tonumber(L, 4); 578 | } else { 579 | color[3] = 1.0; 580 | } 581 | glClearColor(color[0], color[1], color[2], color[3]); 582 | return 0; 583 | } 584 | 585 | static int l_SetDrawLayer(lua_State* L) 586 | { 587 | int n = lua_gettop(L); 588 | pobwindow->LAssert(L, n >= 1, "Usage: SetDrawLayer({layer|nil}[, subLayer])"); 589 | pobwindow->LAssert(L, lua_isnumber(L, 1) || lua_isnil(L, 1), "SetDrawLayer() argument 1: expected number or nil, got %t", 1); 590 | if (n >= 2) { 591 | pobwindow->LAssert(L, lua_isnumber(L, 2), "SetDrawLayer() argument 2: expected number, got %t", 2); 592 | } 593 | if (lua_isnil(L, 1)) { 594 | pobwindow->LAssert(L, n >= 2, "SetDrawLayer(): must provide subLayer if layer is nil"); 595 | pobwindow->SetDrawSubLayer(lua_tointeger(L, 2)); 596 | } else if (n >= 2) { 597 | pobwindow->SetDrawLayer(lua_tointeger(L, 1), lua_tointeger(L, 2)); 598 | } else { 599 | pobwindow->SetDrawLayer(lua_tointeger(L, 1)); 600 | } 601 | return 0; 602 | } 603 | 604 | void ViewportCmd::execute() { 605 | glViewport(x, pobwindow->height - y - h, w, h); 606 | glMatrixMode(GL_PROJECTION); 607 | glLoadIdentity(); 608 | glOrtho(0, (float)w, (float)h, 0, -9999, 9999); 609 | glMatrixMode(GL_MODELVIEW); 610 | glLoadIdentity(); 611 | } 612 | 613 | static int l_SetViewport(lua_State* L) 614 | { 615 | int n = lua_gettop(L); 616 | if (n) { 617 | pobwindow->LAssert(L, n >= 4, "Usage: SetViewport([x, y, width, height])"); 618 | for (int i = 1; i <= 4; i++) { 619 | pobwindow->LAssert(L, lua_isnumber(L, i), "SetViewport() argument %d: expected number, got %t", i, i); 620 | } 621 | pobwindow->AppendCmd(std::make_unique((int)lua_tointeger(L, 1), (int)lua_tointeger(L, 2), (int)lua_tointeger(L, 3), (int)lua_tointeger(L, 4))); 622 | } else { 623 | pobwindow->AppendCmd(std::make_unique(0, 0, pobwindow->width, pobwindow->height)); 624 | } 625 | return 0; 626 | } 627 | 628 | static int l_SetDrawColor(lua_State* L) 629 | { 630 | pobwindow->LAssert(L, pobwindow->isDrawing, "SetDrawColor() called outside of OnFrame"); 631 | int n = lua_gettop(L); 632 | pobwindow->LAssert(L, n >= 1, "Usage: SetDrawColor(red, green, blue[, alpha]) or SetDrawColor(escapeStr)"); 633 | float color[4]; 634 | if (lua_type(L, 1) == LUA_TSTRING) { 635 | pobwindow->LAssert(L, IsColorEscape(lua_tostring(L, 1)), "SetDrawColor() argument 1: invalid color escape sequence"); 636 | ReadColorEscape(lua_tostring(L, 1), color); 637 | color[3] = 1.0; 638 | } else { 639 | pobwindow->LAssert(L, n >= 3, "Usage: SetDrawColor(red, green, blue[, alpha]) or SetDrawColor(escapeStr)"); 640 | for (int i = 1; i <= 3; i++) { 641 | pobwindow->LAssert(L, lua_isnumber(L, i), "SetDrawColor() argument %d: expected number, got %t", i, i); 642 | color[i-1] = (float)lua_tonumber(L, i); 643 | } 644 | if (n >= 4 && !lua_isnil(L, 4)) { 645 | pobwindow->LAssert(L, lua_isnumber(L, 4), "SetDrawColor() argument 4: expected number or nil, got %t", 4); 646 | color[3] = (float)lua_tonumber(L, 4); 647 | } else { 648 | color[3] = 1.0; 649 | } 650 | } 651 | pobwindow->DrawColor(color); 652 | return 0; 653 | } 654 | 655 | static int l_DrawImage(lua_State* L) 656 | { 657 | pobwindow->LAssert(L, pobwindow->isDrawing, "DrawImage() called outside of OnFrame"); 658 | int n = lua_gettop(L); 659 | pobwindow->LAssert(L, n >= 5, "Usage: DrawImage({imgHandle|nil}, left, top, width, height[, tcLeft, tcTop, tcRight, tcBottom])"); 660 | pobwindow->LAssert(L, lua_isnil(L, 1) || pobwindow->IsUserData(L, 1, "uiimghandlemeta"), "DrawImage() argument 1: expected image handle or nil, got %t", 1); 661 | std::shared_ptr hnd; 662 | if ( !lua_isnil(L, 1) ) { 663 | auto imgHandle = (imgHandle_s*)lua_touserdata(L, 1); 664 | if (imgHandle->hnd->get() == nullptr) { 665 | imgHandle->hnd->reset(new QOpenGLTexture(*(imgHandle->img))); 666 | if (!(*imgHandle->hnd)->isCreated()) { 667 | //std::cout << "BROKEN TEXTURE " << imgHandle->img->text("fname").toStdString() << std::endl; 668 | *imgHandle->hnd = pobwindow->white; 669 | } 670 | } 671 | pobwindow->LAssert(L, imgHandle->hnd != nullptr, "DrawImage(): image handle has no image loaded"); 672 | hnd = *imgHandle->hnd; 673 | } 674 | float arg[8]; 675 | if (n > 5) { 676 | pobwindow->LAssert(L, n >= 9, "DrawImage(): incomplete set of texture coordinates provided"); 677 | for (int i = 2; i <= 9; i++) { 678 | pobwindow->LAssert(L, lua_isnumber(L, i), "DrawImage() argument %d: expected number, got %t", i, i); 679 | arg[i-2] = (float)lua_tonumber(L, i); 680 | } 681 | pobwindow->AppendCmd(std::make_unique(hnd, arg[0], arg[1], arg[2], arg[3], arg[4], arg[5], arg[6], arg[7])); 682 | } else { 683 | for (int i = 2; i <= 5; i++) { 684 | pobwindow->LAssert(L, lua_isnumber(L, i), "DrawImage() argument %d: expected number, got %t", i, i); 685 | arg[i-2] = (float)lua_tonumber(L, i); 686 | } 687 | pobwindow->AppendCmd(std::make_unique(hnd, arg[0], arg[1], arg[2], arg[3])); 688 | } 689 | return 0; 690 | } 691 | 692 | void DrawImageQuadCmd::execute() { 693 | if (tex != nullptr && tex->isCreated()) { 694 | tex->bind(); 695 | } else { 696 | pobwindow->white->bind(); 697 | } 698 | glBegin(GL_TRIANGLE_FAN); 699 | for (int v = 0; v < 4; v++) { 700 | glTexCoord2d(s[v], t[v]); 701 | glVertex2d(x[v], y[v]); 702 | } 703 | glEnd(); 704 | } 705 | 706 | static int l_DrawImageQuad(lua_State* L) 707 | { 708 | pobwindow->LAssert(L, pobwindow->isDrawing, "DrawImageQuad() called outside of OnFrame"); 709 | int n = lua_gettop(L); 710 | pobwindow->LAssert(L, n >= 9, "Usage: DrawImageQuad({imgHandle|nil}, x1, y1, x2, y2, x3, y3, x4, y4[, s1, t1, s2, t2, s3, t3, s4, t4])"); 711 | pobwindow->LAssert(L, lua_isnil(L, 1) || pobwindow->IsUserData(L, 1, "uiimghandlemeta"), "DrawImageQuad() argument 1: expected image handle or nil, got %t", 1); 712 | std::shared_ptr hnd; 713 | if ( !lua_isnil(L, 1) ) { 714 | auto imgHandle = (imgHandle_s*)lua_touserdata(L, 1); 715 | if ((*imgHandle->hnd).get() == nullptr) { 716 | (*imgHandle->hnd).reset(new QOpenGLTexture(*(imgHandle->img))); 717 | if (!(*imgHandle->hnd)->isCreated()) { 718 | // std::cout << "BROKEN TEXTURE" << imgHandle->img->text("fname").toStdString() << std::endl; 719 | *imgHandle->hnd = pobwindow->white; 720 | } 721 | } 722 | pobwindow->LAssert(L, imgHandle->hnd != nullptr, "DrawImageQuad(): image handle has no image loaded"); 723 | hnd = *imgHandle->hnd; 724 | } 725 | float arg[16]; 726 | if (n > 9) { 727 | pobwindow->LAssert(L, n >= 17, "DrawImageQuad(): incomplete set of texture coordinates provided"); 728 | for (int i = 2; i <= 17; i++) { 729 | pobwindow->LAssert(L, lua_isnumber(L, i), "DrawImageQuad() argument %d: expected number, got %t", i, i); 730 | arg[i-2] = (float)lua_tonumber(L, i); 731 | } 732 | pobwindow->AppendCmd(std::make_unique(hnd, arg[0], arg[1], arg[2], arg[3], arg[4], arg[5], arg[6], arg[7], arg[8], arg[9], arg[10], arg[11], arg[12], arg[13], arg[14], arg[15])); 733 | } else { 734 | for (int i = 2; i <= 9; i++) { 735 | pobwindow->LAssert(L, lua_isnumber(L, i), "DrawImageQuad() argument %d: expected number, got %t", i, i); 736 | arg[i-2] = (float)lua_tonumber(L, i); 737 | } 738 | pobwindow->AppendCmd(std::make_unique(hnd, arg[0], arg[1], arg[2], arg[3], arg[4], arg[5], arg[6], arg[7])); 739 | } 740 | return 0; 741 | } 742 | 743 | DrawStringCmd::DrawStringCmd(float X, float Y, int Align, int Size, int Font, const char *Text) : text(Text) { 744 | dscount++; 745 | if (text.size() >= 2 && text[0] == '^') { 746 | switch(text[1].toLatin1()) { 747 | case '0': 748 | setCol(0.0f, 0.0f, 0.0f); 749 | break; 750 | case '1': 751 | setCol(1.0f, 0.0f, 0.0f); 752 | break; 753 | case '2': 754 | setCol(0.0f, 1.0f, 0.0f); 755 | break; 756 | case '3': 757 | setCol(0.0f, 0.0f, 1.0f); 758 | break; 759 | case '4': 760 | setCol(1.0f, 1.0f, 0.0f); 761 | break; 762 | case '5': 763 | setCol(1.0f, 0.0f, 1.0f); 764 | break; 765 | case '6': 766 | setCol(0.0f, 1.0f, 1.0f); 767 | break; 768 | case '7': 769 | setCol(1.0f, 1.0f, 1.0f); 770 | break; 771 | case '8': 772 | setCol(0.7f, 0.7f, 0.7f); 773 | break; 774 | case '9': 775 | setCol(0.4f, 0.4f, 0.4f); 776 | break; 777 | case 'x': 778 | int xr, xg, xb; 779 | sscanf(text.toStdString().c_str() + 2, "%2x%2x%2x", &xr, &xg, &xb); 780 | setCol(xr / 255.0f, xg / 255.0f, xb / 255.0f); 781 | break; 782 | default: 783 | break; 784 | } 785 | } else { 786 | col[3] = 0; 787 | } 788 | int count = 0; 789 | for (auto i = colourCodes.globalMatch(text);i.hasNext();i.next()) { 790 | count += 1; 791 | } 792 | if (count > 1) { 793 | //std::cout << text.toStdString().c_str() << " " << count << std::endl; 794 | } 795 | text.remove(colourCodes); 796 | 797 | QString cacheKey = (QString::number(Font) + "_" + QString::number(Size) + "_" + text); 798 | if (pobwindow->stringCache.contains(cacheKey)) { 799 | tex = *pobwindow->stringCache[cacheKey]; 800 | } else { 801 | QString fontName; 802 | switch (Font) { 803 | case 1: 804 | fontName = "Liberation Sans"; 805 | break; 806 | case 2: 807 | fontName = "Liberation Sans Bold"; 808 | break; 809 | case 0: 810 | default: 811 | fontName = "Bitstream Vera Sans Mono"; 812 | break; 813 | } 814 | QFont font(fontName); 815 | font.setPixelSize(Size + pobwindow->fontFudge); 816 | QFontMetrics fm(font); 817 | QSize size = fm.size(0, text); 818 | 819 | QImage brush(size, QImage::Format_ARGB32); 820 | brush.fill(QColor(255, 255, 255, 0)); 821 | tex = nullptr; 822 | if (brush.width() && brush.height()) { 823 | QPainter p(&brush); 824 | p.setPen(QColor(255, 255, 255, 255)); 825 | p.setFont(font); 826 | p.setCompositionMode(QPainter::CompositionMode_Plus); 827 | p.drawText(0, 0, size.width(), size.height(), 0, text); 828 | p.end(); 829 | tex.reset(new QOpenGLTexture(brush)); 830 | } 831 | pobwindow->stringCache.insert(cacheKey, new std::shared_ptr(tex)); 832 | } 833 | int width = 0; 834 | int height = 0; 835 | if (tex.get() != nullptr) { 836 | width = tex->width(); 837 | height = tex->height(); 838 | } 839 | 840 | switch (Align) { 841 | case F_CENTRE: 842 | X = floor((pobwindow->width - width) / 2.0f + X); 843 | break; 844 | case F_RIGHT: 845 | X = floor(pobwindow->width - width - X); 846 | break; 847 | case F_CENTRE_X: 848 | X = floor(X - width / 2.0f); 849 | break; 850 | case F_RIGHT_X: 851 | X = floor(X - width) + 5; 852 | break; 853 | } 854 | x[0] = X; 855 | y[0] = Y; 856 | x[1] = X + width; 857 | y[1] = Y; 858 | x[2] = X + width; 859 | y[2] = Y + height; 860 | x[3] = X; 861 | y[3] = Y + height; 862 | 863 | s[0] = 0; 864 | t[0] = 0; 865 | s[1] = 1; 866 | t[1] = 0; 867 | s[2] = 1; 868 | t[2] = 1; 869 | s[3] = 0; 870 | t[3] = 1; 871 | } 872 | 873 | static int l_DrawString(lua_State* L) 874 | { 875 | pobwindow->LAssert(L, pobwindow->isDrawing, "DrawString() called outside of OnFrame"); 876 | int n = lua_gettop(L); 877 | pobwindow->LAssert(L, n >= 6, "Usage: DrawString(left, top, align, height, font, text)"); 878 | pobwindow->LAssert(L, lua_isnumber(L, 1), "DrawString() argument 1: expected number, got %t", 1); 879 | pobwindow->LAssert(L, lua_isnumber(L, 2), "DrawString() argument 2: expected number, got %t", 2); 880 | pobwindow->LAssert(L, lua_isstring(L, 3) || lua_isnil(L, 3), "DrawString() argument 3: expected string or nil, got %t", 3); 881 | pobwindow->LAssert(L, lua_isnumber(L, 4), "DrawString() argument 4: expected number, got %t", 4); 882 | pobwindow->LAssert(L, lua_isstring(L, 5), "DrawString() argument 5: expected string, got %t", 5); 883 | pobwindow->LAssert(L, lua_isstring(L, 6), "DrawString() argument 6: expected string, got %t", 6); 884 | static const char* alignMap[6] = { "LEFT", "CENTER", "RIGHT", "CENTER_X", "RIGHT_X", nullptr }; 885 | static const char* fontMap[4] = { "FIXED", "VAR", "VAR BOLD", nullptr }; 886 | pobwindow->AppendCmd(std::make_unique( 887 | (float)lua_tonumber(L, 1), (float)lua_tonumber(L, 2), luaL_checkoption(L, 3, "LEFT", alignMap), 888 | (int)lua_tointeger(L, 4), luaL_checkoption(L, 5, "FIXED", fontMap), lua_tostring(L, 6) 889 | )); 890 | return 0; 891 | } 892 | 893 | static int l_DrawStringWidth(lua_State* L) 894 | { 895 | int n = lua_gettop(L); 896 | pobwindow->LAssert(L, n >= 3, "Usage: DrawStringWidth(height, font, text)"); 897 | pobwindow->LAssert(L, lua_isnumber(L, 1), "DrawStringWidth() argument 1: expected number, got %t", 1); 898 | pobwindow->LAssert(L, lua_isstring(L, 2), "DrawStringWidth() argument 2: expected string, got %t", 2); 899 | pobwindow->LAssert(L, lua_isstring(L, 3), "DrawStringWidth() argument 3: expected string, got %t", 3); 900 | int fontsize = lua_tointeger(L, 1); 901 | QString fontName = lua_tostring(L, 2); 902 | QString fontKey = "0"; 903 | if (fontName == "VAR") { 904 | fontName = "Liberation Sans"; 905 | fontKey = "1"; 906 | } else if (fontName == "VAR BOLD") { 907 | fontName = "Liberation Sans Bold"; 908 | fontKey = "2"; 909 | } else { 910 | fontName = "Bitstream Vera Sans Mono"; 911 | } 912 | QString text(lua_tostring(L, 3)); 913 | 914 | text.remove(colourCodes); 915 | 916 | QString cacheKey = (fontKey + "_" + QString::number(fontsize) + "_" + text); 917 | if (pobwindow->stringCache.contains(cacheKey) && pobwindow->stringCache[cacheKey]->get()) { 918 | lua_pushinteger(L, (*pobwindow->stringCache[cacheKey])->width()); 919 | return 1; 920 | } 921 | 922 | QFont font(fontName); 923 | font.setPixelSize(fontsize + pobwindow->fontFudge); 924 | QFontMetrics fm(font); 925 | lua_pushinteger(L, fm.size(0, text).width()); 926 | return 1; 927 | } 928 | 929 | static int l_DrawStringCursorIndex(lua_State* L) 930 | { 931 | int n = lua_gettop(L); 932 | pobwindow->LAssert(L, n >= 5, "Usage: DrawStringCursorIndex(height, font, text, cursorX, cursorY)"); 933 | pobwindow->LAssert(L, lua_isnumber(L, 1), "DrawStringCursorIndex() argument 1: expected number, got %t", 1); 934 | pobwindow->LAssert(L, lua_isstring(L, 2), "DrawStringCursorIndex() argument 2: expected string, got %t", 2); 935 | pobwindow->LAssert(L, lua_isstring(L, 3), "DrawStringCursorIndex() argument 3: expected string, got %t", 3); 936 | pobwindow->LAssert(L, lua_isnumber(L, 4), "DrawStringCursorIndex() argument 4: expected number, got %t", 4); 937 | pobwindow->LAssert(L, lua_isnumber(L, 5), "DrawStringCursorIndex() argument 5: expected number, got %t", 5); 938 | 939 | int fontsize = lua_tointeger(L, 1); 940 | QString fontName = lua_tostring(L, 2); 941 | if (fontName == "VAR") { 942 | fontName = "Liberation Sans"; 943 | } else if (fontName == "VAR BOLD") { 944 | fontName = "Liberation Sans Bold"; 945 | } else { 946 | fontName = "Bitstream Vera Sans Mono"; 947 | } 948 | QString text(lua_tostring(L, 3)); 949 | 950 | text.remove(colourCodes); 951 | 952 | QStringList texts = text.split("\n"); 953 | QFont font(fontName); 954 | font.setPixelSize(fontsize + pobwindow->fontFudge); 955 | QFontMetrics fm(font); 956 | int curX = lua_tointeger(L, 4); 957 | int curY = lua_tointeger(L, 5); 958 | int yidx = std::max(0, std::min(texts.size() - 1, curY / fm.lineSpacing())); 959 | text = texts[yidx]; 960 | int i = 0; 961 | for (;i <= text.size();i++) { 962 | if (fm.size(0, text.left(i)).width() > curX) { 963 | break; 964 | } 965 | } 966 | for (int y = 0;y < yidx;y++) { 967 | i += texts[y].size() + 1; 968 | } 969 | lua_pushinteger(L, i); 970 | return 1; 971 | } 972 | 973 | static int l_StripEscapes(lua_State* L) 974 | { 975 | int n = lua_gettop(L); 976 | pobwindow->LAssert(L, n >= 1, "Usage: StripEscapes(string)"); 977 | pobwindow->LAssert(L, lua_isstring(L, 1), "StripEscapes() argument 1: expected string, got %t", 1); 978 | const char* str = lua_tostring(L, 1); 979 | char* strip = new char[strlen(str) + 1]; 980 | char* p = strip; 981 | while (*str) { 982 | int esclen = IsColorEscape(str); 983 | if (esclen) { 984 | str+= esclen; 985 | } else { 986 | *(p++) = *(str++); 987 | } 988 | } 989 | *p = 0; 990 | lua_pushstring(L, strip); 991 | delete[] strip; 992 | return 1; 993 | } 994 | 995 | static int l_GetAsyncCount(lua_State* L) 996 | { 997 | // lua_pushinteger(L, pobwindow->GetTexAsyncCount()); 998 | lua_pushinteger(L, 0); 999 | return 1; 1000 | } 1001 | 1002 | // ============== 1003 | // Search Handles 1004 | // ============== 1005 | 1006 | struct searchHandle_s { 1007 | QFileInfoList *fil; 1008 | }; 1009 | 1010 | static int l_NewFileSearch(lua_State* L) 1011 | { 1012 | int n = lua_gettop(L); 1013 | pobwindow->LAssert(L, n >= 1, "Usage: NewFileSearch(spec[, findDirectories])"); 1014 | pobwindow->LAssert(L, lua_isstring(L, 1), "NewFileSearch() argument 1: expected string, got %t", 1); 1015 | QString search_string(lua_tostring(L, 1)); 1016 | QStringList split = search_string.split("/"); 1017 | QString wildcard = split.takeLast(); 1018 | QDir dir(split.join("/")); 1019 | QStringList filters; 1020 | filters << wildcard; 1021 | bool dirOnly = lua_toboolean(L, 2) != 0; 1022 | QFileInfoList fil = dir.entryInfoList(filters, QDir::NoDotAndDotDot | (dirOnly ? QDir::Dirs : QDir::Files)); 1023 | if (fil.isEmpty()) { 1024 | return 0; 1025 | } 1026 | 1027 | auto handle = (searchHandle_s*)lua_newuserdata(L, sizeof(searchHandle_s)); 1028 | handle->fil = new QFileInfoList(fil); 1029 | lua_pushvalue(L, lua_upvalueindex(1)); 1030 | lua_setmetatable(L, -2); 1031 | return 1; 1032 | } 1033 | 1034 | static QFileInfoList* GetSearchHandle(lua_State* L, const char* method, bool valid) 1035 | { 1036 | pobwindow->LAssert(L, pobwindow->IsUserData(L, 1, "uisearchhandlemeta"), "searchHandle:%s() must be used on a search handle", method); 1037 | auto searchHandle = (searchHandle_s*)lua_touserdata(L, 1); 1038 | lua_remove(L, 1); 1039 | if (valid) { 1040 | pobwindow->LAssert(L, !searchHandle->fil->isEmpty(), "searchHandle:%s(): search handle is no longer valid (ran out of files to find)", method); 1041 | } 1042 | return searchHandle->fil; 1043 | } 1044 | 1045 | static int l_searchHandleGC(lua_State* L) 1046 | { 1047 | QFileInfoList* searchHandle = GetSearchHandle(L, "__gc", false); 1048 | delete searchHandle; 1049 | return 0; 1050 | } 1051 | 1052 | static int l_searchHandleNextFile(lua_State* L) 1053 | { 1054 | QFileInfoList* searchHandle = GetSearchHandle(L, "NextFile", true); 1055 | searchHandle->removeFirst(); 1056 | if ( searchHandle->isEmpty() ) { 1057 | return 0; 1058 | } 1059 | lua_pushboolean(L, 1); 1060 | return 1; 1061 | } 1062 | 1063 | static int l_searchHandleGetFileName(lua_State* L) 1064 | { 1065 | QFileInfoList* searchHandle = GetSearchHandle(L, "GetFileName", true); 1066 | lua_pushstring(L, searchHandle->first().fileName().toStdString().c_str()); 1067 | return 1; 1068 | } 1069 | 1070 | static int l_searchHandleGetFileSize(lua_State* L) 1071 | { 1072 | QFileInfoList* searchHandle = GetSearchHandle(L, "GetFileSize", true); 1073 | lua_pushinteger(L, searchHandle->first().size()); 1074 | return 1; 1075 | } 1076 | 1077 | static int l_searchHandleGetFileModifiedTime(lua_State* L) 1078 | { 1079 | QFileInfoList* searchHandle = GetSearchHandle(L, "GetFileModifiedTime", true); 1080 | QDateTime modified = searchHandle->first().lastModified(); 1081 | lua_pushnumber(L, modified.toMSecsSinceEpoch()); 1082 | lua_pushstring(L, modified.date().toString().toStdString().c_str()); 1083 | lua_pushstring(L, modified.time().toString().toStdString().c_str()); 1084 | return 3; 1085 | } 1086 | 1087 | // ================= 1088 | // General Functions 1089 | // ================= 1090 | 1091 | static int l_SetWindowTitle(lua_State* L) 1092 | { 1093 | int n = lua_gettop(L); 1094 | pobwindow->LAssert(L, n >= 1, "Usage: SetWindowTitle(title)"); 1095 | pobwindow->LAssert(L, lua_isstring(L, 1), "SetWindowTitle() argument 1: expected string, got %t", 1); 1096 | pobwindow->setTitle(lua_tostring(L, 1)); 1097 | return 0; 1098 | } 1099 | 1100 | static int l_GetCursorPos(lua_State* L) 1101 | { 1102 | QPoint pos = QCursor::pos(); 1103 | pos = pobwindow->mapFromGlobal(pos); 1104 | lua_pushinteger(L, pos.x()); 1105 | lua_pushinteger(L, pos.y()); 1106 | return 2; 1107 | } 1108 | 1109 | static int l_SetCursorPos(lua_State* L) 1110 | { 1111 | int n = lua_gettop(L); 1112 | pobwindow->LAssert(L, n >= 2, "Usage: SetCursorPos(x, y)"); 1113 | pobwindow->LAssert(L, lua_isnumber(L, 1), "SetCursorPos() argument 1: expected number, got %t", 1); 1114 | pobwindow->LAssert(L, lua_isnumber(L, 2), "SetCursorPos() argument 2: expected number, got %t", 2); 1115 | //pobwindow->sys->video->SetRelativeCursor((int)lua_tointeger(L, 1), (int)lua_tointeger(L, 2)); 1116 | return 0; 1117 | } 1118 | 1119 | static int l_ShowCursor(lua_State* L) 1120 | { 1121 | int n = lua_gettop(L); 1122 | pobwindow->LAssert(L, n >= 1, "Usage: ShowCursor(doShow)"); 1123 | //pobwindow->sys->ShowCursor(lua_toboolean(L, 1)); 1124 | return 0; 1125 | } 1126 | 1127 | static int l_IsKeyDown(lua_State* L) 1128 | { 1129 | int n = lua_gettop(L); 1130 | pobwindow->LAssert(L, n >= 1, "Usage: IsKeyDown(keyName)"); 1131 | pobwindow->LAssert(L, lua_isstring(L, 1), "IsKeyDown() argument 1: expected string, got %t", 1); 1132 | size_t len; 1133 | const char* kname = lua_tolstring(L, 1, &len); 1134 | pobwindow->LAssert(L, len >= 1, "IsKeyDown() argument 1: string is empty", 1); 1135 | QString k(kname); 1136 | bool result = false; 1137 | if (k == "LEFTBUTTON") { 1138 | if (QGuiApplication::mouseButtons() & Qt::LeftButton) { 1139 | result = true; 1140 | } 1141 | } else { 1142 | int keys = QGuiApplication::keyboardModifiers(); 1143 | if (k == "CTRL") { 1144 | result = keys & Qt::ControlModifier; 1145 | } else if (k == "SHIFT") { 1146 | result = keys & Qt::ShiftModifier; 1147 | } else if (k == "ALT") { 1148 | result = keys & Qt::AltModifier; 1149 | } else { 1150 | std::cout << "UNKNOWN ISKEYDOWN: " << k.toStdString() << std::endl; 1151 | } 1152 | } 1153 | lua_pushboolean(L, result); 1154 | //int key = pobwindow->KeyForName(kname); 1155 | //pobwindow->LAssert(L, key, "IsKeyDown(): unrecognised key name"); 1156 | //lua_pushboolean(L, pobwindow->sys->IsKeyDown(key)); 1157 | return 1; 1158 | } 1159 | 1160 | static int l_Copy(lua_State* L) 1161 | { 1162 | int n = lua_gettop(L); 1163 | pobwindow->LAssert(L, n >= 1, "Usage: Copy(string)"); 1164 | pobwindow->LAssert(L, lua_isstring(L, 1), "Copy() argument 1: expected string, got %t", 1); 1165 | QGuiApplication::clipboard()->setText(lua_tostring(L, 1)); 1166 | return 0; 1167 | } 1168 | 1169 | static int l_Paste(lua_State* L) 1170 | { 1171 | QString data = QGuiApplication::clipboard()->text(); 1172 | if (data.size()) { 1173 | lua_pushstring(L, data.toStdString().c_str()); 1174 | return 1; 1175 | } else { 1176 | return 0; 1177 | } 1178 | } 1179 | 1180 | static int l_Deflate(lua_State* L) 1181 | { 1182 | int n = lua_gettop(L); 1183 | pobwindow->LAssert(L, n >= 1, "Usage: Deflate(string)"); 1184 | pobwindow->LAssert(L, lua_isstring(L, 1), "Deflate() argument 1: expected string, got %t", 1); 1185 | z_stream_s z; 1186 | z.zalloc = NULL; 1187 | z.zfree = NULL; 1188 | deflateInit(&z, 9); 1189 | size_t inLen; 1190 | Byte* in = (Byte*)lua_tolstring(L, 1, &inLen); 1191 | int outSz = deflateBound(&z, inLen); 1192 | Byte* out = new Byte[outSz]; 1193 | z.next_in = in; 1194 | z.avail_in = inLen; 1195 | z.next_out = out; 1196 | z.avail_out = outSz; 1197 | int err = deflate(&z, Z_FINISH); 1198 | deflateEnd(&z); 1199 | if (err == Z_STREAM_END) { 1200 | lua_pushlstring(L, (const char*)out, z.total_out); 1201 | delete[] out; 1202 | return 1; 1203 | } else { 1204 | lua_pushnil(L); 1205 | lua_pushstring(L, zError(err)); 1206 | delete[] out; 1207 | return 2; 1208 | } 1209 | } 1210 | 1211 | static int l_Inflate(lua_State* L) 1212 | { 1213 | int n = lua_gettop(L); 1214 | pobwindow->LAssert(L, n >= 1, "Usage: Inflate(string)"); 1215 | pobwindow->LAssert(L, lua_isstring(L, 1), "Inflate() argument 1: expected string, got %t", 1); 1216 | size_t inLen; 1217 | Byte* in = (Byte*)lua_tolstring(L, 1, &inLen); 1218 | int outSz = inLen * 4; 1219 | Byte* out = new Byte[outSz]; 1220 | z_stream_s z; 1221 | z.next_in = in; 1222 | z.avail_in = inLen; 1223 | z.zalloc = NULL; 1224 | z.zfree = NULL; 1225 | z.next_out = out; 1226 | z.avail_out = outSz; 1227 | inflateInit(&z); 1228 | int err; 1229 | while ((err = inflate(&z, Z_NO_FLUSH)) == Z_OK) { 1230 | if (z.avail_out == 0) { 1231 | // Output buffer filled, embiggen it 1232 | int newSz = outSz << 1; 1233 | Byte *newOut = (Byte *)realloc(out, newSz); 1234 | if (newOut) { 1235 | out = newOut; 1236 | } else { 1237 | // PANIC 1238 | delete[] out; 1239 | return 0; 1240 | } 1241 | z.next_out = out + outSz; 1242 | z.avail_out = outSz; 1243 | outSz = newSz; 1244 | } 1245 | } 1246 | inflateEnd(&z); 1247 | if (err == Z_STREAM_END) { 1248 | lua_pushlstring(L, (const char*)out, z.total_out); 1249 | delete[] out; 1250 | return 1; 1251 | } else { 1252 | lua_pushnil(L); 1253 | lua_pushstring(L, zError(err)); 1254 | delete[] out; 1255 | return 2; 1256 | } 1257 | } 1258 | 1259 | static int l_GetTime(lua_State* L) 1260 | { 1261 | qint64 ms = QDateTime::currentDateTime().toMSecsSinceEpoch() - pobwindow->baseTime; 1262 | lua_pushinteger(L, ms); 1263 | return 1; 1264 | } 1265 | 1266 | static int l_GetScriptPath(lua_State* L) 1267 | { 1268 | lua_pushstring(L, pobwindow->scriptPath.toStdString().c_str()); 1269 | return 1; 1270 | } 1271 | 1272 | static int l_GetRuntimePath(lua_State* L) 1273 | { 1274 | lua_pushstring(L, pobwindow->basePath.toStdString().c_str()); 1275 | return 1; 1276 | } 1277 | 1278 | static int l_GetUserPath(lua_State* L) 1279 | { 1280 | lua_pushstring(L, pobwindow->userPath.toStdString().c_str()); 1281 | return 1; 1282 | } 1283 | 1284 | static int l_MakeDir(lua_State* L) 1285 | { 1286 | int n = lua_gettop(L); 1287 | pobwindow->LAssert(L, n >= 1, "Usage: MakeDir(path)"); 1288 | pobwindow->LAssert(L, lua_isstring(L, 1), "MakeDir() argument 1: expected string, got %t", 1); 1289 | lua_pushboolean(L, QDir().mkpath(lua_tostring(L, 1))); 1290 | return 1; 1291 | } 1292 | 1293 | static int l_RemoveDir(lua_State* L) 1294 | { 1295 | int n = lua_gettop(L); 1296 | pobwindow->LAssert(L, n >= 1, "Usage: l_RemoveDir(path)"); 1297 | pobwindow->LAssert(L, lua_isstring(L, 1), "l_RemoveDir() argument 1: expected string, got %t", 1); 1298 | QDir d; 1299 | if (!d.rmdir(lua_tostring(L, 1))) { 1300 | lua_pushnil(L); 1301 | return 1; 1302 | } else { 1303 | lua_pushboolean(L, true); 1304 | return 1; 1305 | } 1306 | } 1307 | 1308 | static int l_SetWorkDir(lua_State* L) 1309 | { 1310 | int n = lua_gettop(L); 1311 | pobwindow->LAssert(L, n >= 1, "Usage: SetWorkDir(path)"); 1312 | pobwindow->LAssert(L, lua_isstring(L, 1), "SetWorkDir() argument 1: expected string, got %t", 1); 1313 | if (QDir::setCurrent(lua_tostring(L, 1))) { 1314 | pobwindow->scriptWorkDir = lua_tostring(L, 1); 1315 | } 1316 | return 0; 1317 | } 1318 | 1319 | static int l_GetWorkDir(lua_State* L) 1320 | { 1321 | lua_pushstring(L, QDir::currentPath().toStdString().c_str()); 1322 | return 1; 1323 | } 1324 | 1325 | static int l_LaunchSubScript(lua_State* L) 1326 | { 1327 | int n = lua_gettop(L); 1328 | pobwindow->LAssert(L, n >= 3, "Usage: LaunchSubScript(scriptText, funcList, subList[, ...])"); 1329 | for (int i = 1; i <= 3; i++) { 1330 | pobwindow->LAssert(L, lua_isstring(L, i), "LaunchSubScript() argument %d: expected string, got %t", i, i); 1331 | } 1332 | for (int i = 4; i <= n; i++) { 1333 | pobwindow->LAssert(L, lua_isnil(L, i) || lua_isboolean(L, i) || lua_isnumber(L, i) || lua_isstring(L, i), 1334 | "LaunchSubScript() argument %d: only nil, boolean, number and string types can be passed to sub script", i); 1335 | } 1336 | int slot = pobwindow->subScriptList.size(); 1337 | pobwindow->subScriptList.append(std::make_shared(L)); 1338 | // Signal us when the subscript completes so we can trigger a repaint. 1339 | pobwindow->connect( pobwindow->subScriptList[slot].get(), &SubScript::finished, pobwindow, &POBWindow::subScriptFinished ); 1340 | pobwindow->subScriptList[slot]->start(); 1341 | lua_pushinteger(L, slot); 1342 | return 1; 1343 | } 1344 | 1345 | static int l_AbortSubScript(lua_State* L) 1346 | { 1347 | std::cout << "SUBSCRIPT ABORT STUB" << std::endl; 1348 | return 0; 1349 | /* 1350 | int n = lua_gettop(L); 1351 | pobwindow->LAssert(L, n >= 1, "Usage: AbortSubScript(ssID)"); 1352 | pobwindow->LAssert(L, lua_islightuserdata(L, 1), "AbortSubScript() argument 1: expected subscript ID, got %t", 1); 1353 | notdword slot = (notdword)lua_touserdata(L, 1); 1354 | pobwindow->LAssert(L, slot < pobwindow->subScriptSize && pobwindow->subScriptList[slot], "AbortSubScript() argument 1: invalid subscript ID"); 1355 | pobwindow->LAssert(L, pobwindow->subScriptList[slot]->IsRunning(), "AbortSubScript(): subscript isn't running"); 1356 | ui_ISubScript::FreeHandle(pobwindow->subScriptList[slot]); 1357 | pobwindow->subScriptList[slot] = NULL; 1358 | return 0; 1359 | */ 1360 | } 1361 | 1362 | static int l_IsSubScriptRunning(lua_State* L) 1363 | { 1364 | std::cout << "SUBSCRIPT RUNNING STUB" << std::endl; 1365 | return 0; 1366 | /* 1367 | int n = lua_gettop(L); 1368 | pobwindow->LAssert(L, n >= 1, "Usage: IsSubScriptRunning(ssID)"); 1369 | pobwindow->LAssert(L, lua_islightuserdata(L, 1), "IsSubScriptRunning() argument 1: expected subscript ID, got %t", 1); 1370 | notdword slot = (notdword)lua_touserdata(L, 1); 1371 | pobwindow->LAssert(L, slot < pobwindow->subScriptSize && pobwindow->subScriptList[slot], "IsSubScriptRunning() argument 1: invalid subscript ID"); 1372 | lua_pushboolean(L, pobwindow->subScriptList[slot]->IsRunning()); 1373 | return 1; 1374 | */ 1375 | } 1376 | 1377 | static int l_LoadModule(lua_State* L) 1378 | { 1379 | int n = lua_gettop(L); 1380 | pobwindow->LAssert(L, n >= 1, "Usage: LoadModule(name[, ...])"); 1381 | pobwindow->LAssert(L, lua_isstring(L, 1), "LoadModule() argument 1: expected string, got %t", 1); 1382 | QString fileName(lua_tostring(L, 1)); 1383 | if (!fileName.endsWith(".lua")) { 1384 | fileName = fileName + ".lua"; 1385 | } 1386 | QDir::setCurrent(pobwindow->scriptPath); 1387 | int err = luaL_loadfile(L, fileName.toStdString().c_str()); 1388 | QDir::setCurrent(pobwindow->scriptWorkDir); 1389 | pobwindow->LAssert(L, err == 0, "LoadModule() error loading '%s':\n%s", fileName.toStdString().c_str(), lua_tostring(L, -1)); 1390 | lua_replace(L, 1); // Replace module name with module main chunk 1391 | lua_call(L, n - 1, LUA_MULTRET); 1392 | return lua_gettop(L); 1393 | } 1394 | 1395 | static int l_PLoadModule(lua_State* L) 1396 | { 1397 | int n = lua_gettop(L); 1398 | pobwindow->LAssert(L, n >= 1, "Usage: PLoadModule(name[, ...])"); 1399 | pobwindow->LAssert(L, lua_isstring(L, 1), "PLoadModule() argument 1: expected string, got %t", 1); 1400 | QString fileName(lua_tostring(L, 1)); 1401 | if (!fileName.endsWith(".lua")) { 1402 | fileName = fileName + ".lua"; 1403 | } 1404 | QDir::setCurrent(pobwindow->scriptPath); 1405 | int err = luaL_loadfile(L, fileName.toStdString().c_str()); 1406 | QDir::setCurrent(pobwindow->scriptWorkDir); 1407 | if (err) { 1408 | return 1; 1409 | } 1410 | lua_replace(L, 1); // Replace module name with module main chunk 1411 | //lua_getfield(L, LUA_REGISTRYINDEX, "traceback"); 1412 | //lua_insert(L, 1); // Insert traceback function at start of stack 1413 | err = lua_pcall(L, n - 1, LUA_MULTRET, 0); 1414 | if (err) { 1415 | return 1; 1416 | } 1417 | lua_pushnil(L); 1418 | lua_insert(L, 1); 1419 | // lua_replace(L, 1); // Replace traceback function with nil 1420 | return lua_gettop(L); 1421 | } 1422 | 1423 | static int l_PCall(lua_State* L) 1424 | { 1425 | int n = lua_gettop(L); 1426 | pobwindow->LAssert(L, n >= 1, "Usage: PCall(func[, ...])"); 1427 | pobwindow->LAssert(L, lua_isfunction(L, 1), "PCall() argument 1: expected function, got %t", 1); 1428 | lua_getfield(L, LUA_REGISTRYINDEX, "traceback"); 1429 | lua_insert(L, 1); // Insert traceback function at start of stack 1430 | int err = lua_pcall(L, n - 1, LUA_MULTRET, 1); 1431 | if (err) { 1432 | lua_error(L); 1433 | return 1; 1434 | } 1435 | lua_pushnil(L); 1436 | lua_replace(L, 1); // Replace traceback function with nil 1437 | return lua_gettop(L); 1438 | } 1439 | 1440 | static int l_ConPrintf(lua_State* L) 1441 | { 1442 | int n = lua_gettop(L); 1443 | pobwindow->LAssert(L, n >= 1, "Usage: ConPrintf(fmt[, ...])"); 1444 | pobwindow->LAssert(L, lua_isstring(L, 1), "ConPrintf() argument 1: expected string, got %t", 1); 1445 | lua_pushvalue(L, lua_upvalueindex(1)); // string.format 1446 | lua_insert(L, 1); 1447 | lua_call(L, n, 1); 1448 | pobwindow->LAssert(L, lua_isstring(L, 1), "ConPrintf() error: string.format returned non-string"); 1449 | //std::cout << lua_tostring(L, 1) << std::endl; 1450 | //pobwindow->sys->con->Printf("%s\n", lua_tostring(L, 1)); 1451 | return 0; 1452 | } 1453 | 1454 | static void printTableItter(lua_State* L, int index, int level, bool recurse) 1455 | { 1456 | lua_checkstack(L, 5); 1457 | lua_pushnil(L); 1458 | while (lua_next(L, index)) { 1459 | for (int t = 0; t < level; t++) std::cout << " "; 1460 | // Print key 1461 | if (lua_type(L, -2) == LUA_TSTRING) { 1462 | std::cout << "[\"" << lua_tostring(L, -2) << "\"] = "; 1463 | } else { 1464 | lua_pushvalue(L, 2); // Push tostring function 1465 | lua_pushvalue(L, -3); // Push key 1466 | lua_call(L, 1, 1); // Call tostring 1467 | std::cout << lua_tostring(L, -1) << " = "; 1468 | lua_pop(L, 1); // Pop result of tostring 1469 | } 1470 | // Print value 1471 | if (lua_type(L, -1) == LUA_TTABLE) { 1472 | bool expand = recurse; 1473 | if (expand) { 1474 | lua_pushvalue(L, -1); // Push value 1475 | lua_gettable(L, 3); // Index printed tables list 1476 | expand = lua_toboolean(L, -1) == 0; 1477 | lua_pop(L, 1); // Pop result of indexing 1478 | } 1479 | if (expand) { 1480 | lua_pushvalue(L, -1); // Push value 1481 | lua_pushboolean(L, 1); 1482 | lua_settable(L, 3); // Add to printed tables list 1483 | std::cout << "table: " << lua_topointer(L, -1) << " {" 1484 | << std::endl; 1485 | printTableItter(L, lua_gettop(L), level + 1, true); 1486 | for (int t = 0; t < level; t++) std::cout << " "; 1487 | std::cout << "}" << std::endl; 1488 | } else { 1489 | std::cout << "table: " << lua_topointer(L, -1) << " { ... }\n"; 1490 | } 1491 | } else if (lua_type(L, -1) == LUA_TSTRING) { 1492 | std::cout << "\"" << lua_tostring(L, -1) << "\"" << std::endl; 1493 | } else { 1494 | lua_pushvalue(L, 2); // Push tostring function 1495 | lua_pushvalue(L, -2); // Push value 1496 | lua_call(L, 1, 1); // Call tostring 1497 | std::cout << lua_tostring(L, -1) << std::endl; 1498 | lua_pop(L, 1); // Pop result of tostring 1499 | } 1500 | lua_pop(L, 1); // Pop value 1501 | } 1502 | } 1503 | 1504 | static int l_ConPrintTable(lua_State* L) 1505 | { 1506 | return 0; 1507 | int n = lua_gettop(L); 1508 | pobwindow->LAssert(L, n >= 1, "Usage: ConPrintTable(tbl[, noRecurse])"); 1509 | pobwindow->LAssert(L, lua_istable(L, 1), "ConPrintTable() argument 1: expected table, got %t", 1); 1510 | bool recurse = lua_toboolean(L, 2) == 0; 1511 | lua_settop(L, 1); 1512 | lua_getglobal(L, "tostring"); 1513 | lua_newtable(L); // Printed tables list 1514 | lua_pushvalue(L, 1); // Push root table 1515 | lua_pushboolean(L, 1); 1516 | lua_settable(L, 3); // Add root table to printed tables list 1517 | printTableItter(L, 1, 0, recurse); 1518 | return 0; 1519 | } 1520 | 1521 | static int l_ConExecute(lua_State* L) 1522 | { 1523 | int n = lua_gettop(L); 1524 | pobwindow->LAssert(L, n >= 1, "Usage: ConExecute(cmd)"); 1525 | pobwindow->LAssert(L, lua_isstring(L, 1), "ConExecute() argument 1: expected string, got %t", 1); 1526 | //pobwindow->sys->con->Execute(lua_tostring(L,1)); // FIXME 1527 | return 0; 1528 | } 1529 | 1530 | static int l_ConClear(lua_State* L) 1531 | { 1532 | // pobwindow->sys->con->Clear(); 1533 | return 0; 1534 | } 1535 | 1536 | static int l_print(lua_State* L) 1537 | { 1538 | int n = lua_gettop(L); 1539 | lua_getglobal(L, "tostring"); 1540 | for (int i = 1; i <= n; i++) { 1541 | lua_pushvalue(L, -1); // Push tostring function 1542 | lua_pushvalue(L, i); 1543 | lua_call(L, 1, 1); // Call tostring 1544 | const char* s = lua_tostring(L, -1); 1545 | pobwindow->LAssert(L, s != NULL, "print() error: tostring returned non-string"); 1546 | if (i > 1) std::cout << " "; 1547 | std::cout << s; 1548 | lua_pop(L, 1); // Pop result of tostring 1549 | } 1550 | std::cout << std::endl; 1551 | return 0; 1552 | } 1553 | 1554 | static int l_SpawnProcess(lua_State* L) 1555 | { 1556 | int n = lua_gettop(L); 1557 | pobwindow->LAssert(L, n >= 1, "Usage: SpawnProcess(cmdName[, args])"); 1558 | pobwindow->LAssert(L, lua_isstring(L, 1), "SpawnProcess() argument 1: expected string, got %t", 1); 1559 | // FIXME 1560 | // pobwindow->sys->SpawnProcess(lua_tostring(L, 1), lua_tostring(L, 2)); 1561 | return 0; 1562 | } 1563 | 1564 | static int l_OpenURL(lua_State* L) 1565 | { 1566 | int n = lua_gettop(L); 1567 | pobwindow->LAssert(L, n >= 1, "Usage: OpenURL(url)"); 1568 | pobwindow->LAssert(L, lua_isstring(L, 1), "OpenURL() argument 1: expected string, got %t", 1); 1569 | // FIXME 1570 | //pobwindow->sys->OpenURL(lua_tostring(L, 1)); 1571 | return 0; 1572 | } 1573 | 1574 | static int l_SetProfiling(lua_State* L) 1575 | { 1576 | int n = lua_gettop(L); 1577 | pobwindow->LAssert(L, n >= 1, "Usage: SetProfiling(isEnabled)"); 1578 | // FIXME 1579 | //pobwindow->debug->SetProfiling(lua_toboolean(L, 1) == 1); 1580 | return 0; 1581 | } 1582 | 1583 | static int l_Restart(lua_State* L) 1584 | { 1585 | // FIXME 1586 | //pobwindow->restartFlag = true; 1587 | return 0; 1588 | } 1589 | 1590 | static int l_Exit(lua_State* L) 1591 | { 1592 | int n = lua_gettop(L); 1593 | const char* msg = nullptr; 1594 | if (n >= 1 && !lua_isnil(L, 1)) { 1595 | pobwindow->LAssert(L, lua_isstring(L, 1), "Exit() argument 1: expected string or nil, got %t", 1); 1596 | msg = lua_tostring(L, 1); 1597 | } 1598 | // FIXME 1599 | //pobwindow->sys->Exit(msg); 1600 | //pobwindow->didExit = true; 1601 | // lua_pushstring(L, "dummy"); 1602 | // lua_error(L); 1603 | return 0; 1604 | } 1605 | 1606 | #define ADDFUNC(n) lua_pushcclosure(L, l_##n, 0);lua_setglobal(L, #n); 1607 | #define ADDFUNCCL(n, u) lua_pushcclosure(L, l_##n, u);lua_setglobal(L, #n); 1608 | 1609 | int main(int argc, char **argv) 1610 | { 1611 | #ifdef __APPLE__ 1612 | #define GL_SILENCE_DEPRECATION 1613 | QGuiApplication::setAttribute(Qt::AA_DisableHighDpiScaling); 1614 | qputenv("QT_AUTO_SCREEN_SCALE_FACTOR", "0"); 1615 | #endif //__APPLE__ 1616 | 1617 | QGuiApplication app{argc, argv}; 1618 | 1619 | QStringList args = app.arguments(); 1620 | 1621 | pobwindow = new POBWindow; 1622 | 1623 | if (args.size() > 1) { 1624 | bool ok; 1625 | int ff = args[1].toInt(&ok); 1626 | if (ok) { 1627 | pobwindow->fontFudge = ff; 1628 | args.removeAt(1); // Remove our hacky font factor from the arglist passed on to the script 1629 | } 1630 | } 1631 | 1632 | L = luaL_newstate(); 1633 | luaL_openlibs(L); 1634 | luaJIT_setmode(L, 0, LUAJIT_MODE_ENGINE|LUAJIT_MODE_OFF); 1635 | 1636 | // Callbacks 1637 | lua_newtable(L); // Callbacks table 1638 | lua_pushvalue(L, -1); // Push callbacks table 1639 | ADDFUNCCL(SetCallback, 1); 1640 | lua_pushvalue(L, -1); // Push callbacks table 1641 | ADDFUNCCL(GetCallback, 1); 1642 | lua_pushvalue(L, -1); // Push callbacks table 1643 | ADDFUNCCL(SetMainObject, 1); 1644 | lua_setfield(L, LUA_REGISTRYINDEX, "uicallbacks"); 1645 | 1646 | // Image handles 1647 | lua_newtable(L); // Image handle metatable 1648 | lua_pushvalue(L, -1); // Push image handle metatable 1649 | ADDFUNCCL(NewImageHandle, 1); 1650 | lua_pushvalue(L, -1); // Push image handle metatable 1651 | lua_setfield(L, -2, "__index"); 1652 | lua_pushcfunction(L, l_imgHandleGC); 1653 | lua_setfield(L, -2, "__gc"); 1654 | lua_pushcfunction(L, l_imgHandleLoad); 1655 | lua_setfield(L, -2, "Load"); 1656 | lua_pushcfunction(L, l_imgHandleUnload); 1657 | lua_setfield(L, -2, "Unload"); 1658 | lua_pushcfunction(L, l_imgHandleIsValid); 1659 | lua_setfield(L, -2, "IsValid"); 1660 | lua_pushcfunction(L, l_imgHandleIsLoading); 1661 | lua_setfield(L, -2, "IsLoading"); 1662 | lua_pushcfunction(L, l_imgHandleSetLoadingPriority); 1663 | lua_setfield(L, -2, "SetLoadingPriority"); 1664 | lua_pushcfunction(L, l_imgHandleImageSize); 1665 | lua_setfield(L, -2, "ImageSize"); 1666 | lua_setfield(L, LUA_REGISTRYINDEX, "uiimghandlemeta"); 1667 | 1668 | // Rendering 1669 | ADDFUNC(RenderInit); 1670 | ADDFUNC(GetScreenSize); 1671 | ADDFUNC(SetClearColor); 1672 | ADDFUNC(SetDrawLayer); 1673 | ADDFUNC(SetViewport); 1674 | ADDFUNC(SetDrawColor); 1675 | ADDFUNC(DrawImage); 1676 | ADDFUNC(DrawImageQuad); 1677 | ADDFUNC(DrawString); 1678 | ADDFUNC(DrawStringWidth); 1679 | ADDFUNC(DrawStringCursorIndex); 1680 | ADDFUNC(StripEscapes); 1681 | ADDFUNC(GetAsyncCount); 1682 | 1683 | // Search handles 1684 | lua_newtable(L); // Search handle metatable 1685 | lua_pushvalue(L, -1); // Push search handle metatable 1686 | ADDFUNCCL(NewFileSearch, 1); 1687 | lua_pushvalue(L, -1); // Push search handle metatable 1688 | lua_setfield(L, -2, "__index"); 1689 | lua_pushcfunction(L, l_searchHandleGC); 1690 | lua_setfield(L, -2, "__gc"); 1691 | lua_pushcfunction(L, l_searchHandleNextFile); 1692 | lua_setfield(L, -2, "NextFile"); 1693 | lua_pushcfunction(L, l_searchHandleGetFileName); 1694 | lua_setfield(L, -2, "GetFileName"); 1695 | lua_pushcfunction(L, l_searchHandleGetFileSize); 1696 | lua_setfield(L, -2, "GetFileSize"); 1697 | lua_pushcfunction(L, l_searchHandleGetFileModifiedTime); 1698 | lua_setfield(L, -2, "GetFileModifiedTime"); 1699 | lua_setfield(L, LUA_REGISTRYINDEX, "uisearchhandlemeta"); 1700 | 1701 | // General function 1702 | ADDFUNC(SetWindowTitle); 1703 | ADDFUNC(GetCursorPos); 1704 | ADDFUNC(SetCursorPos); 1705 | ADDFUNC(ShowCursor); 1706 | ADDFUNC(IsKeyDown); 1707 | ADDFUNC(Copy); 1708 | ADDFUNC(Paste); 1709 | ADDFUNC(Deflate); 1710 | ADDFUNC(Inflate); 1711 | ADDFUNC(GetTime); 1712 | ADDFUNC(GetScriptPath); 1713 | ADDFUNC(GetRuntimePath); 1714 | ADDFUNC(GetUserPath); 1715 | ADDFUNC(MakeDir); 1716 | ADDFUNC(RemoveDir); 1717 | ADDFUNC(SetWorkDir); 1718 | ADDFUNC(GetWorkDir); 1719 | ADDFUNC(LaunchSubScript); 1720 | ADDFUNC(AbortSubScript); 1721 | ADDFUNC(IsSubScriptRunning); 1722 | ADDFUNC(LoadModule); 1723 | ADDFUNC(PLoadModule); 1724 | ADDFUNC(PCall); 1725 | lua_getglobal(L, "string"); 1726 | lua_getfield(L, -1, "format"); 1727 | ADDFUNCCL(ConPrintf, 1); 1728 | lua_pop(L, 1); // Pop 'string' table 1729 | ADDFUNC(ConPrintTable); 1730 | ADDFUNC(ConExecute); 1731 | ADDFUNC(ConClear); 1732 | ADDFUNC(print); 1733 | ADDFUNC(SpawnProcess); 1734 | ADDFUNC(OpenURL); 1735 | ADDFUNC(SetProfiling); 1736 | ADDFUNC(Restart); 1737 | ADDFUNC(Exit); 1738 | lua_getglobal(L, "os"); 1739 | lua_pushcfunction(L, l_Exit); 1740 | lua_setfield(L, -2, "exit"); 1741 | lua_pop(L, 1); // Pop 'os' table 1742 | lua_createtable(L, args.size() - 1, 1); 1743 | for (int i = 0; i < args.size(); i++) { 1744 | lua_pushstring(L, args[i].toStdString().c_str()); 1745 | lua_rawseti(L, -2, i); 1746 | } 1747 | lua_setglobal(L, "arg"); 1748 | 1749 | // Override the package.path so dkjson.lua and other libraries are 1750 | // can be added via `require` 1751 | std::string basePath = pobwindow->basePath.toStdString(); 1752 | std::string extraPathCommand = "package.path = package.path .. \";" 1753 | + basePath 1754 | + "/runtime/lua/?.lua\""; 1755 | luaL_dostring(L, extraPathCommand.c_str()); 1756 | 1757 | int result = luaL_dofile(L, "Launch.lua"); 1758 | if (result != 0) { 1759 | lua_error(L); 1760 | } 1761 | 1762 | pushCallback("OnInit"); 1763 | result = lua_pcall(L, 1, 0, 0); 1764 | if (result != 0) { 1765 | lua_error(L); 1766 | } 1767 | pobwindow->resize(800, 600); 1768 | pobwindow->show(); 1769 | 1770 | // Add the bundled fonts 1771 | QFontDatabase::addApplicationFont(QDir::currentPath() + "/VeraMono.ttf"); 1772 | QFontDatabase::addApplicationFont(QDir::currentPath() + "/LiberationSans-Regular.ttf"); 1773 | QFontDatabase::addApplicationFont(QDir::currentPath() + "/LiberationSans-Bold.ttf"); 1774 | return app.exec(); 1775 | } 1776 | 1777 | -------------------------------------------------------------------------------- /main.h: -------------------------------------------------------------------------------- 1 | #ifndef MAIN_H 2 | #define MAIN_H 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | // Font alignment 11 | enum r_fontAlign_e { 12 | F_LEFT, 13 | F_CENTRE, 14 | F_RIGHT, 15 | F_CENTRE_X, 16 | F_RIGHT_X 17 | }; 18 | 19 | // Fonts 20 | enum r_fonts_e { 21 | F_FIXED, // Monospaced: Bitsteam Vera Sans Mono 22 | F_VAR, // Normal: Liberation Sans 23 | F_VAR_BOLD, // Normal: Liberation Sans Bold 24 | F_NUMFONTS 25 | }; 26 | 27 | // Texture flags 28 | enum r_texFlag_e { 29 | TF_CLAMP = 0x01, // Clamp texture 30 | TF_NOMIPMAP = 0x02, // No mipmaps 31 | TF_NEAREST = 0x04, // Use nearest-pixel magnification instead of linear 32 | TF_ASYNC = 0x08 // Asynchronous loading 33 | }; 34 | 35 | class Cmd { 36 | public: 37 | virtual ~Cmd() = default; 38 | virtual void execute() = 0; 39 | }; 40 | 41 | class ViewportCmd : public Cmd { 42 | public: 43 | ViewportCmd(int X, int Y, int W, int H) : x(X), y(Y), w(W), h(H) { 44 | } 45 | 46 | void execute(); 47 | private: 48 | int x, y, w, h; 49 | }; 50 | 51 | class ColorCmd : public Cmd { 52 | public: 53 | ColorCmd(float Col[4]) : col {Col[0], Col[1], Col[2], Col[3]} {} 54 | void execute() { 55 | glColor4fv(col); 56 | } 57 | private: 58 | float col[4]; 59 | }; 60 | 61 | class DrawImageQuadCmd : public Cmd { 62 | public: 63 | DrawImageQuadCmd() {} 64 | DrawImageQuadCmd(std::shared_ptr Tex, float X0, float Y0, float X1, float Y1, float X2, float Y2, float X3, float Y3, float S0 = 0, float T0 = 0, float S1 = 1, float T1 = 0, float S2 = 1, float T2 = 1, float S3 = 0, float T3 = 1) : x {X0, X1, X2, X3}, y {Y0, Y1, Y2, Y3}, s {S0, S1, S2, S3}, t {T0, T1, T2, T3}, tex(Tex) { 65 | } 66 | 67 | void execute(); 68 | protected: 69 | std::shared_ptr tex; 70 | float x[4]; 71 | float y[4]; 72 | float s[4]; 73 | float t[4]; 74 | }; 75 | 76 | class DrawImageCmd : public DrawImageQuadCmd { 77 | public: 78 | DrawImageCmd(std::shared_ptr tex, float x, float y, float w, float h, float s1 = 0, float t1 = 0, float s2 = 1.0f, float t2 = 1.0f) : DrawImageQuadCmd(tex, x, y, x + w, y, x + w, y + h, x, y + h, s1, t1, s2, t1, s2, t2, s1, t2) { 79 | } 80 | }; 81 | 82 | class DrawStringCmd : public DrawImageQuadCmd { 83 | public: 84 | DrawStringCmd(float X, float Y, int Align, int Size, int Font, const char *Text); 85 | ~DrawStringCmd() { 86 | } 87 | 88 | void execute() { 89 | float curCol[4]; 90 | if (col[3] > 0) { 91 | glGetFloatv(GL_CURRENT_COLOR, curCol); 92 | glColor4fv(col); 93 | } 94 | DrawImageQuadCmd::execute(); 95 | if (col[3] > 0) { 96 | glColor4fv(curCol); 97 | } 98 | } 99 | 100 | void setCol(float c0, float c1, float c2) { 101 | col[0] = c0; 102 | col[1] = c1; 103 | col[2] = c2; 104 | col[3] = 1.0f; 105 | } 106 | private: 107 | float col[4]; 108 | QString text; 109 | }; 110 | #endif 111 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project('POB Frontend', 'cpp', default_options : ['cpp_std=c++17']) 2 | 3 | qt5_dep = dependency('qt5', modules : ['Gui','Core','Widgets']) 4 | lua_dep = dependency('luajit') 5 | # NB on OSX you also need to invoke meson like so, because luajit: 6 | # LDFLAGS="-pagezero_size 10000 -image_base 100000000" meson pobfrontend build 7 | if build_machine.system() == 'darwin' 8 | gl_dep = dependency('appleframeworks', modules: ['OpenGL']) 9 | else 10 | gl_dep = dependency('gl') 11 | endif 12 | zlib_dep = dependency('zlib') 13 | curl_dep = dependency('libcurl') 14 | 15 | # Added flag based on https://stackoverflow.com/a/37729971/319066 16 | # and arguments based on https://mesonbuild.com/Adding-arguments.html 17 | compiler_arguments = ['-mmacosx-version-min=10.12', '-isysroot', '/Library/Developer/CommandLineTools/SDKs/MacOSX12.sdk'] 18 | add_project_arguments(compiler_arguments , language : 'c') 19 | add_project_arguments(compiler_arguments, language : 'cpp') 20 | 21 | linker_arguments = ['-mmacosx-version-min=10.12', '-isysroot', '/Library/Developer/CommandLineTools/SDKs/MacOSX12.sdk'] 22 | add_project_link_arguments(linker_arguments, language : 'c') 23 | add_project_link_arguments(linker_arguments, language : 'cpp') 24 | 25 | # Import the extension module that knows how 26 | # to invoke Qt tools. 27 | qt5 = import('qt5') 28 | prep = qt5.preprocess(moc_headers : ['subscript.hpp', 'pobwindow.hpp']) 29 | 30 | executable('PathOfBuilding', 31 | sources : ['main.cpp', prep], 32 | dependencies : [qt5_dep, gl_dep, zlib_dep, lua_dep, curl_dep], 33 | install : true) 34 | 35 | 36 | if build_machine.system() == 'darwin' 37 | install_data('pobLogo.icns', install_dir : 'Contents/Resources') 38 | install_data('Info.plist', install_dir : 'Contents') 39 | install_data('pobWrapper.sh', install_dir : 'Contents/MacOS') 40 | meson.add_install_script('mesonInstaller.sh') 41 | endif 42 | -------------------------------------------------------------------------------- /mesonInstaller.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cp -rf "${MESON_SOURCE_ROOT}"/PathOfBuildingBuild/* "${MESON_INSTALL_PREFIX}"/Contents/MacOS/ 4 | cp -f "${MESON_SOURCE_ROOT}"/fonts/*.ttf "${MESON_INSTALL_PREFIX}"/Contents/MacOS/ 5 | 6 | mkdir -p "${MESON_INSTALL_PREFIX}/Contents/Frameworks" 7 | 8 | # Meson doesn't bundle dependencies of our shared library by default, so we have 9 | # to do it on our own. 10 | # 11 | # This fixes the error: 12 | # "Symbol not found: _curl_easy_option_by_id" since that didn't exist in older 13 | # cURL libraries. 14 | echo 'Bundling dylibs for lcurl.so' 15 | cd "${MESON_INSTALL_PREFIX}/Contents" 16 | dylibbundler --overwrite-dir --create-dir --bundle-deps --fix-file MacOS/lcurl.so 17 | -------------------------------------------------------------------------------- /pobLogo.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hsource/pobfrontend/f0b601b07915b10878f9ec5eb5e9334265692476/pobLogo.icns -------------------------------------------------------------------------------- /pobWrapper.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd "${0%/*}" 4 | ./PathOfBuilding 5 | -------------------------------------------------------------------------------- /pobwindow.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "main.h" 10 | #include "subscript.hpp" 11 | 12 | extern "C" { 13 | #include "lua.h" 14 | #include "lualib.h" 15 | #include "lauxlib.h" 16 | #include "luajit.h" 17 | } 18 | 19 | 20 | class POBWindow : public QOpenGLWindow { 21 | Q_OBJECT 22 | public: 23 | // POBWindow(QWindow *parent = 0) : QOpenGLWindow(parent) {}; 24 | POBWindow() : stringCache(5) { 25 | // QSurfaceFormat theformat(format()); 26 | // format.setProfile(QSurfaceFormat::CompatibilityProfile); 27 | /* format.setDepthBufferSize(24); 28 | format.setStencilBufferSize(0); 29 | format.setGreenBufferSize(8); 30 | format.setRedBufferSize(8); 31 | format.setBlueBufferSize(8);*/ 32 | // theformat.setAlphaBufferSize(8); 33 | // std::cout << theformat.hasAlpha() << std::endl; 34 | // setFormat(theformat); 35 | baseTime = QDateTime::currentDateTime().toMSecsSinceEpoch(); 36 | scriptPath = QDir::currentPath(); 37 | scriptWorkDir = QDir::currentPath(); 38 | basePath = QDir::currentPath(); 39 | userPath = QDir::homePath(); 40 | 41 | fontFudge = 0; 42 | 43 | connect(&updateTimer, &QTimer::timeout, this, QOverload<>::of(&POBWindow::triggerUpdate)); 44 | updateTimer.start(100); 45 | } 46 | 47 | // POBWindow() : QOpenGLWindow() { 48 | // }; 49 | // ~POBWindow() {}; 50 | //protected: 51 | 52 | void initializeGL(); 53 | void resizeGL(int w, int h); 54 | void paintGL(); 55 | 56 | void subScriptFinished(); 57 | void mouseMoveEvent(QMouseEvent *event); 58 | void mousePressEvent(QMouseEvent *event); 59 | void mouseReleaseEvent(QMouseEvent *event); 60 | void mouseDoubleClickEvent(QMouseEvent *event); 61 | void wheelEvent(QWheelEvent *event); 62 | void keyPressEvent(QKeyEvent *event); 63 | void keyReleaseEvent(QKeyEvent *event); 64 | 65 | void LAssert(lua_State* L, int cond, const char* fmt, ...); 66 | int IsUserData(lua_State* L, int index, const char* metaName); 67 | 68 | void SetDrawLayer(int layer); 69 | void SetDrawLayer(int layer, int subLayer); 70 | void SetDrawSubLayer(int subLayer) { 71 | SetDrawLayer(curLayer, subLayer); 72 | } 73 | void AppendCmd(std::unique_ptr cmd); 74 | void DrawColor(const float col[4] = NULL); 75 | void DrawColor(uint32_t col); 76 | qint64 baseTime; 77 | QString scriptPath; 78 | QString scriptWorkDir; 79 | QString basePath; 80 | QString userPath; 81 | int curLayer; 82 | int curSubLayer; 83 | int fontFudge; 84 | int width; 85 | int height; 86 | bool isDrawing; 87 | QString fontName; 88 | float drawColor[4]; 89 | std::map, std::vector>> layers; 90 | QList> subScriptList; 91 | std::shared_ptr white; 92 | QCache> stringCache; 93 | QTimer updateTimer; 94 | void triggerUpdate(); 95 | }; 96 | -------------------------------------------------------------------------------- /subscript.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SUBSCRIPT_HPP 2 | #define SUBSCRIPT_HPP 3 | 4 | #include 5 | 6 | #include 7 | 8 | extern "C" { 9 | #include "lua.h" 10 | #include "lualib.h" 11 | #include "lauxlib.h" 12 | #include "luajit.h" 13 | } 14 | 15 | static int dummy_ConPrintf(lua_State* L) 16 | { 17 | return 0; 18 | } 19 | 20 | class SubScript : public QThread { 21 | Q_OBJECT 22 | public: 23 | SubScript(lua_State *L_main) { 24 | L = luaL_newstate(); 25 | // FIXME check for failure? 26 | lua_pushlightuserdata(L, this); 27 | lua_rawseti(L, LUA_REGISTRYINDEX, 0); 28 | luaL_openlibs(L); 29 | lua_pushcfunction(L, dummy_ConPrintf); 30 | lua_setglobal(L, "ConPrintf"); 31 | int err = luaL_loadstring(L, lua_tostring(L_main, 1)); 32 | if (err) { 33 | std::cout << "Error in subscript: " << err << std::endl; 34 | std::cout << lua_tostring(L, -1) << std::endl; 35 | } 36 | for (int stackpos = 4;stackpos <= lua_gettop(L_main);stackpos++) { 37 | switch (lua_type(L_main, stackpos)) { 38 | case LUA_TNIL: 39 | lua_pushnil(L); 40 | break; 41 | case LUA_TBOOLEAN: 42 | lua_pushboolean(L, lua_toboolean(L_main, stackpos)); 43 | break; 44 | case LUA_TNUMBER: 45 | lua_pushnumber(L, lua_tonumber(L_main, stackpos)); 46 | break; 47 | case LUA_TSTRING: 48 | lua_pushstring(L, lua_tostring(L_main, stackpos)); 49 | break; 50 | } 51 | } 52 | lua_pushnumber(L, lua_gettop(L_main) - 3); 53 | } 54 | 55 | void run() override { 56 | int numarg = (int)lua_tointeger(L, -1); 57 | lua_pop(L, 1); 58 | if (lua_pcall(L, numarg, LUA_MULTRET, 0)) { 59 | std::cout << "Error in thread call: " << lua_tostring(L, -1) << std::endl; 60 | } 61 | }; 62 | 63 | void onSubFinished(lua_State *L_main, int id) { 64 | lua_getfield(L_main, LUA_REGISTRYINDEX, "uicallbacks"); 65 | lua_getfield(L_main, -1, "MainObject"); 66 | lua_remove(L_main, -2); 67 | lua_getfield(L_main, -1, "OnSubFinished"); 68 | lua_insert(L_main, -2); 69 | lua_pushinteger(L_main, id); 70 | int n = lua_gettop(L); 71 | for (int i = 1; i <= n; i++) { 72 | switch (lua_type(L, i)) { 73 | case LUA_TNIL: 74 | lua_pushnil(L_main); 75 | break; 76 | case LUA_TBOOLEAN: 77 | lua_pushboolean(L_main, lua_toboolean(L, i)); 78 | break; 79 | case LUA_TNUMBER: 80 | lua_pushnumber(L_main, lua_tonumber(L, i)); 81 | break; 82 | case LUA_TSTRING: 83 | lua_pushstring(L_main, lua_tostring(L, i)); 84 | break; 85 | default: 86 | std::cout << "Subscript return " << (i - 1) << ": only nil, boolean, number and string can be returned from sub script" << std::endl; 87 | return; 88 | } 89 | } 90 | int result = lua_pcall(L_main, lua_gettop(L) + 2, 0, 0); 91 | if (result) { 92 | std::cout << "Error calling OnSubFinished: " << result << std::endl; 93 | std::cout << lua_tostring(L_main, -1) << std::endl; 94 | } 95 | } 96 | lua_State *L; 97 | }; 98 | 99 | #endif 100 | --------------------------------------------------------------------------------