Compare commits
115 Commits
04deddbe79
...
8c8635146e
Author | SHA1 | Date |
---|---|---|
Takase | 8c8635146e | |
Guldoman | 6d5c6051cd | |
Adam Harrison | 6bb9a89a8b | |
Shreyas A S | 02e421149b | |
Guldoman | 16bfa6d958 | |
Delta-official | 9c9f2dace0 | |
Takase | 8daf7dc926 | |
Adam | 5145194f1f | |
Takase | 6ad67c18f0 | |
Takase | 7ba1d1ba8e | |
Takase | ac3630c2ea | |
Adam | c470683005 | |
Takase | 8c9620aaed | |
Guldoman | 0ebf3c0393 | |
Takase | 74d9c15736 | |
Guldoman | 3775032c78 | |
Guldoman | ca3acd4ee9 | |
Luke aka SwissalpS | 0e5f35ff9f | |
Luke aka SwissalpS | 39319e2ce9 | |
Jan | 6e113cb15e | |
Guldoman | 9bb6589790 | |
Jan | e6c7001b5a | |
Guldoman | 94b4825754 | |
Guldoman | 12a552931e | |
Guldoman | ff38e449d1 | |
Guldoman | 122b72ed90 | |
Guldoman | f80a4563be | |
Guldoman | 862aba0f91 | |
Guldoman | 528e5641fb | |
Jefferson González | 35647067d8 | |
Jefferson González | ba753593f3 | |
takase1121 | 363b102abc | |
takase1121 | 252bf87ead | |
Jefferson González | 431c8f4a36 | |
Adam | 21db8313c1 | |
Guldoman | e7168a1e00 | |
takase1121 | c255e53d37 | |
takase1121 | a007a190ef | |
Adam Harrison | 6714732222 | |
Adam | aa2ac0a4ce | |
takase1121 | 2090afccca | |
Takase | 46260b8073 | |
Jan | 0fae012c68 | |
jgmdev | ff4364b0ff | |
Takase | e2a582d5fd | |
Takase | 3c60c1c7f1 | |
Guldoman | 532d3a6572 | |
Takase | 7f75619aa2 | |
Guldoman | 0be18493a9 | |
Takase | 6ad288aa39 | |
Takase | c2357721e5 | |
Jefferson González | baa8f528f1 | |
sammyette | 2978037f51 | |
vqn | 7aa1217878 | |
Guldoman | 0ee346014e | |
Takase | 4e626bc320 | |
Takase | c133c39e92 | |
Takase | ac9ca96698 | |
Takase | 26ff5e28a6 | |
Jan | 662fde364b | |
Takase | bc2c433b00 | |
Takase | e935454992 | |
Adam | 7ca0ec18ca | |
vqn | 449f7d66c3 | |
vqn | 46ea86e28c | |
Adam | ee80b451c6 | |
Guldoman | 1c8c569fae | |
Adam | 1becf35508 | |
Guldoman | b005454652 | |
Adam Harrison | 73ff025552 | |
Adam | 7f0651155d | |
Guldoman | 146dca9188 | |
Takase | 350131dabc | |
Guldoman | 1d86665b6d | |
Guldoman | 3739bf0186 | |
Guldoman | 4f1360a6c5 | |
Guldoman | 7907fa785c | |
Takase | 193871869d | |
Guldoman | dbb9f30c81 | |
Takase | 2d2d715fd9 | |
Guldoman | e1f92683bc | |
Adam | 83c27cf9f4 | |
vqn | 9b4a86f763 | |
Adam | b9cc661a84 | |
Guldoman | 7e0ddf2817 | |
jgmdev | 51ab72f715 | |
Eric Gaudet | 911eb325cc | |
Guldoman | d9925b7d44 | |
Guldoman | be5d23557d | |
Jefferson González | ce9d540e92 | |
Jefferson González | 84039331a5 | |
Jefferson González | 1c2571bad7 | |
Jefferson González | d68583b688 | |
Guldoman | 57cd4e2949 | |
Takase | ab3d6004a1 | |
vqn | b634b61866 | |
Himura Kazuto | aef400bc90 | |
sammyette | 3f917dcb45 | |
Adam | 4ef4b99c7a | |
Adam | f74716b436 | |
jgmdev | 8fbc843260 | |
Jefferson González | ccceb2a65c | |
Jan | 16182d01d8 | |
Adam Harrison | de06dcc5bb | |
Jefferson González | 9dcdf1f7c9 | |
Adam | 4682092b8d | |
Guldoman | 12bae1ec95 | |
Jan | 64065b98ca | |
Adam Harrison | bdd87298d6 | |
Guldoman | b58ba3fede | |
xwii | e4c5fceaf9 | |
Merlin Volkmer | 75b6173dc9 | |
adityaraj | 8c4f093c41 | |
Jan | c44a3cd291 | |
Takase | 3edd53a835 |
|
@ -30,9 +30,9 @@ jobs:
|
|||
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
||||
echo "INSTALL_REF=${GITHUB_REF##*/}" >> "$GITHUB_ENV"
|
||||
echo "INSTALL_NAME=lite-xl-${GITHUB_REF##*/}-linux-$(uname -m)-portable" >> "$GITHUB_ENV"
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Python Setup
|
||||
uses: actions/setup-python@v2
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.9
|
||||
- name: Update Packages
|
||||
|
@ -47,18 +47,21 @@ jobs:
|
|||
if: ${{ matrix.config.cc == 'gcc' }}
|
||||
run: bash scripts/package.sh --version ${INSTALL_REF} --debug --binary
|
||||
- name: Upload Artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
if: ${{ matrix.config.cc == 'gcc' }}
|
||||
with:
|
||||
name: Linux Artifacts
|
||||
path: ${{ env.INSTALL_NAME }}.tar.gz
|
||||
|
||||
build_macos:
|
||||
name: macOS (x86_64)
|
||||
name: macOS
|
||||
runs-on: macos-11
|
||||
env:
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
strategy:
|
||||
matrix:
|
||||
arch: ['x86_64', 'arm64']
|
||||
steps:
|
||||
- name: System Information
|
||||
run: |
|
||||
|
@ -70,24 +73,60 @@ jobs:
|
|||
run: |
|
||||
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
||||
echo "INSTALL_REF=${GITHUB_REF##*/}" >> "$GITHUB_ENV"
|
||||
echo "INSTALL_NAME=lite-xl-${GITHUB_REF##*/}-macos-$(uname -m)" >> "$GITHUB_ENV"
|
||||
- uses: actions/checkout@v2
|
||||
echo "INSTALL_NAME=lite-xl-${GITHUB_REF##*/}-macos-${{ matrix.arch }}" >> "$GITHUB_ENV"
|
||||
if [[ $(uname -m) != ${{ matrix.arch }} ]]; then echo "ARCH=--cross-arch ${{ matrix.arch }}" >> "$GITHUB_ENV"; fi
|
||||
- uses: actions/checkout@v3
|
||||
- name: Python Setup
|
||||
uses: actions/setup-python@v2
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.9
|
||||
- name: Install Dependencies
|
||||
run: bash scripts/install-dependencies.sh --debug
|
||||
# --lhelper will eliminate a warning with arm64 and libusb
|
||||
run: bash scripts/install-dependencies.sh --debug --lhelper
|
||||
- name: Build
|
||||
run: |
|
||||
bash --version
|
||||
bash scripts/build.sh --bundle --debug --forcefallback
|
||||
bash scripts/build.sh --bundle --debug --forcefallback $ARCH
|
||||
- name: Create DMG Image
|
||||
run: bash scripts/package.sh --version ${INSTALL_REF} --debug --dmg
|
||||
run: bash scripts/package.sh --version ${INSTALL_REF} $ARCH --debug --dmg
|
||||
- name: Upload DMG Image
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: macOS DMG Image
|
||||
name: macOS DMG Images
|
||||
path: ${{ env.INSTALL_NAME }}.dmg
|
||||
|
||||
build_macos_universal:
|
||||
name: macOS (Universal)
|
||||
runs-on: macos-11
|
||||
needs: build_macos
|
||||
steps:
|
||||
- name: System Information
|
||||
run: |
|
||||
system_profiler SPSoftwareDataType
|
||||
bash --version
|
||||
gcc -v
|
||||
xcodebuild -version
|
||||
- name: Set Environment Variables
|
||||
run: |
|
||||
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
||||
echo "INSTALL_NAME=lite-xl-${GITHUB_REF##*/}-macos-universal" >> "$GITHUB_ENV"
|
||||
- uses: actions/checkout@v3
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
id: download
|
||||
with:
|
||||
name: macOS DMG Images
|
||||
path: dmgs-original
|
||||
- name: Install appdmg
|
||||
run: cd ~; npm i appdmg; cd -
|
||||
- name: Make universal bundles
|
||||
run: |
|
||||
bash --version
|
||||
bash scripts/make-universal-binaries.sh ${{ steps.download.outputs.download-path }} "${INSTALL_NAME}"
|
||||
- name: Upload DMG Image
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: macOS Universal DMG Images
|
||||
path: ${{ env.INSTALL_NAME }}.dmg
|
||||
|
||||
build_windows_msys2:
|
||||
|
@ -95,20 +134,26 @@ jobs:
|
|||
runs-on: windows-2019
|
||||
strategy:
|
||||
matrix:
|
||||
msystem: [MINGW32, MINGW64]
|
||||
config:
|
||||
- {msystem: MINGW32, arch: i686}
|
||||
- {msystem: MINGW64, arch: x86_64}
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: ${{ matrix.msystem }}
|
||||
update: true
|
||||
msystem: ${{ matrix.config.msystem }}
|
||||
install: >-
|
||||
base-devel
|
||||
git
|
||||
zip
|
||||
mingw-w64-${{ matrix.config.arch }}-gcc
|
||||
mingw-w64-${{ matrix.config.arch }}-meson
|
||||
mingw-w64-${{ matrix.config.arch }}-ninja
|
||||
mingw-w64-${{ matrix.config.arch }}-ca-certificates
|
||||
mingw-w64-${{ matrix.config.arch }}-ntldd
|
||||
- name: Set Environment Variables
|
||||
run: |
|
||||
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
||||
|
@ -119,6 +164,7 @@ jobs:
|
|||
echo "INSTALL_NAME=lite-xl-${GITHUB_REF##*/}-windows-i686" >> "$GITHUB_ENV"
|
||||
fi
|
||||
- name: Install Dependencies
|
||||
if: false
|
||||
run: bash scripts/install-dependencies.sh --debug
|
||||
- name: Build
|
||||
run: |
|
||||
|
@ -127,7 +173,7 @@ jobs:
|
|||
- name: Package
|
||||
run: bash scripts/package.sh --version ${INSTALL_REF} --debug --binary
|
||||
- name: Upload Artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: Windows Artifacts
|
||||
path: ${{ env.INSTALL_NAME }}.zip
|
||||
|
@ -137,34 +183,40 @@ jobs:
|
|||
runs-on: windows-2019
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [amd64, amd64_x86]
|
||||
arch:
|
||||
- { target: x86, name: i686 }
|
||||
- { target: x64, name: x86_64 }
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ilammy/msvc-dev-cmd@v1
|
||||
with:
|
||||
arch: ${{ matrix.arch }}
|
||||
- uses: actions/setup-python@v1
|
||||
arch: ${{ matrix.arch.target }}
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Install meson and ninja
|
||||
run: pip install meson ninja
|
||||
- name: Set up environment variables
|
||||
run: |
|
||||
"INSTALL_NAME=lite-xl-$($env:GITHUB_REF -replace ".*/")-windows-msvc-${{ matrix.arch }}" >> $env:GITHUB_ENV
|
||||
"INSTALL_NAME=lite-xl-$($env:GITHUB_REF -replace ".*/")-windows-msvc-${{ matrix.arch.name }}" >> $env:GITHUB_ENV
|
||||
"INSTALL_REF=$($env:GITHUB_REF -replace ".*/")" >> $env:GITHUB_ENV
|
||||
"LUA_SUBPROJECT_PATH=subprojects/lua-5.4.4" >> $env:GITHUB_ENV
|
||||
- name: Configure
|
||||
run: |
|
||||
meson setup --wrap-mode=forcefallback build
|
||||
# Download the subprojects first so we can patch it before configuring.
|
||||
# This avoids reconfiguring the subprojects when compiling.
|
||||
meson subprojects download
|
||||
Get-Content -Path resources/windows/001-lua-unicode.diff -Raw | patch -d $env:LUA_SUBPROJECT_PATH -p1 --forward
|
||||
meson setup --wrap-mode=forcefallback build
|
||||
- name: Build
|
||||
run: meson install -C build --destdir="../lite-xl"
|
||||
run: |
|
||||
meson install -C build --destdir="../lite-xl"
|
||||
- name: Package
|
||||
run: |
|
||||
Remove-Item -Recurse -Force -Path "lite-xl/lib","lite-xl/include"
|
||||
Compress-Archive -Path lite-xl -DestinationPath "$env:INSTALL_NAME.zip"
|
||||
- name: Upload Artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: Windows Artifacts (MSVC)
|
||||
path: ${{ env.INSTALL_NAME }}.zip
|
||||
|
|
|
@ -15,13 +15,13 @@ on:
|
|||
jobs:
|
||||
release:
|
||||
name: Create Release
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
version: ${{ steps.tag.outputs.version }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
- name: Fetch Version
|
||||
id: tag
|
||||
run: |
|
||||
|
@ -49,7 +49,8 @@ jobs:
|
|||
build_linux:
|
||||
name: Linux
|
||||
needs: release
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/lite-xl/lite-xl-build-box:latest
|
||||
env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
|
@ -58,17 +59,27 @@ jobs:
|
|||
run: |
|
||||
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
||||
echo "INSTALL_REF=${{ needs.release.outputs.version }}" >> "$GITHUB_ENV"
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
# disabled because this will break our own Python install
|
||||
- name: Python Setup
|
||||
uses: actions/setup-python@v2
|
||||
if: false
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.9
|
||||
|
||||
# disabled because the container has up-to-date packages
|
||||
- name: Update Packages
|
||||
if: false
|
||||
run: sudo apt-get update
|
||||
|
||||
# disabled as the dependencies are already installed
|
||||
- name: Install Dependencies
|
||||
if: false
|
||||
run: |
|
||||
bash scripts/install-dependencies.sh --debug
|
||||
sudo apt-get install -y ccache
|
||||
|
||||
- name: Build Portable
|
||||
run: |
|
||||
bash --version
|
||||
|
@ -93,7 +104,7 @@ jobs:
|
|||
LiteXL-${{ env.INSTALL_REF }}-addons-x86_64.AppImage
|
||||
|
||||
build_macos:
|
||||
name: macOS (x86_64)
|
||||
name: macOS
|
||||
needs: release
|
||||
runs-on: macos-11
|
||||
strategy:
|
||||
|
@ -115,9 +126,10 @@ jobs:
|
|||
echo "INSTALL_REF=${{ needs.release.outputs.version }}" >> "$GITHUB_ENV"
|
||||
echo "INSTALL_NAME=lite-xl-${{ needs.release.outputs.version }}-macos-${{ matrix.arch }}" >> "$GITHUB_ENV"
|
||||
echo "INSTALL_NAME_ADDONS=lite-xl-${{ needs.release.outputs.version }}-addons-macos-${{ matrix.arch }}" >> "$GITHUB_ENV"
|
||||
- uses: actions/checkout@v2
|
||||
if [[ $(uname -m) != ${{ matrix.arch }} ]]; then echo "ARCH=--cross-arch ${{ matrix.arch }}" >> "$GITHUB_ENV"; fi
|
||||
- uses: actions/checkout@v3
|
||||
- name: Python Setup
|
||||
uses: actions/setup-python@v2
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.9
|
||||
- name: Install Dependencies
|
||||
|
@ -125,11 +137,18 @@ jobs:
|
|||
- name: Build
|
||||
run: |
|
||||
bash --version
|
||||
CROSS_ARCH=${{ matrix.arch }} bash scripts/build.sh --bundle --debug --forcefallback --release
|
||||
bash scripts/build.sh --bundle --debug --forcefallback --release $ARCH
|
||||
- name: Create DMG Image
|
||||
run: |
|
||||
CROSS_ARCH=${{ matrix.arch }} bash scripts/package.sh --version ${INSTALL_REF} --debug --dmg --release
|
||||
CROSS_ARCH=${{ matrix.arch }} bash scripts/package.sh --version ${INSTALL_REF} --debug --addons --dmg --release
|
||||
bash scripts/package.sh --version ${INSTALL_REF} $ARCH --debug --dmg --release
|
||||
bash scripts/package.sh --version ${INSTALL_REF} $ARCH --debug --addons --dmg --release
|
||||
- name: Upload Artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: macOS DMG Images
|
||||
path: |
|
||||
${{ env.INSTALL_NAME }}.dmg
|
||||
${{ env.INSTALL_NAME_ADDONS }}.dmg
|
||||
- name: Upload Files
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
|
@ -139,6 +158,54 @@ jobs:
|
|||
${{ env.INSTALL_NAME }}.dmg
|
||||
${{ env.INSTALL_NAME_ADDONS }}.dmg
|
||||
|
||||
build_macos_universal:
|
||||
name: macOS (Universal)
|
||||
needs: [release, build_macos]
|
||||
runs-on: macos-11
|
||||
steps:
|
||||
- name: System Information
|
||||
run: |
|
||||
system_profiler SPSoftwareDataType
|
||||
bash --version
|
||||
gcc -v
|
||||
xcodebuild -version
|
||||
- name: Set Environment Variables
|
||||
run: |
|
||||
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
||||
echo "INSTALL_BASE=lite-xl-${{ needs.release.outputs.version }}-macos" >> "$GITHUB_ENV"
|
||||
echo "INSTALL_BASE_ADDONS=lite-xl-${{ needs.release.outputs.version }}-addons-macos" >> "$GITHUB_ENV"
|
||||
- uses: actions/checkout@v2
|
||||
- name: Download Artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
id: download
|
||||
with:
|
||||
name: macOS DMG Images
|
||||
path: dmgs-original
|
||||
- name: Python Setup
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.9
|
||||
- name: Install appdmg
|
||||
run: cd ~; npm i appdmg; cd -
|
||||
- name: Prepare DMG Images
|
||||
run: |
|
||||
mkdir -p dmgs-addons dmgs-normal
|
||||
mv -v "${{ steps.download.outputs.download-path }}/$INSTALL_BASE-"{x86_64,arm64}.dmg dmgs-normal
|
||||
mv -v "${{ steps.download.outputs.download-path }}/$INSTALL_BASE_ADDONS-"{x86_64,arm64}.dmg dmgs-addons
|
||||
- name: Create Universal DMGs
|
||||
run: |
|
||||
bash --version
|
||||
bash scripts/make-universal-binaries.sh dmgs-normal "$INSTALL_BASE-universal"
|
||||
bash scripts/make-universal-binaries.sh dmgs-addons "$INSTALL_BASE_ADDONS-universal"
|
||||
- name: Upload Files
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
tag_name: ${{ needs.release.outputs.version }}
|
||||
draft: true
|
||||
files: |
|
||||
${{ env.INSTALL_BASE }}-universal.dmg
|
||||
${{ env.INSTALL_BASE_ADDONS }}-universal.dmg
|
||||
|
||||
build_windows_msys2:
|
||||
name: Windows
|
||||
needs: release
|
||||
|
@ -150,7 +217,7 @@ jobs:
|
|||
run:
|
||||
shell: msys2 {0}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: ${{ matrix.msystem }}
|
||||
|
|
|
@ -21,3 +21,4 @@ lite-xl*
|
|||
LiteXL*
|
||||
|
||||
!resources/windows/*.diff
|
||||
!resources/windows/*.exe.manifest.in
|
||||
|
|
10
README.md
10
README.md
|
@ -92,15 +92,15 @@ cd lite-xl
|
|||
|
||||
To run lite-xl without installing:
|
||||
```sh
|
||||
cd bin
|
||||
./lite-xl
|
||||
```
|
||||
|
||||
To install lite-xl copy files over into appropriate directories:
|
||||
|
||||
```sh
|
||||
mkdir -p $HOME/.local/bin && cp bin/lite-xl $HOME/.local/bin
|
||||
cp -r share $HOME/.local
|
||||
rm -rf $HOME/.local/share/lite-xl $HOME/.local/bin/lite-xl
|
||||
mkdir -p $HOME/.local/bin && cp lite-xl $HOME/.local/bin/
|
||||
mkdir -p $HOME/.local/share/lite-xl && cp -r data/* $HOME/.local/share/lite-xl/
|
||||
```
|
||||
|
||||
If `$HOME/.local/bin` is not in PATH:
|
||||
|
@ -122,8 +122,8 @@ To uninstall just run:
|
|||
```sh
|
||||
rm -f $HOME/.local/bin/lite-xl
|
||||
rm -rf $HOME/.local/share/icons/hicolor/scalable/apps/lite-xl.svg \
|
||||
$HOME/.local/share/applications/org.lite_xl.lite_xl.desktop \
|
||||
$HOME/.local/share/metainfo/org.lite_xl.lite_xl.appdata.xml \
|
||||
$HOME/.local/share/applications/com.lite_xl.LiteXL.desktop \
|
||||
$HOME/.local/share/metainfo/com.lite_xl.LiteXL.appdata.xml \
|
||||
$HOME/.local/share/lite-xl
|
||||
```
|
||||
|
||||
|
|
|
@ -18,6 +18,8 @@ show_help() {
|
|||
echo " Default: '$(get_default_build_dir)'."
|
||||
echo "-p --prefix PREFIX Install directory prefix."
|
||||
echo " Default: '/'."
|
||||
echo " --cross-platform PLATFORM The platform to cross compile for."
|
||||
echo " --cross-arch ARCH The architecture to cross compile for."
|
||||
echo " --debug Debug this script."
|
||||
echo
|
||||
echo "Build options:"
|
||||
|
@ -27,6 +29,7 @@ show_help() {
|
|||
echo "-P --portable Create a portable package."
|
||||
echo "-O --pgo Use profile guided optimizations (pgo)."
|
||||
echo " Requires running the application iteractively."
|
||||
echo " --cross-file CROSS_FILE The cross file used for compiling."
|
||||
echo
|
||||
echo "Package options:"
|
||||
echo
|
||||
|
@ -60,6 +63,12 @@ main() {
|
|||
local portable
|
||||
local pgo
|
||||
local release
|
||||
local cross_platform
|
||||
local cross_platform_option=()
|
||||
local cross_arch
|
||||
local cross_arch_option=()
|
||||
local cross_file
|
||||
local cross_file_option=()
|
||||
|
||||
for i in "$@"; do
|
||||
case $i in
|
||||
|
@ -123,6 +132,21 @@ main() {
|
|||
pgo="--pgo"
|
||||
shift
|
||||
;;
|
||||
--cross-platform)
|
||||
cross_platform="$2"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
--cross-arch)
|
||||
cross_arch="$2"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
--cross-file)
|
||||
cross_file="$2"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
--debug)
|
||||
debug="--debug"
|
||||
set -x
|
||||
|
@ -143,10 +167,18 @@ main() {
|
|||
if [[ -n $dest_dir ]]; then dest_dir_option=("--destdir" "${dest_dir}"); fi
|
||||
if [[ -n $prefix ]]; then prefix_option=("--prefix" "${prefix}"); fi
|
||||
if [[ -n $version ]]; then version_option=("--version" "${version}"); fi
|
||||
if [[ -n $cross_platform ]]; then cross_platform_option=("--cross-platform" "${cross_platform}"); fi
|
||||
if [[ -n $cross_arch ]]; then cross_arch_option=("--cross-arch" "${cross_arch}"); fi
|
||||
if [[ -n $cross_file ]]; then cross_file_option=("--cross-file" "${cross_file}"); fi
|
||||
|
||||
|
||||
|
||||
source scripts/build.sh \
|
||||
${build_dir_option[@]} \
|
||||
${prefix_option[@]} \
|
||||
${cross_platform_option[@]} \
|
||||
${cross_arch_option[@]} \
|
||||
${cross_file_option[@]} \
|
||||
$debug \
|
||||
$force_fallback \
|
||||
$bundle \
|
||||
|
@ -159,6 +191,8 @@ main() {
|
|||
${dest_dir_option[@]} \
|
||||
${prefix_option[@]} \
|
||||
${version_option[@]} \
|
||||
${cross_platform_option[@]} \
|
||||
${cross_arch_option[@]} \
|
||||
--binary \
|
||||
--addons \
|
||||
$debug \
|
||||
|
|
|
@ -1,25 +1,55 @@
|
|||
local core = require "core"
|
||||
local command = {}
|
||||
|
||||
---A predicate function accepts arguments from `command.perform()` and evaluates to a boolean. </br>
|
||||
---If the function returns true, then the function associated with the command is executed.
|
||||
---
|
||||
---The predicate function can also return other values after the boolean, which will
|
||||
---be passed into the function associated with the command.
|
||||
---@alias core.command.predicate_function fun(...: any): boolean, ...
|
||||
|
||||
---A predicate is a string, an Object or a function, that is used to determine
|
||||
---whether a command should be executed.
|
||||
---
|
||||
---If the predicate is a string, it is resolved into an `Object` via `require()`
|
||||
---and checked against the active view with `Object:extends()`. </br>
|
||||
---For example, `"core.docview"` will match any view that inherits from `DocView`. </br>
|
||||
---A `!` can be appended to the predicate to strictly match the current view via `Object:is()`,
|
||||
---instead of matching any view that inherits the predicate.
|
||||
---
|
||||
---If the predicate is a table, it is checked against the active view with `Object:extends()`.
|
||||
---Strict matching via `Object:is()` is not available.
|
||||
---
|
||||
---If the predicate is a function, it must behave like a predicate function.
|
||||
---@see core.command.predicate_function
|
||||
---@alias core.command.predicate string|core.object|core.command.predicate_function
|
||||
|
||||
---A command is identified by a command name.
|
||||
---The command name contains a category and the name itself, separated by a colon (':').
|
||||
---
|
||||
---All commands should be in lowercase and should not contain whitespaces; instead
|
||||
---they should be replaced by a dash ('-').
|
||||
---@alias core.command.command_name string
|
||||
|
||||
---The predicate and its associated function.
|
||||
---@class core.command.command
|
||||
---@field predicate core.command.predicate_function
|
||||
---@field perform fun(...: any)
|
||||
|
||||
---@type { [string]: core.command.command }
|
||||
command.map = {}
|
||||
|
||||
---@type core.command.predicate_function
|
||||
local always_true = function() return true end
|
||||
|
||||
|
||||
---Used iternally by command.add, statusview, and contextmenu to generate a
|
||||
---function with a condition to evaluate returning the boolean result of this
|
||||
---evaluation.
|
||||
---This function takes in a predicate and produces a predicate function
|
||||
---that is internally used to dispatch and execute commands.
|
||||
---
|
||||
---If a string predicate is given it is treated as a require import that should
|
||||
---return a valid object which is checked against the current active view,
|
||||
---eg: "core.docview" will match any view that inherits from DocView. Appending
|
||||
---a `!` at the end of the string means we want to match the given object
|
||||
---from the import strcitly eg: "core.docview!" only DocView is matched.
|
||||
---A function that returns a boolean can be used instead to perform a custom
|
||||
---evaluation, setting to nil means always evaluates to true.
|
||||
---
|
||||
---@param predicate string | table | function
|
||||
---@return function
|
||||
---This function should not be called manually.
|
||||
---@see core.command.predicate
|
||||
---@param predicate core.command.predicate|nil If nil, the predicate always evaluates to true.
|
||||
---@return core.command.predicate_function
|
||||
function command.generate_predicate(predicate)
|
||||
predicate = predicate or always_true
|
||||
local strict = false
|
||||
|
@ -38,10 +68,20 @@ function command.generate_predicate(predicate)
|
|||
predicate = function(...) return core.active_view:is(class), core.active_view, ... end
|
||||
end
|
||||
end
|
||||
---@cast predicate core.command.predicate_function
|
||||
return predicate
|
||||
end
|
||||
|
||||
|
||||
---Adds commands to the map.
|
||||
---
|
||||
---The function accepts a table containing a list of commands
|
||||
---and their functions. </br>
|
||||
---If a command already exists, it will be replaced.
|
||||
---@see core.command.predicate
|
||||
---@see core.command.command_name
|
||||
---@param predicate core.command.predicate
|
||||
---@param map { [core.command.command_name]: fun(...: any) }
|
||||
function command.add(predicate, map)
|
||||
predicate = command.generate_predicate(predicate)
|
||||
for name, fn in pairs(map) do
|
||||
|
@ -57,11 +97,21 @@ local function capitalize_first(str)
|
|||
return str:sub(1, 1):upper() .. str:sub(2)
|
||||
end
|
||||
|
||||
---Prettifies the command name.
|
||||
---
|
||||
---This function adds a space between the colon and the command name,
|
||||
---replaces dashes with spaces and capitalizes the command appropriately.
|
||||
---@see core.command.command_name
|
||||
---@param name core.command.command_name
|
||||
---@return string
|
||||
function command.prettify_name(name)
|
||||
---@diagnostic disable-next-line: redundant-return-value
|
||||
return name:gsub(":", ": "):gsub("-", " "):gsub("%S+", capitalize_first)
|
||||
end
|
||||
|
||||
|
||||
---Returns all the commands that can be executed (their predicates evaluate to true).
|
||||
---@return core.command.command_name[]
|
||||
function command.get_all_valid()
|
||||
local res = {}
|
||||
local memoized_predicates = {}
|
||||
|
@ -76,6 +126,10 @@ function command.get_all_valid()
|
|||
return res
|
||||
end
|
||||
|
||||
---Checks whether a command can be executed (its predicate evaluates to true).
|
||||
---@param name core.command.command_name
|
||||
---@param ... any
|
||||
---@return boolean
|
||||
function command.is_valid(name, ...)
|
||||
return command.map[name] and command.map[name].predicate(...)
|
||||
end
|
||||
|
@ -98,16 +152,30 @@ local function perform(name, ...)
|
|||
end
|
||||
|
||||
|
||||
function command.perform(...)
|
||||
local ok, res = core.try(perform, ...)
|
||||
---Performs a command.
|
||||
---
|
||||
---The arguments passed into this function are forwarded to the predicate function. </br>
|
||||
---If the predicate function returns more than 1 value, the other values are passed
|
||||
---to the command.
|
||||
---
|
||||
---Otherwise, the arguments passed into this function are passed directly
|
||||
---to the command.
|
||||
---@see core.command.predicate
|
||||
---@see core.command.predicate_function
|
||||
---@param name core.command.command_name
|
||||
---@param ... any
|
||||
---@return boolean # true if the command is performed successfully.
|
||||
function command.perform(name, ...)
|
||||
local ok, res = core.try(perform, name, ...)
|
||||
return not ok or res
|
||||
end
|
||||
|
||||
|
||||
---Inserts the default commands for Lite XL into the map.
|
||||
function command.add_defaults()
|
||||
local reg = {
|
||||
"core", "root", "command", "doc", "findreplace",
|
||||
"files", "drawwhitespace", "dialog", "log", "statusbar"
|
||||
"files", "dialog", "log", "statusbar"
|
||||
}
|
||||
for _, name in ipairs(reg) do
|
||||
require("core.commands." .. name)
|
||||
|
|
|
@ -44,8 +44,8 @@ local function save(filename)
|
|||
else
|
||||
core.error(err)
|
||||
core.nag_view:show("Saving failed", string.format("Could not save \"%s\" do you want to save to another location?", doc().filename), {
|
||||
{ font = style.font, text = "No", default_no = true },
|
||||
{ font = style.font, text = "Yes" , default_yes = true }
|
||||
{ text = "No", default_no = true },
|
||||
{ text = "Yes", default_yes = true }
|
||||
}, function(item)
|
||||
if item.text == "Yes" then
|
||||
core.add_thread(function()
|
||||
|
@ -62,10 +62,10 @@ local function cut_or_copy(delete)
|
|||
local text = ""
|
||||
core.cursor_clipboard = {}
|
||||
core.cursor_clipboard_whole_line = {}
|
||||
for idx, line1, col1, line2, col2 in doc():get_selections() do
|
||||
for idx, line1, col1, line2, col2 in doc():get_selections(true, true) do
|
||||
if line1 ~= line2 or col1 ~= col2 then
|
||||
text = doc():get_text(line1, col1, line2, col2)
|
||||
full_text = full_text == "" and text or (full_text .. " " .. text)
|
||||
full_text = full_text == "" and text or (text .. " " .. full_text)
|
||||
core.cursor_clipboard_whole_line[idx] = false
|
||||
if delete then
|
||||
doc():delete_to_cursor(idx, 0)
|
||||
|
@ -73,7 +73,7 @@ local function cut_or_copy(delete)
|
|||
else -- Cut/copy whole line
|
||||
-- Remove newline from the text. It will be added as needed on paste.
|
||||
text = string.sub(doc().lines[line1], 1, -2)
|
||||
full_text = full_text == "" and text or (full_text .. text .. "\n")
|
||||
full_text = full_text == "" and text .. "\n" or (text .. "\n" .. full_text)
|
||||
core.cursor_clipboard_whole_line[idx] = true
|
||||
if delete then
|
||||
if line1 < #doc().lines then
|
||||
|
@ -83,10 +83,12 @@ local function cut_or_copy(delete)
|
|||
else
|
||||
doc():remove(line1 - 1, math.huge, line1, math.huge)
|
||||
end
|
||||
doc():set_selections(idx, line1, col1, line2, col2)
|
||||
end
|
||||
end
|
||||
core.cursor_clipboard[idx] = text
|
||||
end
|
||||
if delete then doc():merge_cursors() end
|
||||
core.cursor_clipboard["full"] = full_text
|
||||
system.set_clipboard(full_text)
|
||||
end
|
||||
|
@ -323,7 +325,7 @@ local commands = {
|
|||
end,
|
||||
|
||||
["doc:delete"] = function(dv)
|
||||
for idx, line1, col1, line2, col2 in dv.doc:get_selections() do
|
||||
for idx, line1, col1, line2, col2 in dv.doc:get_selections(true, true) do
|
||||
if line1 == line2 and col1 == col2 and dv.doc.lines[line1]:find("^%s*$", col1) then
|
||||
dv.doc:remove(line1, col1, line1, math.huge)
|
||||
end
|
||||
|
@ -333,7 +335,7 @@ local commands = {
|
|||
|
||||
["doc:backspace"] = function(dv)
|
||||
local _, indent_size = dv.doc:get_indent_info()
|
||||
for idx, line1, col1, line2, col2 in dv.doc:get_selections() do
|
||||
for idx, line1, col1, line2, col2 in dv.doc:get_selections(true, true) do
|
||||
if line1 == line2 and col1 == col2 then
|
||||
local text = dv.doc:get_text(line1, 1, line1, col1)
|
||||
if #text >= indent_size and text:find("^ *$") then
|
||||
|
@ -357,7 +359,7 @@ local commands = {
|
|||
["doc:select-lines"] = function(dv)
|
||||
for idx, line1, _, line2 in dv.doc:get_selections(true) do
|
||||
append_line_if_last_line(line2)
|
||||
dv.doc:set_selections(idx, line1, 1, line2 + 1, 1)
|
||||
dv.doc:set_selections(idx, line2 + 1, 1, line1, 1)
|
||||
end
|
||||
end,
|
||||
|
||||
|
@ -698,6 +700,7 @@ commands["doc:move-to-previous-char"] = function(dv)
|
|||
dv.doc:move_to_cursor(idx, translate.previous_char)
|
||||
end
|
||||
end
|
||||
dv.doc:merge_cursors()
|
||||
end
|
||||
|
||||
commands["doc:move-to-next-char"] = function(dv)
|
||||
|
@ -708,6 +711,7 @@ commands["doc:move-to-next-char"] = function(dv)
|
|||
dv.doc:move_to_cursor(idx, translate.next_char)
|
||||
end
|
||||
end
|
||||
dv.doc:merge_cursors()
|
||||
end
|
||||
|
||||
command.add("core.docview", commands)
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
local command = require "core.command"
|
||||
local config = require "core.config"
|
||||
|
||||
command.add(nil, {
|
||||
["draw-whitespace:toggle"] = function()
|
||||
config.draw_whitespace = not config.draw_whitespace
|
||||
end,
|
||||
|
||||
["draw-whitespace:disable"] = function()
|
||||
config.draw_whitespace = false
|
||||
end,
|
||||
|
||||
["draw-whitespace:enable"] = function()
|
||||
config.draw_whitespace = true
|
||||
end,
|
||||
})
|
|
@ -193,6 +193,23 @@ local function select_next(reverse)
|
|||
if l2 then doc():set_selection(l2, c2, l1, c1) end
|
||||
end
|
||||
|
||||
---@param in_selection? boolean whether to replace in the selections only, or in the whole file.
|
||||
local function find_replace(in_selection)
|
||||
local l1, c1, l2, c2 = doc():get_selection()
|
||||
local selected_text = ""
|
||||
if not in_selection then
|
||||
selected_text = doc():get_text(l1, c1, l2, c2)
|
||||
doc():set_selection(l2, c2, l2, c2)
|
||||
end
|
||||
replace("Text", l1 == l2 and selected_text or "", function(text, old, new)
|
||||
if not find_regex then
|
||||
return text:gsub(old:gsub("%W", "%%%1"), new:gsub("%%", "%%%%"), nil)
|
||||
end
|
||||
local result, matches = regex.gsub(regex.compile(old, "m"), text, new)
|
||||
return result, matches
|
||||
end)
|
||||
end
|
||||
|
||||
command.add(has_unique_selection, {
|
||||
["find-replace:select-next"] = select_next,
|
||||
["find-replace:select-previous"] = function() select_next(true) end,
|
||||
|
@ -209,15 +226,11 @@ command.add("core.docview!", {
|
|||
end,
|
||||
|
||||
["find-replace:replace"] = function()
|
||||
local l1, c1, l2, c2 = doc():get_selection()
|
||||
local selected_text = doc():get_text(l1, c1, l2, c2)
|
||||
replace("Text", l1 == l2 and selected_text or "", function(text, old, new)
|
||||
if not find_regex then
|
||||
return text:gsub(old:gsub("%W", "%%%1"), new:gsub("%%", "%%%%"), nil)
|
||||
end
|
||||
local result, matches = regex.gsub(regex.compile(old, "m"), text, new)
|
||||
return result, matches
|
||||
end)
|
||||
find_replace()
|
||||
end,
|
||||
|
||||
["find-replace:replace-in-selection"] = function()
|
||||
find_replace(true)
|
||||
end,
|
||||
|
||||
["find-replace:replace-symbol"] = function()
|
||||
|
@ -240,34 +253,38 @@ command.add("core.docview!", {
|
|||
})
|
||||
|
||||
local function valid_for_finding()
|
||||
return core.active_view:is(DocView) or core.active_view:is(CommandView)
|
||||
-- Allow using this while in the CommandView
|
||||
if core.active_view:is(CommandView) and last_view then
|
||||
return true, last_view
|
||||
end
|
||||
return core.active_view:is(DocView), core.active_view
|
||||
end
|
||||
|
||||
command.add(valid_for_finding, {
|
||||
["find-replace:repeat-find"] = function()
|
||||
["find-replace:repeat-find"] = function(dv)
|
||||
if not last_fn then
|
||||
core.error("No find to continue from")
|
||||
else
|
||||
local sl1, sc1, sl2, sc2 = doc():get_selection(true)
|
||||
local line1, col1, line2, col2 = last_fn(doc(), sl1, sc2, last_text, case_sensitive, find_regex, false)
|
||||
local sl1, sc1, sl2, sc2 = dv.doc:get_selection(true)
|
||||
local line1, col1, line2, col2 = last_fn(dv.doc, sl1, sc2, last_text, case_sensitive, find_regex, false)
|
||||
if line1 then
|
||||
doc():set_selection(line2, col2, line1, col1)
|
||||
last_view:scroll_to_line(line2, true)
|
||||
dv.doc:set_selection(line2, col2, line1, col1)
|
||||
dv:scroll_to_line(line2, true)
|
||||
else
|
||||
core.error("Couldn't find %q", last_text)
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
["find-replace:previous-find"] = function()
|
||||
["find-replace:previous-find"] = function(dv)
|
||||
if not last_fn then
|
||||
core.error("No find to continue from")
|
||||
else
|
||||
local sl1, sc1, sl2, sc2 = doc():get_selection(true)
|
||||
local line1, col1, line2, col2 = last_fn(doc(), sl1, sc1, last_text, case_sensitive, find_regex, true)
|
||||
local sl1, sc1, sl2, sc2 = dv.doc:get_selection(true)
|
||||
local line1, col1, line2, col2 = last_fn(dv.doc, sl1, sc1, last_text, case_sensitive, find_regex, true)
|
||||
if line1 then
|
||||
doc():set_selection(line2, col2, line1, col1)
|
||||
last_view:scroll_to_line(line2, true)
|
||||
dv.doc:set_selection(line2, col2, line1, col1)
|
||||
dv:scroll_to_line(line2, true)
|
||||
else
|
||||
core.error("Couldn't find %q", last_text)
|
||||
end
|
||||
|
|
|
@ -4,6 +4,7 @@ local DocView = require "core.docview"
|
|||
local command = require "core.command"
|
||||
local common = require "core.common"
|
||||
local config = require "core.config"
|
||||
local Node = require "core.node"
|
||||
|
||||
|
||||
local t = {
|
||||
|
@ -29,20 +30,6 @@ local t = {
|
|||
core.confirm_close_docs(docs, core.root_view.close_all_docviews, core.root_view, true)
|
||||
end,
|
||||
|
||||
["root:switch-to-previous-tab"] = function(node)
|
||||
local idx = node:get_view_idx(core.active_view)
|
||||
idx = idx - 1
|
||||
if idx < 1 then idx = #node.views end
|
||||
node:set_active_view(node.views[idx])
|
||||
end,
|
||||
|
||||
["root:switch-to-next-tab"] = function(node)
|
||||
local idx = node:get_view_idx(core.active_view)
|
||||
idx = idx + 1
|
||||
if idx > #node.views then idx = 1 end
|
||||
node:set_active_view(node.views[idx])
|
||||
end,
|
||||
|
||||
["root:move-tab-left"] = function(node)
|
||||
local idx = node:get_view_idx(core.active_view)
|
||||
if idx > 1 then
|
||||
|
@ -117,7 +104,7 @@ end, t)
|
|||
|
||||
command.add(nil, {
|
||||
["root:scroll"] = function(delta)
|
||||
local view = (core.root_view.overlapping_node and core.root_view.overlapping_node.active_view) or core.active_view
|
||||
local view = core.root_view.overlapping_view or core.active_view
|
||||
if view and view.scrollable then
|
||||
view.scroll.to.y = view.scroll.to.y + delta * -config.mouse_wheel_scroll
|
||||
return true
|
||||
|
@ -125,7 +112,7 @@ command.add(nil, {
|
|||
return false
|
||||
end,
|
||||
["root:horizontal-scroll"] = function(delta)
|
||||
local view = (core.root_view.overlapping_node and core.root_view.overlapping_node.active_view) or core.active_view
|
||||
local view = core.root_view.overlapping_view or core.active_view
|
||||
if view and view.scrollable then
|
||||
view.scroll.to.x = view.scroll.to.x + delta * -config.mouse_wheel_scroll
|
||||
return true
|
||||
|
@ -133,3 +120,74 @@ command.add(nil, {
|
|||
return false
|
||||
end
|
||||
})
|
||||
|
||||
command.add(function(node)
|
||||
if not Node:is_extended_by(node) then node = nil end
|
||||
-- No node was specified, use the active one
|
||||
node = node or core.root_view:get_active_node()
|
||||
if not node then return false end
|
||||
return true, node
|
||||
end,
|
||||
{
|
||||
["root:switch-to-previous-tab"] = function(node)
|
||||
local idx = node:get_view_idx(node.active_view)
|
||||
idx = idx - 1
|
||||
if idx < 1 then idx = #node.views end
|
||||
node:set_active_view(node.views[idx])
|
||||
end,
|
||||
|
||||
["root:switch-to-next-tab"] = function(node)
|
||||
local idx = node:get_view_idx(node.active_view)
|
||||
idx = idx + 1
|
||||
if idx > #node.views then idx = 1 end
|
||||
node:set_active_view(node.views[idx])
|
||||
end,
|
||||
|
||||
["root:scroll-tabs-backward"] = function(node)
|
||||
node:scroll_tabs(1)
|
||||
end,
|
||||
|
||||
["root:scroll-tabs-forward"] = function(node)
|
||||
node:scroll_tabs(2)
|
||||
end
|
||||
}
|
||||
)
|
||||
|
||||
command.add(function()
|
||||
local node = core.root_view.root_node:get_child_overlapping_point(core.root_view.mouse.x, core.root_view.mouse.y)
|
||||
if not node then return false end
|
||||
return (node.hovered_tab or node.hovered_scroll_button > 0) and true, node
|
||||
end,
|
||||
{
|
||||
["root:switch-to-hovered-previous-tab"] = function(node)
|
||||
command.perform("root:switch-to-previous-tab", node)
|
||||
end,
|
||||
|
||||
["root:switch-to-hovered-next-tab"] = function(node)
|
||||
command.perform("root:switch-to-next-tab", node)
|
||||
end,
|
||||
|
||||
["root:scroll-hovered-tabs-backward"] = function(node)
|
||||
command.perform("root:scroll-tabs-backward", node)
|
||||
end,
|
||||
|
||||
["root:scroll-hovered-tabs-forward"] = function(node)
|
||||
command.perform("root:scroll-tabs-forward", node)
|
||||
end
|
||||
}
|
||||
)
|
||||
|
||||
-- double clicking the tab bar, or on the emptyview should open a new doc
|
||||
command.add(function(x, y)
|
||||
local node = x and y and core.root_view.root_node:get_child_overlapping_point(x, y)
|
||||
return node and node:is_in_tab_area(x, y)
|
||||
end, {
|
||||
["tabbar:new-doc"] = function()
|
||||
command.perform("core:new-doc")
|
||||
end
|
||||
})
|
||||
command.add("core.emptyview", {
|
||||
["emptyview:new-doc"] = function()
|
||||
command.perform("core:new-doc")
|
||||
end
|
||||
})
|
||||
|
|
|
@ -84,6 +84,11 @@ function CommandView:get_line_screen_position(line, col)
|
|||
end
|
||||
|
||||
|
||||
function CommandView:supports_text_input()
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
function CommandView:get_scrollable_size()
|
||||
return 0
|
||||
end
|
||||
|
|
|
@ -1,22 +1,41 @@
|
|||
local common = {}
|
||||
|
||||
|
||||
---Checks if the byte at offset is a UTF-8 continuation byte.
|
||||
---
|
||||
---UTF-8 encodes code points in 1 to 4 bytes.
|
||||
---For a multi-byte sequence, each byte following the start byte is a continuation byte.
|
||||
---@param s string
|
||||
---@param offset? integer The offset of the string to start searching. Defaults to 1.
|
||||
---@return boolean
|
||||
function common.is_utf8_cont(s, offset)
|
||||
local byte = s:byte(offset or 1)
|
||||
return byte >= 0x80 and byte < 0xc0
|
||||
end
|
||||
|
||||
|
||||
---Returns an iterator that yields a UTF-8 character on each iteration.
|
||||
---@param text string
|
||||
---@return fun(): string
|
||||
function common.utf8_chars(text)
|
||||
return text:gmatch("[\0-\x7f\xc2-\xf4][\x80-\xbf]*")
|
||||
end
|
||||
|
||||
|
||||
---Clamps the number n between lo and hi.
|
||||
---@param n number
|
||||
---@param lo number
|
||||
---@param hi number
|
||||
---@return number
|
||||
function common.clamp(n, lo, hi)
|
||||
return math.max(math.min(n, hi), lo)
|
||||
end
|
||||
|
||||
|
||||
---Returns a new table containing the contents of b merged into a.
|
||||
---@param a table|nil
|
||||
---@param b table?
|
||||
---@return table
|
||||
function common.merge(a, b)
|
||||
a = type(a) == "table" and a or {}
|
||||
local t = {}
|
||||
|
@ -32,11 +51,19 @@ function common.merge(a, b)
|
|||
end
|
||||
|
||||
|
||||
---Returns the value of a number rounded to the nearest integer.
|
||||
---@param n number
|
||||
---@return number
|
||||
function common.round(n)
|
||||
return n >= 0 and math.floor(n + 0.5) or math.ceil(n - 0.5)
|
||||
end
|
||||
|
||||
|
||||
---Returns the first index where a subtable in tbl has prop set.
|
||||
---If none is found, nil is returned.
|
||||
---@param tbl table
|
||||
---@param prop any
|
||||
---@return number|nil
|
||||
function common.find_index(tbl, prop)
|
||||
for i, o in ipairs(tbl) do
|
||||
if o[prop] then return i end
|
||||
|
@ -44,6 +71,16 @@ function common.find_index(tbl, prop)
|
|||
end
|
||||
|
||||
|
||||
---Returns a value between a and b on a linear scale, based on the
|
||||
---interpolation point t.
|
||||
---
|
||||
---If a and b are tables, a table containing the result for all the
|
||||
---elements in a and b is returned.
|
||||
---@param a number
|
||||
---@param b number
|
||||
---@param t number
|
||||
---@return number
|
||||
---@overload fun(a: table, b: table, t: number): table
|
||||
function common.lerp(a, b, t)
|
||||
if type(a) ~= "table" then
|
||||
return a + (b - a) * t
|
||||
|
@ -56,11 +93,29 @@ function common.lerp(a, b, t)
|
|||
end
|
||||
|
||||
|
||||
---Returns the euclidean distance between two points.
|
||||
---@param x1 number
|
||||
---@param y1 number
|
||||
---@param x2 number
|
||||
---@param y2 number
|
||||
---@return number
|
||||
function common.distance(x1, y1, x2, y2)
|
||||
return math.sqrt(((x2-x1) ^ 2)+((y2-y1) ^ 2))
|
||||
end
|
||||
|
||||
|
||||
---Parses a CSS color string.
|
||||
---
|
||||
---Only these formats are supported:
|
||||
---* `rgb(r, g, b)`
|
||||
---* `rgba(r, g, b, a)`
|
||||
---* `#rrggbbaa`
|
||||
---* `#rrggbb`
|
||||
---@param str string
|
||||
---@return number r
|
||||
---@return number g
|
||||
---@return number b
|
||||
---@return number a
|
||||
function common.color(str)
|
||||
local r, g, b, a = str:match("^#(%x%x)(%x%x)(%x%x)(%x?%x?)$")
|
||||
if r then
|
||||
|
@ -81,26 +136,21 @@ function common.color(str)
|
|||
end
|
||||
|
||||
|
||||
---Splices a numerically indexed table.
|
||||
---This function mutates the original table.
|
||||
---@param t any[]
|
||||
---@param at number Index at which to start splicing.
|
||||
---@param remove number Number of elements to remove.
|
||||
---@param insert? any[] A table containing elements to insert after splicing.
|
||||
function common.splice(t, at, remove, insert)
|
||||
assert(remove >= 0, "bad argument #3 to 'splice' (non-negative value expected)")
|
||||
insert = insert or {}
|
||||
local offset = #insert - remove
|
||||
local old_len = #t
|
||||
if offset < 0 then
|
||||
for i = at - offset, old_len - offset do
|
||||
t[i + offset] = t[i]
|
||||
end
|
||||
elseif offset > 0 then
|
||||
for i = old_len, at, -1 do
|
||||
t[i + offset] = t[i]
|
||||
end
|
||||
end
|
||||
for i, item in ipairs(insert) do
|
||||
t[at + i - 1] = item
|
||||
end
|
||||
local len = #insert
|
||||
if remove ~= len then table.move(t, at + remove, #t + remove, at + len) end
|
||||
table.move(insert, 1, len, at, t)
|
||||
end
|
||||
|
||||
|
||||
|
||||
local function compare_score(a, b)
|
||||
return a.score > b.score
|
||||
end
|
||||
|
@ -121,6 +171,16 @@ local function fuzzy_match_items(items, needle, files)
|
|||
end
|
||||
|
||||
|
||||
---Performs fuzzy matching.
|
||||
---
|
||||
---If the haystack is a string, a score ranging from 0 to 1 is returned. </br>
|
||||
---If the haystack is a table, a table containing the haystack sorted in ascending
|
||||
---order of similarity is returned.
|
||||
---@param haystack string
|
||||
---@param needle string
|
||||
---@param files boolean If true, the matching process will be performed in reverse to better match paths.
|
||||
---@return number
|
||||
---@overload fun(haystack: string[], needle: string, files: boolean): string[]
|
||||
function common.fuzzy_match(haystack, needle, files)
|
||||
if type(haystack) == "table" then
|
||||
return fuzzy_match_items(haystack, needle, files)
|
||||
|
@ -129,6 +189,14 @@ function common.fuzzy_match(haystack, needle, files)
|
|||
end
|
||||
|
||||
|
||||
---Performs fuzzy matching and returns recently used strings if needed.
|
||||
---
|
||||
---If the needle is empty, then a list of recently used strings
|
||||
---are added to the result, followed by strings from the haystack.
|
||||
---@param haystack string[]
|
||||
---@param recents string[]
|
||||
---@param needle string
|
||||
---@return string[]
|
||||
function common.fuzzy_match_with_recents(haystack, recents, needle)
|
||||
if needle == "" then
|
||||
local recents_ext = {}
|
||||
|
@ -147,6 +215,13 @@ function common.fuzzy_match_with_recents(haystack, recents, needle)
|
|||
end
|
||||
|
||||
|
||||
---Returns a list of paths that are relative to the input path.
|
||||
---
|
||||
---If a root directory is specified, the function returns paths
|
||||
---that are relative to the root directory.
|
||||
---@param text string The input path.
|
||||
---@param root? string The root directory.
|
||||
---@return string[]
|
||||
function common.path_suggest(text, root)
|
||||
if root and root:sub(-1) ~= PATHSEP then
|
||||
root = root .. PATHSEP
|
||||
|
@ -200,6 +275,9 @@ function common.path_suggest(text, root)
|
|||
end
|
||||
|
||||
|
||||
---Returns a list of directories that are related to a path.
|
||||
---@param text string The input path.
|
||||
---@return string[]
|
||||
function common.dir_path_suggest(text)
|
||||
local path, name = text:match("^(.-)([^/\\]*)$")
|
||||
local files = system.list_dir(path == "" and "." or path) or {}
|
||||
|
@ -215,6 +293,10 @@ function common.dir_path_suggest(text)
|
|||
end
|
||||
|
||||
|
||||
---Filters a list of paths to find those that are related to the input path.
|
||||
---@param text string The input path.
|
||||
---@param dir_list string[] A list of paths to filter.
|
||||
---@return string[]
|
||||
function common.dir_list_suggest(text, dir_list)
|
||||
local path, name = text:match("^(.-)([^/\\]*)$")
|
||||
local res = {}
|
||||
|
@ -227,6 +309,15 @@ function common.dir_list_suggest(text, dir_list)
|
|||
end
|
||||
|
||||
|
||||
---Matches a string against a list of patterns.
|
||||
---
|
||||
---If a match was found, its start and end index is returned.
|
||||
---Otherwise, false is returned.
|
||||
---@param text string
|
||||
---@param pattern string|string[]
|
||||
---@param ... any Other options for string.find().
|
||||
---@return number|boolean start_index
|
||||
---@return number|nil end_index
|
||||
function common.match_pattern(text, pattern, ...)
|
||||
if type(pattern) == "string" then
|
||||
return text:find(pattern, ...)
|
||||
|
@ -239,8 +330,24 @@ function common.match_pattern(text, pattern, ...)
|
|||
end
|
||||
|
||||
|
||||
---Draws text onto the window.
|
||||
---The function returns the X and Y coordinates of the bottom-right
|
||||
---corner of the text.
|
||||
---@param font renderer.font
|
||||
---@param color renderer.color
|
||||
---@param text string
|
||||
---@param align string
|
||||
---| '"left"' # Align text to the left of the bounding box
|
||||
---| '"right"' # Align text to the right of the bounding box
|
||||
---| '"center"' # Center text in the bounding box
|
||||
---@param x number
|
||||
---@param y number
|
||||
---@param w number
|
||||
---@param h number
|
||||
---@return number x_advance
|
||||
---@return number y_advance
|
||||
function common.draw_text(font, color, text, align, x,y,w,h)
|
||||
local tw, th = font:get_width(text), font:get_height(text)
|
||||
local tw, th = font:get_width(text), font:get_height()
|
||||
if align == "center" then
|
||||
x = x + (w - tw) / 2
|
||||
elseif align == "right" then
|
||||
|
@ -251,6 +358,16 @@ function common.draw_text(font, color, text, align, x,y,w,h)
|
|||
end
|
||||
|
||||
|
||||
---Prints the execution time of a function.
|
||||
---
|
||||
---The execution time and percentage of frame time
|
||||
---for the function is printed to standard output. </br>
|
||||
---The frame rate is always assumed to be 60 FPS, thus
|
||||
---a value of 100% would mean that the benchmark took
|
||||
---1/60 of a second to execute.
|
||||
---@param name string
|
||||
---@param fn fun(...: any): any
|
||||
---@return any # The result returned by the function
|
||||
function common.bench(name, fn, ...)
|
||||
local start = system.get_time()
|
||||
local res = fn(...)
|
||||
|
@ -296,14 +413,28 @@ local function serialize(val, pretty, indent_str, escape, sort, limit, level)
|
|||
return tostring(val)
|
||||
end
|
||||
|
||||
-- Serialize `val` into a parsable string.
|
||||
-- Available options
|
||||
-- * pretty: enable pretty printing
|
||||
-- * indent_str: indent to use (" " by default)
|
||||
-- * escape: use normal escape characters instead of the ones used by string.format("%q", ...)
|
||||
-- * sort: sort the keys inside tables
|
||||
-- * limit: limit how deep to serialize
|
||||
-- * initial_indent: the initial indentation level
|
||||
|
||||
---@class common.serializeoptions
|
||||
---@field pretty boolean Enables pretty printing.
|
||||
---@field indent_str string The indentation character to use. Defaults to `" "`.
|
||||
---@field escape boolean Uses normal escape characters ("\n") instead of decimal escape sequences ("\10").
|
||||
---@field limit number Limits the depth when serializing nested tables. Defaults to `math.huge`.
|
||||
---@field sort boolean Sorts the output if it is a sortable table.
|
||||
---@field initial_indent number The initial indentation level. Defaults to 0.
|
||||
|
||||
---Serializes a value into a Lua string that is loadable with load().
|
||||
---
|
||||
---Only these basic types are supported:
|
||||
---* nil
|
||||
---* boolean
|
||||
---* number (except very large numbers and special constants, e.g. `math.huge`, `inf` and `nan`)
|
||||
---* integer
|
||||
---* string
|
||||
---* table
|
||||
---
|
||||
---@param val any
|
||||
---@param opts? common.serializeoptions
|
||||
---@return string
|
||||
function common.serialize(val, opts)
|
||||
opts = opts or {}
|
||||
local indent_str = opts.indent_str or " "
|
||||
|
@ -315,6 +446,9 @@ function common.serialize(val, opts)
|
|||
end
|
||||
|
||||
|
||||
---Returns the last portion of a path.
|
||||
---@param path string
|
||||
---@return string
|
||||
function common.basename(path)
|
||||
-- a path should never end by / or \ except if it is '/' (unix root) or
|
||||
-- 'X:\' (windows drive)
|
||||
|
@ -322,12 +456,18 @@ function common.basename(path)
|
|||
end
|
||||
|
||||
|
||||
-- can return nil if there is no directory part in the path
|
||||
---Returns the directory name of a path.
|
||||
---If the path doesn't have a directory, this function may return nil.
|
||||
---@param path string
|
||||
---@return string|nil
|
||||
function common.dirname(path)
|
||||
return path:match("(.+)[\\/][^\\/]+$")
|
||||
end
|
||||
|
||||
|
||||
---Returns a path where the user's home directory is replaced by `"~"`.
|
||||
---@param text string
|
||||
---@return string
|
||||
function common.home_encode(text)
|
||||
if HOME and string.find(text, HOME, 1, true) == 1 then
|
||||
local dir_pos = #HOME + 1
|
||||
|
@ -341,6 +481,9 @@ function common.home_encode(text)
|
|||
end
|
||||
|
||||
|
||||
---Returns a list of paths where the user's home directory is replaced by `"~"`.
|
||||
---@param paths string[] A list of paths to encode
|
||||
---@return string[]
|
||||
function common.home_encode_list(paths)
|
||||
local t = {}
|
||||
for i = 1, #paths do
|
||||
|
@ -350,6 +493,10 @@ function common.home_encode_list(paths)
|
|||
end
|
||||
|
||||
|
||||
---Expands the `"~"` prefix in a path into the user's home directory.
|
||||
---This function is not guaranteed to return an absolute path.
|
||||
---@param text string
|
||||
---@return string
|
||||
function common.home_expand(text)
|
||||
return HOME and text:gsub("^~", HOME) or text
|
||||
end
|
||||
|
@ -367,12 +514,13 @@ local function split_on_slash(s, sep_pattern)
|
|||
end
|
||||
|
||||
|
||||
-- The filename argument given to the function is supposed to
|
||||
-- come from system.absolute_path and as such should be an
|
||||
-- absolute path without . or .. elements.
|
||||
-- This function exists because on Windows the drive letter returned
|
||||
-- by system.absolute_path is sometimes with a lower case and sometimes
|
||||
-- with an upper case so we normalize to upper case.
|
||||
---Normalizes the drive letter in a Windows path to uppercase.
|
||||
---This function expects an absolute path, e.g. a path from `system.absolute_path`.
|
||||
---
|
||||
---This function is needed because the path returned by `system.absolute_path`
|
||||
---may contain drive letters in upper or lowercase.
|
||||
---@param filename string|nil The input path.
|
||||
---@return string|nil
|
||||
function common.normalize_volume(filename)
|
||||
if not filename then return end
|
||||
if PATHSEP == '\\' then
|
||||
|
@ -385,6 +533,13 @@ function common.normalize_volume(filename)
|
|||
end
|
||||
|
||||
|
||||
---Normalizes a path into the same format across platforms.
|
||||
---
|
||||
---On Windows, all drive letters are converted to uppercase.
|
||||
---UNC paths with drive letters are converted back to ordinary Windows paths.
|
||||
---All path separators (`"/"`, `"\\"`) are converted to platform-specific ones.
|
||||
---@param filename string|nil
|
||||
---@return string|nil
|
||||
function common.normalize_path(filename)
|
||||
if not filename then return end
|
||||
local volume
|
||||
|
@ -425,16 +580,27 @@ function common.normalize_path(filename)
|
|||
end
|
||||
|
||||
|
||||
---Checks whether a path is absolute or relative.
|
||||
---@param path string
|
||||
---@return boolean
|
||||
function common.is_absolute_path(path)
|
||||
return path:sub(1, 1) == PATHSEP or path:match("^(%a):\\")
|
||||
end
|
||||
|
||||
|
||||
---Checks whether a path belongs to a parent directory.
|
||||
---@param filename string The path to check.
|
||||
---@param path string The parent path.
|
||||
---@return boolean
|
||||
function common.path_belongs_to(filename, path)
|
||||
return string.find(filename, path .. PATHSEP, 1, true) == 1
|
||||
end
|
||||
|
||||
|
||||
---Checks whether a path is relative to another path.
|
||||
---@param ref_dir string The path to check against.
|
||||
---@param dir string The input path.
|
||||
---@return boolean
|
||||
function common.relative_path(ref_dir, dir)
|
||||
local drive_pattern = "^(%a):\\"
|
||||
local drive, ref_drive = dir:match(drive_pattern), ref_dir:match(drive_pattern)
|
||||
|
@ -460,6 +626,11 @@ function common.relative_path(ref_dir, dir)
|
|||
end
|
||||
|
||||
|
||||
---Creates a directory recursively if necessary.
|
||||
---@param path string
|
||||
---@return boolean success
|
||||
---@return string|nil error
|
||||
---@return string|nil path The path where an error occured.
|
||||
function common.mkdirp(path)
|
||||
local stat = system.get_file_info(path)
|
||||
if stat and stat.type then
|
||||
|
@ -482,6 +653,13 @@ function common.mkdirp(path)
|
|||
return true
|
||||
end
|
||||
|
||||
|
||||
---Removes a path.
|
||||
---@param path string
|
||||
---@param recursively boolean If true, the function will attempt to remove everything in the specified path.
|
||||
---@return boolean success
|
||||
---@return string|nil error
|
||||
---@return string|nil path The path where the error occured.
|
||||
function common.rm(path, recursively)
|
||||
local stat = system.get_file_info(path)
|
||||
if not stat or (stat.type ~= "file" and stat.type ~= "dir") then
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
local common = require "core.common"
|
||||
|
||||
local config = {}
|
||||
|
||||
config.fps = 60
|
||||
|
@ -55,18 +57,49 @@ config.max_clicks = 3
|
|||
-- set as true to be able to test non supported plugins
|
||||
config.skip_plugins_version = false
|
||||
|
||||
-- holds the plugins real config table
|
||||
local plugins_config = {}
|
||||
|
||||
-- virtual representation of plugins config table
|
||||
config.plugins = {}
|
||||
-- Allow you to set plugin configs even if we haven't seen the plugin before.
|
||||
|
||||
-- allows virtual access to the plugins config table
|
||||
setmetatable(config.plugins, {
|
||||
__index = function(t, k)
|
||||
local v = rawget(t, k)
|
||||
if v == true or v == nil then v = {} rawset(t, k, v) end
|
||||
return v
|
||||
__index = function(_, k)
|
||||
if not plugins_config[k] then
|
||||
plugins_config[k] = { enabled = true, config = {} }
|
||||
end
|
||||
if plugins_config[k].enabled ~= false then
|
||||
return plugins_config[k].config
|
||||
end
|
||||
return false
|
||||
end,
|
||||
__newindex = function(_, k, v)
|
||||
if not plugins_config[k] then
|
||||
plugins_config[k] = { enabled = nil, config = {} }
|
||||
end
|
||||
if v == false and package.loaded["plugins."..k] then
|
||||
local core = require "core"
|
||||
core.warn("[%s] is already enabled, restart the editor for the change to take effect", k)
|
||||
return
|
||||
elseif plugins_config[k].enabled == false and v ~= false then
|
||||
plugins_config[k].enabled = true
|
||||
end
|
||||
if v == false then
|
||||
plugins_config[k].enabled = false
|
||||
elseif type(v) == "table" then
|
||||
plugins_config[k].enabled = true
|
||||
plugins_config[k].config = common.merge(plugins_config[k].config, v)
|
||||
end
|
||||
end,
|
||||
__pairs = function()
|
||||
return coroutine.wrap(function()
|
||||
for name, status in pairs(plugins_config) do
|
||||
coroutine.yield(name, status.config)
|
||||
end
|
||||
end)
|
||||
end
|
||||
})
|
||||
|
||||
-- Disable these plugins by default.
|
||||
config.plugins.trimwhitespace = false
|
||||
config.plugins.drawwhitespace = false
|
||||
|
||||
return config
|
||||
|
|
|
@ -19,27 +19,36 @@ function Highlighter:start()
|
|||
if self.running then return end
|
||||
self.running = true
|
||||
core.add_thread(function()
|
||||
while self.first_invalid_line < self.max_wanted_line do
|
||||
while self.first_invalid_line <= self.max_wanted_line do
|
||||
local max = math.min(self.first_invalid_line + 40, self.max_wanted_line)
|
||||
local retokenized_from
|
||||
for i = self.first_invalid_line, max do
|
||||
local state = (i > 1) and self.lines[i - 1].state
|
||||
local line = self.lines[i]
|
||||
if not (line and line.init_state == state and line.text == self.doc.lines[i]) then
|
||||
if line and line.resume and (line.init_state ~= state or line.text ~= self.doc.lines[i]) then
|
||||
-- Reset the progress if no longer valid
|
||||
line.resume = nil
|
||||
end
|
||||
if not (line and line.init_state == state and line.text == self.doc.lines[i] and not line.resume) then
|
||||
retokenized_from = retokenized_from or i
|
||||
self.lines[i] = self:tokenize_line(i, state)
|
||||
self.lines[i] = self:tokenize_line(i, state, line and line.resume)
|
||||
if self.lines[i].resume then
|
||||
self.first_invalid_line = i
|
||||
goto yield
|
||||
end
|
||||
elseif retokenized_from then
|
||||
self:update_notify(retokenized_from, i - retokenized_from - 1)
|
||||
retokenized_from = nil
|
||||
end
|
||||
end
|
||||
|
||||
self.first_invalid_line = max + 1
|
||||
::yield::
|
||||
if retokenized_from then
|
||||
self:update_notify(retokenized_from, max - retokenized_from)
|
||||
end
|
||||
|
||||
self.first_invalid_line = max + 1
|
||||
core.redraw = true
|
||||
coroutine.yield()
|
||||
coroutine.yield(0)
|
||||
end
|
||||
self.max_wanted_line = 0
|
||||
self.running = false
|
||||
|
@ -48,7 +57,7 @@ end
|
|||
|
||||
local function set_max_wanted_lines(self, amount)
|
||||
self.max_wanted_line = amount
|
||||
if self.first_invalid_line < self.max_wanted_line then
|
||||
if self.first_invalid_line <= self.max_wanted_line then
|
||||
self:start()
|
||||
end
|
||||
end
|
||||
|
@ -91,11 +100,11 @@ function Highlighter:update_notify(line, n)
|
|||
end
|
||||
|
||||
|
||||
function Highlighter:tokenize_line(idx, state)
|
||||
function Highlighter:tokenize_line(idx, state, resume)
|
||||
local res = {}
|
||||
res.init_state = state
|
||||
res.text = self.doc.lines[idx]
|
||||
res.tokens, res.state = tokenizer.tokenize(self.doc.syntax, res.text, state)
|
||||
res.tokens, res.state, res.resume = tokenizer.tokenize(self.doc.syntax, res.text, state, resume)
|
||||
return res
|
||||
end
|
||||
|
||||
|
|
|
@ -44,7 +44,12 @@ end
|
|||
|
||||
function Doc:reset_syntax()
|
||||
local header = self:get_text(1, 1, self:position_offset(1, 1, 128))
|
||||
local syn = syntax.get(self.filename or "", header)
|
||||
local path = self.abs_filename
|
||||
if not path and self.filename then
|
||||
path = core.project_dir .. PATHSEP .. self.filename
|
||||
end
|
||||
if path then path = common.normalize_path(path) end
|
||||
local syn = syntax.get(path, header)
|
||||
if self.syntax ~= syn then
|
||||
self.syntax = syn
|
||||
self.highlighter:soft_reset()
|
||||
|
@ -276,9 +281,13 @@ end
|
|||
-- End of cursor seciton.
|
||||
|
||||
function Doc:sanitize_position(line, col)
|
||||
line = common.clamp(line, 1, #self.lines)
|
||||
col = common.clamp(col, 1, #self.lines[line])
|
||||
return line, col
|
||||
local nlines = #self.lines
|
||||
if line > nlines then
|
||||
return nlines, #self.lines[nlines]
|
||||
elseif line < 1 then
|
||||
return 1, 1
|
||||
end
|
||||
return line, common.clamp(col, 1, #self.lines[line])
|
||||
end
|
||||
|
||||
|
||||
|
@ -427,19 +436,51 @@ function Doc:raw_remove(line1, col1, line2, col2, undo_stack, time)
|
|||
local before = self.lines[line1]:sub(1, col1 - 1)
|
||||
local after = self.lines[line2]:sub(col2)
|
||||
|
||||
-- splice line into line array
|
||||
common.splice(self.lines, line1, line2 - line1 + 1, { before .. after })
|
||||
|
||||
-- move all cursors back if they share a line with the removed text
|
||||
for idx, cline1, ccol1, cline2, ccol2 in self:get_selections(true, true) do
|
||||
if cline1 < line2 then break end
|
||||
local line_removal = line2 - line1
|
||||
local column_removal = line2 == cline2 and col2 < ccol1 and (line2 == line1 and col2 - col1 or col2) or 0
|
||||
self:set_selections(idx, cline1 - line_removal, ccol1 - column_removal, cline2 - line_removal, ccol2 - column_removal)
|
||||
local col_removal = col2 - col1
|
||||
|
||||
-- splice line into line array
|
||||
common.splice(self.lines, line1, line_removal + 1, { before .. after })
|
||||
|
||||
local merge = false
|
||||
|
||||
-- keep selections in correct positions: each pair (line, col)
|
||||
-- * remains unchanged if before the deleted text
|
||||
-- * is set to (line1, col1) if in the deleted text
|
||||
-- * is set to (line1, col - col_removal) if on line2 but out of the deleted text
|
||||
-- * is set to (line - line_removal, col) if after line2
|
||||
for idx, cline1, ccol1, cline2, ccol2 in self:get_selections(true, true) do
|
||||
if cline2 < line1 then break end
|
||||
local l1, c1, l2, c2 = cline1, ccol1, cline2, ccol2
|
||||
|
||||
if cline1 > line1 or (cline1 == line1 and ccol1 > col1) then
|
||||
if cline1 > line2 then
|
||||
l1 = l1 - line_removal
|
||||
else
|
||||
l1 = line1
|
||||
c1 = (cline1 == line2 and ccol1 > col2) and c1 - col_removal or col1
|
||||
end
|
||||
end
|
||||
|
||||
if cline2 > line1 or (cline2 == line1 and ccol2 > col1) then
|
||||
if cline2 > line2 then
|
||||
l2 = l2 - line_removal
|
||||
else
|
||||
l2 = line1
|
||||
c2 = (cline2 == line2 and ccol2 > col2) and c2 - col_removal or col1
|
||||
end
|
||||
end
|
||||
|
||||
if l1 == line1 and c1 == col1 then merge = true end
|
||||
self:set_selections(idx, l1, c1, l2, c2)
|
||||
end
|
||||
|
||||
if merge then
|
||||
self:merge_cursors()
|
||||
end
|
||||
|
||||
-- update highlighter and assure selection is in bounds
|
||||
self.highlighter:remove_notify(line1, line2 - line1)
|
||||
self.highlighter:remove_notify(line1, line_removal)
|
||||
self:sanitize_selection()
|
||||
end
|
||||
|
||||
|
|
|
@ -112,7 +112,8 @@ end
|
|||
|
||||
function DocView:get_scrollable_size()
|
||||
if not config.scroll_past_end then
|
||||
return self:get_line_height() * (#self.doc.lines) + style.padding.y * 2
|
||||
local _, _, _, h_scroll = self.h_scrollbar:get_track_rect()
|
||||
return self:get_line_height() * (#self.doc.lines) + style.padding.y * 2 + h_scroll
|
||||
end
|
||||
return self:get_line_height() * (#self.doc.lines - 1) + self.size.y
|
||||
end
|
||||
|
@ -160,26 +161,38 @@ end
|
|||
function DocView:get_visible_line_range()
|
||||
local x, y, x2, y2 = self:get_content_bounds()
|
||||
local lh = self:get_line_height()
|
||||
local minline = math.max(1, math.floor(y / lh))
|
||||
local maxline = math.min(#self.doc.lines, math.floor(y2 / lh) + 1)
|
||||
local minline = math.max(1, math.floor((y - style.padding.y) / lh) + 1)
|
||||
local maxline = math.min(#self.doc.lines, math.floor((y2 - style.padding.y) / lh) + 1)
|
||||
return minline, maxline
|
||||
end
|
||||
|
||||
|
||||
function DocView:get_col_x_offset(line, col)
|
||||
local default_font = self:get_font()
|
||||
local _, indent_size = self.doc:get_indent_info()
|
||||
default_font:set_tab_size(indent_size)
|
||||
local column = 1
|
||||
local xoffset = 0
|
||||
for _, type, text in self.doc.highlighter:each_token(line) do
|
||||
local font = style.syntax_fonts[type] or default_font
|
||||
if font ~= default_font then font:set_tab_size(indent_size) end
|
||||
local length = #text
|
||||
if column + length <= col then
|
||||
xoffset = xoffset + font:get_width(text)
|
||||
column = column + length
|
||||
if column >= col then
|
||||
return xoffset
|
||||
end
|
||||
else
|
||||
for char in common.utf8_chars(text) do
|
||||
if column == col then
|
||||
if column >= col then
|
||||
return xoffset
|
||||
end
|
||||
xoffset = xoffset + font:get_width(char)
|
||||
column = column + #char
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return xoffset
|
||||
end
|
||||
|
@ -190,8 +203,18 @@ function DocView:get_x_offset_col(line, x)
|
|||
|
||||
local xoffset, last_i, i = 0, 1, 1
|
||||
local default_font = self:get_font()
|
||||
local _, indent_size = self.doc:get_indent_info()
|
||||
default_font:set_tab_size(indent_size)
|
||||
for _, type, text in self.doc.highlighter:each_token(line) do
|
||||
local font = style.syntax_fonts[type] or default_font
|
||||
if font ~= default_font then font:set_tab_size(indent_size) end
|
||||
local width = font:get_width(text)
|
||||
-- Don't take the shortcut if the width matches x,
|
||||
-- because we need last_i which should be calculated using utf-8.
|
||||
if xoffset + width < x then
|
||||
xoffset = xoffset + width
|
||||
i = i + #text
|
||||
else
|
||||
for char in common.utf8_chars(text) do
|
||||
local w = font:get_width(char)
|
||||
if xoffset >= x then
|
||||
|
@ -202,6 +225,7 @@ function DocView:get_x_offset_col(line, x)
|
|||
i = i + #char
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return #line_text
|
||||
end
|
||||
|
@ -221,7 +245,8 @@ function DocView:scroll_to_line(line, ignore_if_visible, instant)
|
|||
if not (ignore_if_visible and line > min and line < max) then
|
||||
local x, y = self:get_line_screen_position(line)
|
||||
local ox, oy = self:get_content_offset()
|
||||
self.scroll.to.y = math.max(0, y - oy - self.size.y / 2)
|
||||
local _, _, _, scroll_h = self.h_scrollbar:get_track_rect()
|
||||
self.scroll.to.y = math.max(0, y - oy - (self.size.y - scroll_h) / 2)
|
||||
if instant then
|
||||
self.scroll.y = self.scroll.to.y
|
||||
end
|
||||
|
@ -229,18 +254,26 @@ function DocView:scroll_to_line(line, ignore_if_visible, instant)
|
|||
end
|
||||
|
||||
|
||||
function DocView:supports_text_input()
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
function DocView:scroll_to_make_visible(line, col)
|
||||
local ox, oy = self:get_content_offset()
|
||||
local _, oy = self:get_content_offset()
|
||||
local _, ly = self:get_line_screen_position(line, col)
|
||||
local lh = self:get_line_height()
|
||||
self.scroll.to.y = common.clamp(self.scroll.to.y, ly - oy - self.size.y + lh * 2, ly - oy - lh)
|
||||
local _, _, _, scroll_h = self.h_scrollbar:get_track_rect()
|
||||
self.scroll.to.y = common.clamp(self.scroll.to.y, ly - oy - self.size.y + scroll_h + lh * 2, ly - oy - lh)
|
||||
local gw = self:get_gutter_width()
|
||||
local xoffset = self:get_col_x_offset(line, col)
|
||||
local xmargin = 3 * self:get_font():get_width(' ')
|
||||
local xsup = xoffset + gw + xmargin
|
||||
local xinf = xoffset - xmargin
|
||||
if xsup > self.scroll.x + self.size.x then
|
||||
self.scroll.to.x = xsup - self.size.x
|
||||
local _, _, scroll_w = self.v_scrollbar:get_track_rect()
|
||||
local size_x = math.max(0, self.size.x - scroll_w)
|
||||
if xsup > self.scroll.x + size_x then
|
||||
self.scroll.to.x = xsup - size_x
|
||||
elseif xinf < self.scroll.x then
|
||||
self.scroll.to.x = math.max(0, xinf)
|
||||
end
|
||||
|
@ -289,7 +322,7 @@ function DocView:mouse_selection(doc, snap_type, line1, col1, line2, col2)
|
|||
line1, col1 = translate.start_of_word(doc, line1, col1)
|
||||
line2, col2 = translate.end_of_word(doc, line2, col2)
|
||||
elseif snap_type == "lines" then
|
||||
col1, col2 = 1, math.huge
|
||||
col1, col2, line2 = 1, 1, line2 + 1
|
||||
end
|
||||
if swap then
|
||||
return line2, col2, line1, col1
|
||||
|
@ -422,6 +455,7 @@ function DocView:draw_line_text(line, x, y)
|
|||
-- do not render newline, fixes issue #1164
|
||||
if tidx == last_token then text = text:sub(1, -2) end
|
||||
tx = renderer.draw_text(font, text, tx, ty, color)
|
||||
if tx > self.position.x + self.size.x then break end
|
||||
end
|
||||
return self:get_line_height()
|
||||
end
|
||||
|
|
|
@ -18,13 +18,13 @@ local Doc
|
|||
local core = {}
|
||||
|
||||
local function load_session()
|
||||
local ok, t = pcall(dofile, USERDIR .. "/session.lua")
|
||||
local ok, t = pcall(dofile, USERDIR .. PATHSEP .. "session.lua")
|
||||
return ok and t or {}
|
||||
end
|
||||
|
||||
|
||||
local function save_session()
|
||||
local fp = io.open(USERDIR .. "/session.lua", "w")
|
||||
local fp = io.open(USERDIR .. PATHSEP .. "session.lua", "w")
|
||||
if fp then
|
||||
fp:write("return {recents=", common.serialize(core.recent_projects),
|
||||
", window=", common.serialize(table.pack(system.get_window_size())),
|
||||
|
@ -305,7 +305,7 @@ function core.add_project_directory(path)
|
|||
end
|
||||
end
|
||||
if project_dir_open then
|
||||
coroutine.yield(changed and 0.05 or 0)
|
||||
coroutine.yield(changed and 0 or 0.05)
|
||||
else
|
||||
return
|
||||
end
|
||||
|
@ -531,12 +531,9 @@ local style = require "core.style"
|
|||
|
||||
------------------------------ Plugins ----------------------------------------
|
||||
|
||||
-- enable or disable plugin loading setting config entries:
|
||||
-- disable plugin loading setting config entries:
|
||||
|
||||
-- enable plugins.trimwhitespace, otherwise it is disabled by default:
|
||||
-- config.plugins.trimwhitespace = true
|
||||
--
|
||||
-- disable detectindent, otherwise it is enabled by default
|
||||
-- disable plugin detectindent, otherwise it is enabled by default:
|
||||
-- config.plugins.detectindent = false
|
||||
|
||||
---------------------------- Miscellaneous -------------------------------------
|
||||
|
@ -607,7 +604,7 @@ function core.load_user_directory()
|
|||
if not stat_dir then
|
||||
create_user_directory()
|
||||
end
|
||||
local init_filename = USERDIR .. "/init.lua"
|
||||
local init_filename = USERDIR .. PATHSEP .. "init.lua"
|
||||
local stat_file = system.get_file_info(init_filename)
|
||||
if not stat_file then
|
||||
write_user_init_file(init_filename)
|
||||
|
@ -673,6 +670,9 @@ end
|
|||
|
||||
|
||||
function core.init()
|
||||
core.log_items = {}
|
||||
core.log_quiet("Lite XL version %s - mod-version %s", VERSION, MOD_VERSION_STRING)
|
||||
|
||||
command = require "core.command"
|
||||
keymap = require "core.keymap"
|
||||
dirwatch = require "core.dirwatch"
|
||||
|
@ -726,7 +726,6 @@ function core.init()
|
|||
|
||||
core.frame_start = 0
|
||||
core.clip_rect_stack = {{ 0,0,0,0 }}
|
||||
core.log_items = {}
|
||||
core.docs = {}
|
||||
core.cursor_clipboard = {}
|
||||
core.cursor_clipboard_whole_line = {}
|
||||
|
@ -818,21 +817,25 @@ function core.init()
|
|||
|
||||
if #plugins_refuse_list.userdir.plugins > 0 or #plugins_refuse_list.datadir.plugins > 0 then
|
||||
local opt = {
|
||||
{ font = style.font, text = "Exit", default_no = true },
|
||||
{ font = style.font, text = "Continue" , default_yes = true }
|
||||
{ text = "Exit", default_no = true },
|
||||
{ text = "Continue", default_yes = true }
|
||||
}
|
||||
local msg = {}
|
||||
for _, entry in pairs(plugins_refuse_list) do
|
||||
if #entry.plugins > 0 then
|
||||
msg[#msg + 1] = string.format("Plugins from directory \"%s\":\n%s", common.home_encode(entry.dir), table.concat(entry.plugins, "\n"))
|
||||
local msg_list = {}
|
||||
for _, p in pairs(entry.plugins) do
|
||||
table.insert(msg_list, string.format("%s[%s]", p.file, p.version_string))
|
||||
end
|
||||
msg[#msg + 1] = string.format("Plugins from directory \"%s\":\n%s", common.home_encode(entry.dir), table.concat(msg_list, "\n"))
|
||||
end
|
||||
end
|
||||
core.nag_view:show(
|
||||
"Refused Plugins",
|
||||
string.format(
|
||||
"Some plugins are not loaded due to version mismatch.\n\n%s.\n\n" ..
|
||||
"Some plugins are not loaded due to version mismatch. Expected version %s.\n\n%s.\n\n" ..
|
||||
"Please download a recent version from https://github.com/lite-xl/lite-xl-plugins.",
|
||||
table.concat(msg, ".\n\n")),
|
||||
MOD_VERSION_STRING, table.concat(msg, ".\n\n")),
|
||||
opt, function(item)
|
||||
if item.text == "Exit" then os.exit(1) end
|
||||
end)
|
||||
|
@ -860,8 +863,8 @@ function core.confirm_close_docs(docs, close_fn, ...)
|
|||
end
|
||||
local args = {...}
|
||||
local opt = {
|
||||
{ font = style.font, text = "Yes", default_yes = true },
|
||||
{ font = style.font, text = "No" , default_no = true }
|
||||
{ text = "Yes", default_yes = true },
|
||||
{ text = "No", default_no = true }
|
||||
}
|
||||
core.nag_view:show("Unsaved Changes", text, opt, function(item)
|
||||
if item.text == "Yes" then close_fn(table.unpack(args)) end
|
||||
|
@ -921,10 +924,12 @@ function core.restart()
|
|||
end
|
||||
|
||||
|
||||
local mod_version_regex =
|
||||
regex.compile([[--.*mod-version:(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:$|\s)]])
|
||||
local function get_plugin_details(filename)
|
||||
local info = system.get_file_info(filename)
|
||||
if info ~= nil and info.type == "dir" then
|
||||
filename = filename .. "/init.lua"
|
||||
filename = filename .. PATHSEP .. "init.lua"
|
||||
info = system.get_file_info(filename)
|
||||
end
|
||||
if not info or not filename:match("%.lua$") then return false end
|
||||
|
@ -932,17 +937,32 @@ local function get_plugin_details(filename)
|
|||
if not f then return false end
|
||||
local priority = false
|
||||
local version_match = false
|
||||
local major, minor, patch
|
||||
|
||||
for line in f:lines() do
|
||||
if not version_match then
|
||||
local mod_version = line:match('%-%-.*%f[%a]mod%-version%s*:%s*(%d+)')
|
||||
if mod_version then
|
||||
version_match = (mod_version == MOD_VERSION)
|
||||
local _major, _minor, _patch = mod_version_regex:match(line)
|
||||
if _major then
|
||||
_major = tonumber(_major) or 0
|
||||
_minor = tonumber(_minor) or 0
|
||||
_patch = tonumber(_patch) or 0
|
||||
major, minor, patch = _major, _minor, _patch
|
||||
|
||||
version_match = major == MOD_VERSION_MAJOR
|
||||
if version_match then
|
||||
version_match = minor <= MOD_VERSION_MINOR
|
||||
end
|
||||
if version_match then
|
||||
version_match = patch <= MOD_VERSION_PATCH
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not priority then
|
||||
priority = line:match('%-%-.*%f[%a]priority%s*:%s*(%d+)')
|
||||
if priority then priority = tonumber(priority) end
|
||||
end
|
||||
|
||||
if version_match then
|
||||
break
|
||||
end
|
||||
|
@ -950,6 +970,7 @@ local function get_plugin_details(filename)
|
|||
f:close()
|
||||
return true, {
|
||||
version_match = version_match,
|
||||
version = major and {major, minor, patch} or {},
|
||||
priority = priority or 100
|
||||
}
|
||||
end
|
||||
|
@ -963,7 +984,7 @@ function core.load_plugins()
|
|||
}
|
||||
local files, ordered = {}, {}
|
||||
for _, root_dir in ipairs {DATADIR, USERDIR} do
|
||||
local plugin_dir = root_dir .. "/plugins"
|
||||
local plugin_dir = root_dir .. PATHSEP .. "plugins"
|
||||
for _, filename in ipairs(system.list_dir(plugin_dir) or {}) do
|
||||
if not files[filename] then
|
||||
table.insert(
|
||||
|
@ -978,13 +999,15 @@ function core.load_plugins()
|
|||
for _, plugin in ipairs(ordered) do
|
||||
local dir = files[plugin.file]
|
||||
local name = plugin.file:match("(.-)%.lua$") or plugin.file
|
||||
local is_lua_file, details = get_plugin_details(dir .. '/' .. plugin.file)
|
||||
local is_lua_file, details = get_plugin_details(dir .. PATHSEP .. plugin.file)
|
||||
|
||||
plugin.valid = is_lua_file
|
||||
plugin.name = name
|
||||
plugin.dir = dir
|
||||
plugin.priority = details and details.priority or 100
|
||||
plugin.version_match = details and details.version_match or false
|
||||
plugin.version = details and details.version or {}
|
||||
plugin.version_string = #plugin.version > 0 and table.concat(plugin.version, ".") or "unknown"
|
||||
end
|
||||
|
||||
-- sort by priority or name for plugins that have same priority
|
||||
|
@ -1000,27 +1023,35 @@ function core.load_plugins()
|
|||
if plugin.valid then
|
||||
if not config.skip_plugins_version and not plugin.version_match then
|
||||
core.log_quiet(
|
||||
"Version mismatch for plugin %q from %s",
|
||||
"Version mismatch for plugin %q[%s] from %s",
|
||||
plugin.name,
|
||||
plugin.version_string,
|
||||
plugin.dir
|
||||
)
|
||||
local rlist = plugin.dir:find(USERDIR, 1, true) == 1
|
||||
and 'userdir' or 'datadir'
|
||||
local list = refused_list[rlist].plugins
|
||||
table.insert(list, plugin.file)
|
||||
table.insert(list, plugin)
|
||||
elseif config.plugins[plugin.name] ~= false then
|
||||
local start = system.get_time()
|
||||
local ok = core.try(require, "plugins." .. plugin.name)
|
||||
local ok, loaded_plugin = core.try(require, "plugins." .. plugin.name)
|
||||
if ok then
|
||||
local plugin_version = ""
|
||||
if plugin.version_string ~= MOD_VERSION_STRING then
|
||||
plugin_version = "["..plugin.version_string.."]"
|
||||
end
|
||||
core.log_quiet(
|
||||
"Loaded plugin %q from %s in %.1fms",
|
||||
"Loaded plugin %q%s from %s in %.1fms",
|
||||
plugin.name,
|
||||
plugin_version,
|
||||
plugin.dir,
|
||||
(system.get_time() - start) * 1000
|
||||
)
|
||||
end
|
||||
if not ok then
|
||||
no_errors = false
|
||||
elseif config.plugins[plugin.name].onload then
|
||||
core.try(config.plugins[plugin.name].onload, loaded_plugin)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1074,6 +1105,7 @@ function core.set_active_view(view)
|
|||
-- Reset the IME even if the focus didn't change
|
||||
ime.stop()
|
||||
if view ~= core.active_view then
|
||||
system.text_input(view:supports_text_input())
|
||||
if core.active_view and core.active_view.force_focus then
|
||||
core.next_active_view = view
|
||||
return
|
||||
|
@ -1165,8 +1197,10 @@ function core.custom_log(level, show, backtrace, fmt, ...)
|
|||
local text = string.format(fmt, ...)
|
||||
if show then
|
||||
local s = style.log[level]
|
||||
if core.status_view then
|
||||
core.status_view:show_message(s.icon, s.color, text)
|
||||
end
|
||||
end
|
||||
|
||||
local info = debug.getinfo(2, "Sl")
|
||||
local at = string.format("%s:%d", info.short_src, info.currentline)
|
||||
|
@ -1257,6 +1291,12 @@ function core.on_event(type, ...)
|
|||
if not core.root_view:on_mouse_wheel(...) then
|
||||
did_keymap = keymap.on_mouse_wheel(...)
|
||||
end
|
||||
elseif type == "touchpressed" then
|
||||
core.root_view:on_touch_pressed(...)
|
||||
elseif type == "touchreleased" then
|
||||
core.root_view:on_touch_released(...)
|
||||
elseif type == "touchmoved" then
|
||||
core.root_view:on_touch_moved(...)
|
||||
elseif type == "resized" then
|
||||
core.window_mode = system.get_window_mode()
|
||||
elseif type == "minimized" or type == "maximized" or type == "restored" then
|
||||
|
@ -1306,6 +1346,11 @@ function core.step()
|
|||
did_keymap = false
|
||||
elseif type == "mousemoved" then
|
||||
core.try(core.on_event, type, a, b, c, d)
|
||||
elseif type == "enteringforeground" then
|
||||
-- to break our frame refresh in two if we get entering/entered at the same time.
|
||||
-- required to avoid flashing and refresh issues on mobile
|
||||
core.redraw = true
|
||||
break
|
||||
else
|
||||
local _, res = core.try(core.on_event, type, a, b, c, d)
|
||||
did_keymap = res or did_keymap
|
||||
|
@ -1350,7 +1395,7 @@ end
|
|||
local run_threads = coroutine.wrap(function()
|
||||
while true do
|
||||
local max_time = 1 / config.fps - 0.004
|
||||
local need_more_work = false
|
||||
local minimal_time_to_wake = math.huge
|
||||
|
||||
for k, thread in pairs(core.threads) do
|
||||
-- run thread
|
||||
|
@ -1362,50 +1407,66 @@ local run_threads = coroutine.wrap(function()
|
|||
else
|
||||
core.threads[k] = nil
|
||||
end
|
||||
elseif wait then
|
||||
thread.wake = system.get_time() + wait
|
||||
else
|
||||
need_more_work = true
|
||||
wait = wait or (1/30)
|
||||
thread.wake = system.get_time() + wait
|
||||
minimal_time_to_wake = math.min(minimal_time_to_wake, wait)
|
||||
end
|
||||
else
|
||||
minimal_time_to_wake = math.min(minimal_time_to_wake, thread.wake - system.get_time())
|
||||
end
|
||||
|
||||
-- stop running threads if we're about to hit the end of frame
|
||||
if system.get_time() - core.frame_start > max_time then
|
||||
coroutine.yield(true)
|
||||
coroutine.yield(0)
|
||||
end
|
||||
end
|
||||
|
||||
if not need_more_work then coroutine.yield(false) end
|
||||
coroutine.yield(minimal_time_to_wake)
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
function core.run()
|
||||
local idle_iterations = 0
|
||||
local next_step
|
||||
local last_frame_time
|
||||
while true do
|
||||
core.frame_start = system.get_time()
|
||||
local need_more_work = run_threads()
|
||||
local did_redraw = core.step()
|
||||
local time_to_wake = run_threads()
|
||||
local did_redraw = false
|
||||
local force_draw = core.redraw and last_frame_time and core.frame_start - last_frame_time > (1 / config.fps)
|
||||
if force_draw or not next_step or system.get_time() >= next_step then
|
||||
if core.step() then
|
||||
did_redraw = true
|
||||
last_frame_time = core.frame_start
|
||||
end
|
||||
next_step = nil
|
||||
end
|
||||
if core.restart_request or core.quit_request then break end
|
||||
if not did_redraw and not need_more_work then
|
||||
idle_iterations = idle_iterations + 1
|
||||
-- do not wait of events at idle_iterations = 1 to give a chance at core.step to run
|
||||
-- and set "redraw" flag.
|
||||
if idle_iterations > 1 then
|
||||
|
||||
if not did_redraw then
|
||||
if system.window_has_focus() then
|
||||
-- keep running even with no events to make the cursor blinks
|
||||
local t = system.get_time() - core.blink_start
|
||||
local now = system.get_time()
|
||||
if not next_step then -- compute the time until the next blink
|
||||
local t = now - core.blink_start
|
||||
local h = config.blink_period / 2
|
||||
local dt = math.ceil(t / h) * h - t
|
||||
system.wait_event(dt + 1 / config.fps)
|
||||
local cursor_time_to_wake = dt + 1 / config.fps
|
||||
next_step = now + cursor_time_to_wake
|
||||
end
|
||||
if time_to_wake > 0 and system.wait_event(math.min(next_step - now, time_to_wake)) then
|
||||
next_step = nil -- if we've recevied an event, perform a step
|
||||
end
|
||||
else
|
||||
system.wait_event()
|
||||
next_step = nil -- perform a step when we're not in focus if get we an event
|
||||
end
|
||||
end
|
||||
else
|
||||
idle_iterations = 0
|
||||
local elapsed = system.get_time() - core.frame_start
|
||||
system.sleep(math.max(0, 1 / config.fps - elapsed))
|
||||
else -- if we redrew, then make sure we only draw at most FPS/sec
|
||||
local now = system.get_time()
|
||||
local elapsed = now - core.frame_start
|
||||
local next_frame = math.max(0, 1 / config.fps - elapsed)
|
||||
next_step = next_step or (now + next_frame)
|
||||
system.sleep(math.min(next_frame, time_to_wake))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1423,7 +1484,7 @@ end
|
|||
|
||||
function core.on_error(err)
|
||||
-- write error to file
|
||||
local fp = io.open(USERDIR .. "/error.txt", "wb")
|
||||
local fp = io.open(USERDIR .. PATHSEP .. "error.txt", "wb")
|
||||
fp:write("Error: " .. tostring(err) .. "\n")
|
||||
fp:write(debug.traceback("", 4) .. "\n")
|
||||
fp:close()
|
||||
|
@ -1436,4 +1497,15 @@ function core.on_error(err)
|
|||
end
|
||||
|
||||
|
||||
local alerted_deprecations = {}
|
||||
---Show deprecation notice once per `kind`.
|
||||
---
|
||||
---@param kind string
|
||||
function core.deprecation_log(kind)
|
||||
if alerted_deprecations[kind] then return end
|
||||
alerted_deprecations[kind] = true
|
||||
core.warn("Used deprecated functionality [%s]. Check if your plugins are up to date.", kind)
|
||||
end
|
||||
|
||||
|
||||
return core
|
||||
|
|
|
@ -36,6 +36,8 @@ local function keymap_macos(keymap)
|
|||
["wheel"] = "root:scroll",
|
||||
["hwheel"] = "root:horizontal-scroll",
|
||||
["shift+hwheel"] = "root:horizontal-scroll",
|
||||
["wheelup"] = "root:scroll-hovered-tabs-backward",
|
||||
["wheeldown"] = "root:scroll-hovered-tabs-forward",
|
||||
|
||||
["cmd+f"] = "find-replace:find",
|
||||
["cmd+r"] = "find-replace:replace",
|
||||
|
|
|
@ -35,6 +35,23 @@ local modkey_map = modkeys_os.map
|
|||
local modkeys = modkeys_os.keys
|
||||
|
||||
|
||||
---Normalizes a stroke sequence to follow the modkeys table
|
||||
---@param stroke string
|
||||
---@return string
|
||||
local function normalize_stroke(stroke)
|
||||
local stroke_table = {}
|
||||
for modkey in stroke:gmatch("(%w+)%+") do
|
||||
table.insert(stroke_table, modkey)
|
||||
end
|
||||
if not next(stroke_table) then
|
||||
return stroke
|
||||
end
|
||||
table.sort(stroke_table)
|
||||
local new_stroke = table.concat(stroke_table, "+") .. "+"
|
||||
return new_stroke .. stroke:sub(new_stroke:len() + 1)
|
||||
end
|
||||
|
||||
|
||||
---Generates a stroke sequence including currently pressed mod keys.
|
||||
---@param key string
|
||||
---@return string
|
||||
|
@ -45,10 +62,9 @@ local function key_to_stroke(key)
|
|||
stroke = stroke .. mk .. "+"
|
||||
end
|
||||
end
|
||||
return stroke .. key
|
||||
return normalize_stroke(stroke) .. key
|
||||
end
|
||||
|
||||
|
||||
---Remove the given value from an array associated to a key in a table.
|
||||
---@param tbl table<string, string> The table containing the key
|
||||
---@param k string The key containing the array
|
||||
|
@ -74,6 +90,7 @@ end
|
|||
---@param map keymap.map
|
||||
local function remove_duplicates(map)
|
||||
for stroke, commands in pairs(map) do
|
||||
stroke = normalize_stroke(stroke)
|
||||
if type(commands) == "string" or type(commands) == "function" then
|
||||
commands = { commands }
|
||||
end
|
||||
|
@ -96,11 +113,12 @@ local function remove_duplicates(map)
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
---Add bindings by replacing commands that were previously assigned to a shortcut.
|
||||
---@param map keymap.map
|
||||
function keymap.add_direct(map)
|
||||
for stroke, commands in pairs(map) do
|
||||
stroke = normalize_stroke(stroke)
|
||||
|
||||
if type(commands) == "string" or type(commands) == "function" then
|
||||
commands = { commands }
|
||||
end
|
||||
|
@ -128,6 +146,7 @@ function keymap.add(map, overwrite)
|
|||
if macos then
|
||||
stroke = stroke:gsub("%f[%a]ctrl%f[%A]", "cmd")
|
||||
end
|
||||
stroke = normalize_stroke(stroke)
|
||||
if overwrite then
|
||||
if keymap.map[stroke] then
|
||||
for _, cmd in ipairs(keymap.map[stroke]) do
|
||||
|
@ -153,7 +172,7 @@ end
|
|||
---@param shortcut string
|
||||
---@param cmd string
|
||||
function keymap.unbind(shortcut, cmd)
|
||||
remove_only(keymap.map, shortcut, cmd)
|
||||
remove_only(keymap.map, normalize_stroke(shortcut), cmd)
|
||||
remove_only(keymap.reverse_map, cmd, shortcut)
|
||||
end
|
||||
|
||||
|
@ -302,6 +321,8 @@ keymap.add_direct {
|
|||
["wheel"] = "root:scroll",
|
||||
["hwheel"] = "root:horizontal-scroll",
|
||||
["shift+wheel"] = "root:horizontal-scroll",
|
||||
["wheelup"] = "root:scroll-hovered-tabs-backward",
|
||||
["wheeldown"] = "root:scroll-hovered-tabs-forward",
|
||||
|
||||
["ctrl+f"] = "find-replace:find",
|
||||
["ctrl+r"] = "find-replace:replace",
|
||||
|
@ -367,7 +388,7 @@ keymap.add_direct {
|
|||
["shift+1lclick"] = "doc:select-to-cursor",
|
||||
["ctrl+1lclick"] = "doc:split-cursor",
|
||||
["1lclick"] = "doc:set-cursor",
|
||||
["2lclick"] = "doc:set-cursor-word",
|
||||
["2lclick"] = { "doc:set-cursor-word", "emptyview:new-doc", "tabbar:new-doc" },
|
||||
["3lclick"] = "doc:set-cursor-line",
|
||||
["shift+left"] = "doc:select-to-previous-char",
|
||||
["shift+right"] = "doc:select-to-next-char",
|
||||
|
|
|
@ -125,9 +125,19 @@ end
|
|||
function LogView:update()
|
||||
local item = core.log_items[#core.log_items]
|
||||
if self.last_item ~= item then
|
||||
local lh = style.font:get_height() + style.padding.y
|
||||
if 0 < self.scroll.to.y then
|
||||
local index = #core.log_items
|
||||
while index > 1 and self.last_item ~= core.log_items[index] do
|
||||
index = index - 1
|
||||
end
|
||||
local diff_index = #core.log_items - index
|
||||
self.scroll.to.y = self.scroll.to.y + diff_index * lh
|
||||
self.scroll.y = self.scroll.to.y
|
||||
else
|
||||
self.yoffset = -lh
|
||||
end
|
||||
self.last_item = item
|
||||
self.scroll.to.y = 0
|
||||
self.yoffset = -(style.font:get_height() + style.padding.y)
|
||||
end
|
||||
|
||||
local expanding = self.expanding[1]
|
||||
|
|
|
@ -91,7 +91,7 @@ function NagView:each_option()
|
|||
|
||||
for i = #self.options, 1, -1 do
|
||||
opt = self.options[i]
|
||||
bw = opt.font:get_width(opt.text) + 2 * BORDER_WIDTH + style.padding.x
|
||||
bw = style.font:get_width(opt.text) + 2 * BORDER_WIDTH + style.padding.x
|
||||
|
||||
ox = ox - bw - style.padding.x
|
||||
coroutine.yield(i, opt, ox,oy,bw,bh)
|
||||
|
@ -230,7 +230,7 @@ local function draw_nagview_message(self)
|
|||
renderer.draw_rect(lx,ly,uw,UNDERLINE_WIDTH, style.nagbar_text)
|
||||
end
|
||||
|
||||
common.draw_text(opt.font, style.nagbar_text, opt.text, "center", fx,fy,fw,fh)
|
||||
common.draw_text(style.font, style.nagbar_text, opt.text, "center", fx,fy,fw,fh)
|
||||
end
|
||||
|
||||
self:draw_scrollbar()
|
||||
|
@ -245,6 +245,16 @@ function NagView:draw()
|
|||
core.root_view:defer_draw(draw_nagview_message, self)
|
||||
end
|
||||
|
||||
function NagView:on_scale_change(new_scale, old_scale)
|
||||
BORDER_WIDTH = common.round(1 * new_scale)
|
||||
UNDERLINE_WIDTH = common.round(2 * new_scale)
|
||||
UNDERLINE_MARGIN = common.round(1 * new_scale)
|
||||
self.target_height = math.max(
|
||||
self:get_message_height(),
|
||||
self:get_buttons_height()
|
||||
)
|
||||
end
|
||||
|
||||
function NagView:get_message_height()
|
||||
local h = 0
|
||||
for str in string.gmatch(self.message, "(.-)\n") do
|
||||
|
|
|
@ -18,7 +18,6 @@ function Node:new(type)
|
|||
if self.type == "leaf" then
|
||||
self:add_view(EmptyView())
|
||||
end
|
||||
self.hovered = {x = -1, y = -1 }
|
||||
self.hovered_close = 0
|
||||
self.tab_shift = 0
|
||||
self.tab_offset = 1
|
||||
|
@ -33,9 +32,10 @@ function Node:propagate(fn, ...)
|
|||
end
|
||||
|
||||
|
||||
---@deprecated
|
||||
function Node:on_mouse_moved(x, y, ...)
|
||||
core.deprecation_log("Node:on_mouse_moved")
|
||||
if self.type == "leaf" then
|
||||
self.hovered.x, self.hovered.y = x, y
|
||||
self.active_view:on_mouse_moved(x, y, ...)
|
||||
else
|
||||
self:propagate("on_mouse_moved", x, y, ...)
|
||||
|
@ -43,7 +43,9 @@ function Node:on_mouse_moved(x, y, ...)
|
|||
end
|
||||
|
||||
|
||||
---@deprecated
|
||||
function Node:on_mouse_released(...)
|
||||
core.deprecation_log("Node:on_mouse_released")
|
||||
if self.type == "leaf" then
|
||||
self.active_view:on_mouse_released(...)
|
||||
else
|
||||
|
@ -52,7 +54,9 @@ function Node:on_mouse_released(...)
|
|||
end
|
||||
|
||||
|
||||
---@deprecated
|
||||
function Node:on_mouse_left()
|
||||
core.deprecation_log("Node:on_mouse_left")
|
||||
if self.type == "leaf" then
|
||||
self.active_view:on_mouse_left()
|
||||
else
|
||||
|
@ -61,6 +65,17 @@ function Node:on_mouse_left()
|
|||
end
|
||||
|
||||
|
||||
---@deprecated
|
||||
function Node:on_touch_moved(...)
|
||||
core.deprecation_log("Node:on_touch_moved")
|
||||
if self.type == "leaf" then
|
||||
self.active_view:on_touch_moved(...)
|
||||
else
|
||||
self:propagate("on_touch_moved", ...)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function Node:consume(node)
|
||||
for k, _ in pairs(self) do self[k] = nil end
|
||||
for k, v in pairs(node) do self[k] = v end
|
||||
|
@ -301,7 +316,7 @@ function Node:tab_hovered_update(px, py)
|
|||
if px >= cx and px < cx + cw and py >= y and py < y + h and config.tab_close_button then
|
||||
self.hovered_close = tab_index
|
||||
end
|
||||
else
|
||||
elseif #self.views > self:get_visible_tabs_number() then
|
||||
self.hovered_scroll_button = self:get_scroll_button_index(px, py) or 0
|
||||
end
|
||||
end
|
||||
|
@ -319,10 +334,17 @@ function Node:get_child_overlapping_point(x, y)
|
|||
return child:get_child_overlapping_point(x, y)
|
||||
end
|
||||
|
||||
-- returns: total height, text padding, top margin
|
||||
local function get_tab_y_sizes()
|
||||
local height = style.font:get_height()
|
||||
local padding = style.padding.y
|
||||
local margin = style.margin.tab.top
|
||||
return height + (padding * 2) + margin, padding, margin
|
||||
end
|
||||
|
||||
function Node:get_scroll_button_rect(index)
|
||||
local w, pad = get_scroll_button_width()
|
||||
local h = style.font:get_height() + style.padding.y * 2
|
||||
local h = get_tab_y_sizes()
|
||||
local x = self.position.x + (index == 1 and self.size.x - w * 2 or self.size.x - w)
|
||||
return x, self.position.y, w, h, pad
|
||||
end
|
||||
|
@ -333,8 +355,8 @@ function Node:get_tab_rect(idx)
|
|||
local x0 = self.position.x
|
||||
local x1 = x0 + common.clamp(self.tab_width * (idx - 1) - self.tab_shift, 0, maxw)
|
||||
local x2 = x0 + common.clamp(self.tab_width * idx - self.tab_shift, 0, maxw)
|
||||
local h = style.font:get_height() + style.padding.y * 2
|
||||
return x1, self.position.y, x2 - x1, h
|
||||
local h, pad_y, margin_y = get_tab_y_sizes()
|
||||
return x1, self.position.y, x2 - x1, h, margin_y
|
||||
end
|
||||
|
||||
|
||||
|
@ -482,7 +504,7 @@ function Node:update()
|
|||
for _, view in ipairs(self.views) do
|
||||
view:update()
|
||||
end
|
||||
self:tab_hovered_update(self.hovered.x, self.hovered.y)
|
||||
self:tab_hovered_update(core.root_view.mouse.x, core.root_view.mouse.y)
|
||||
local tab_width = self:target_tab_width()
|
||||
self:move_towards("tab_shift", tab_width * (self.tab_offset - 1), nil, "tabs")
|
||||
self:move_towards("tab_width", tab_width, nil, "tabs")
|
||||
|
@ -525,6 +547,7 @@ function Node:draw_tab_borders(view, is_active, is_hovered, x, y, w, h, standalo
|
|||
if is_active then
|
||||
color = style.text
|
||||
renderer.draw_rect(x, y, w, h, style.background)
|
||||
renderer.draw_rect(x, y, w, ds, style.divider)
|
||||
renderer.draw_rect(x + w, y, ds, h, style.divider)
|
||||
renderer.draw_rect(x - ds, y, ds, h, style.divider)
|
||||
end
|
||||
|
@ -532,7 +555,8 @@ function Node:draw_tab_borders(view, is_active, is_hovered, x, y, w, h, standalo
|
|||
end
|
||||
|
||||
function Node:draw_tab(view, is_active, is_hovered, is_close_hovered, x, y, w, h, standalone)
|
||||
x, y, w, h = self:draw_tab_borders(view, is_active, is_hovered, x, y, w, h, standalone)
|
||||
local _, padding_y, margin_y = get_tab_y_sizes()
|
||||
x, y, w, h = self:draw_tab_borders(view, is_active, is_hovered, x, y + margin_y, w, h - margin_y, standalone)
|
||||
-- Close button
|
||||
local cx, cw, cpad = close_button_location(x, w)
|
||||
local show_close_button = ((is_active or is_hovered) and not standalone and config.tab_close_button)
|
||||
|
@ -608,6 +632,13 @@ function Node:is_empty()
|
|||
end
|
||||
|
||||
|
||||
function Node:is_in_tab_area(x, y)
|
||||
if not self:should_show_tabs() then return false end
|
||||
local _, ty, _, th = self:get_scroll_button_rect(1)
|
||||
return y >= ty and y < ty + th
|
||||
end
|
||||
|
||||
|
||||
function Node:close_all_docviews(keep_active)
|
||||
local node_active_view = self.active_view
|
||||
local lost_active_view = false
|
||||
|
@ -746,7 +777,7 @@ function Node:get_drag_overlay_tab_position(x, y, dragged_node, dragged_index)
|
|||
tab_index = self:get_visible_tabs_number() + (self.tab_offset - 1 or 0)
|
||||
end
|
||||
end
|
||||
local tab_x, tab_y, tab_w, tab_h = self:get_tab_rect(tab_index)
|
||||
local tab_x, tab_y, tab_w, tab_h, margin_y = self:get_tab_rect(tab_index)
|
||||
if x > tab_x + tab_w / 2 and tab_index <= #self.views then
|
||||
-- use next tab
|
||||
tab_x = tab_x + tab_w
|
||||
|
@ -757,7 +788,7 @@ function Node:get_drag_overlay_tab_position(x, y, dragged_node, dragged_index)
|
|||
tab_index = tab_index - 1
|
||||
tab_x = tab_x - tab_w
|
||||
end
|
||||
return tab_index, tab_x, tab_y, tab_w, tab_h
|
||||
return tab_index, tab_x, tab_y + margin_y, tab_w, tab_h - margin_y
|
||||
end
|
||||
|
||||
return Node
|
||||
|
|
|
@ -27,6 +27,13 @@ function Object:is(T)
|
|||
return getmetatable(self) == T
|
||||
end
|
||||
|
||||
---Check if the parameter is strictly of the object type.
|
||||
---@param T any
|
||||
---@return boolean
|
||||
function Object:is_class_of(T)
|
||||
return getmetatable(T) == self
|
||||
end
|
||||
|
||||
---Check if the object inherits from the given type.
|
||||
---@param T any
|
||||
---@return boolean
|
||||
|
@ -41,13 +48,29 @@ function Object:extends(T)
|
|||
return false
|
||||
end
|
||||
|
||||
---Check if the parameter inherits from the object.
|
||||
---@param T any
|
||||
---@return boolean
|
||||
function Object:is_extended_by(T)
|
||||
local mt = getmetatable(T)
|
||||
while mt do
|
||||
if mt == self then
|
||||
return true
|
||||
end
|
||||
local _mt = getmetatable(T)
|
||||
if mt == _mt then break end
|
||||
mt = _mt
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
---Metamethod to get a string representation of an object.
|
||||
---@return string
|
||||
function Object:__tostring()
|
||||
return "Object"
|
||||
end
|
||||
|
||||
---Methamethod to allow using the object call as a constructor.
|
||||
---Metamethod to allow using the object call as a constructor.
|
||||
---@return core.object
|
||||
function Object:__call(...)
|
||||
local obj = setmetatable({}, self)
|
||||
|
|
|
@ -24,6 +24,9 @@ function RootView:new()
|
|||
base_color = style.drag_overlay_tab,
|
||||
color = { table.unpack(style.drag_overlay_tab) } }
|
||||
self.drag_overlay_tab.to = { x = 0, y = 0, w = 0, h = 0 }
|
||||
self.grab = nil -- = {view = nil, button = nil}
|
||||
self.overlapping_view = nil
|
||||
self.touched_view = nil
|
||||
end
|
||||
|
||||
|
||||
|
@ -116,6 +119,31 @@ function RootView:close_all_docviews(keep_active)
|
|||
end
|
||||
|
||||
|
||||
---Obtain mouse grab.
|
||||
---
|
||||
---This means that mouse movements will be sent to the specified view, even when
|
||||
---those occur outside of it.
|
||||
---There can't be multiple mouse grabs, even for different buttons.
|
||||
---@see RootView:ungrab_mouse
|
||||
---@param button core.view.mousebutton
|
||||
---@param view core.view
|
||||
function RootView:grab_mouse(button, view)
|
||||
assert(self.grab == nil)
|
||||
self.grab = {view = view, button = button}
|
||||
end
|
||||
|
||||
|
||||
---Release mouse grab.
|
||||
---
|
||||
---The specified button *must* be the last button that grabbed the mouse.
|
||||
---@see RootView:grab_mouse
|
||||
---@param button core.view.mousebutton
|
||||
function RootView:ungrab_mouse(button)
|
||||
assert(self.grab and self.grab.button == button)
|
||||
self.grab = nil
|
||||
end
|
||||
|
||||
|
||||
---Function to intercept mouse pressed events on the active view.
|
||||
---Do nothing by default.
|
||||
---@param button core.view.mousebutton
|
||||
|
@ -132,6 +160,10 @@ end
|
|||
---@param clicks integer
|
||||
---@return boolean
|
||||
function RootView:on_mouse_pressed(button, x, y, clicks)
|
||||
-- If there is a grab, release it first
|
||||
if self.grab then
|
||||
self:on_mouse_released(self.grab.button, x, y)
|
||||
end
|
||||
local div = self.root_node:get_divider_overlapping_point(x, y)
|
||||
local node = self.root_node:get_child_overlapping_point(x, y)
|
||||
if div and (node and not node.active_view:scrollbar_overlaps_point(x, y)) then
|
||||
|
@ -156,6 +188,7 @@ function RootView:on_mouse_pressed(button, x, y, clicks)
|
|||
end
|
||||
elseif not self.dragged_node then -- avoid sending on_mouse_pressed events when dragging tabs
|
||||
core.set_active_view(node.active_view)
|
||||
self:grab_mouse(button, node.active_view)
|
||||
return self.on_view_mouse_pressed(button, x, y, clicks) or node.active_view:on_mouse_pressed(button, x, y, clicks)
|
||||
end
|
||||
end
|
||||
|
@ -188,6 +221,21 @@ end
|
|||
---@param x number
|
||||
---@param y number
|
||||
function RootView:on_mouse_released(button, x, y, ...)
|
||||
if self.grab then
|
||||
if self.grab.button == button then
|
||||
local grabbed_view = self.grab.view
|
||||
grabbed_view:on_mouse_released(button, x, y, ...)
|
||||
self:ungrab_mouse(button)
|
||||
|
||||
-- If the mouse was released over a different view, send it the mouse position
|
||||
local hovered_view = self.root_node:get_child_overlapping_point(x, y)
|
||||
if grabbed_view ~= hovered_view then
|
||||
self:on_mouse_moved(x, y, 0, 0)
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if self.dragged_divider then
|
||||
self.dragged_divider = nil
|
||||
end
|
||||
|
@ -228,8 +276,6 @@ function RootView:on_mouse_released(button, x, y, ...)
|
|||
end
|
||||
self.dragged_node = nil
|
||||
end
|
||||
else -- avoid sending on_mouse_released events when dragging tabs
|
||||
self.root_node:on_mouse_released(button, x, y, ...)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -250,6 +296,14 @@ end
|
|||
---@param dx number
|
||||
---@param dy number
|
||||
function RootView:on_mouse_moved(x, y, dx, dy)
|
||||
self.mouse.x, self.mouse.y = x, y
|
||||
|
||||
if self.grab then
|
||||
self.grab.view:on_mouse_moved(x, y, dx, dy)
|
||||
core.request_cursor(self.grab.view.cursor)
|
||||
return
|
||||
end
|
||||
|
||||
if core.active_view == core.nag_view then
|
||||
core.request_cursor("arrow")
|
||||
core.active_view:on_mouse_moved(x, y, dx, dy)
|
||||
|
@ -269,8 +323,6 @@ function RootView:on_mouse_moved(x, y, dx, dy)
|
|||
return
|
||||
end
|
||||
|
||||
self.mouse.x, self.mouse.y = x, y
|
||||
|
||||
local dn = self.dragged_node
|
||||
if dn and not dn.dragging then
|
||||
-- start dragging only after enough movement
|
||||
|
@ -283,32 +335,33 @@ function RootView:on_mouse_moved(x, y, dx, dy)
|
|||
-- avoid sending on_mouse_moved events when dragging tabs
|
||||
if dn then return end
|
||||
|
||||
self.root_node:on_mouse_moved(x, y, dx, dy)
|
||||
local last_overlapping_view = self.overlapping_view
|
||||
local overlapping_node = self.root_node:get_child_overlapping_point(x, y)
|
||||
self.overlapping_view = overlapping_node and overlapping_node.active_view
|
||||
|
||||
local last_overlapping_node = self.overlapping_node
|
||||
self.overlapping_node = self.root_node:get_child_overlapping_point(x, y)
|
||||
|
||||
if last_overlapping_node and last_overlapping_node ~= self.overlapping_node then
|
||||
last_overlapping_node:on_mouse_left()
|
||||
if last_overlapping_view and last_overlapping_view ~= self.overlapping_view then
|
||||
last_overlapping_view:on_mouse_left()
|
||||
end
|
||||
|
||||
if not self.overlapping_view then return end
|
||||
|
||||
self.overlapping_view:on_mouse_moved(x, y, dx, dy)
|
||||
core.request_cursor(self.overlapping_view.cursor)
|
||||
|
||||
if not overlapping_node then return end
|
||||
|
||||
local div = self.root_node:get_divider_overlapping_point(x, y)
|
||||
local tab_index = self.overlapping_node and self.overlapping_node:get_tab_overlapping_point(x, y)
|
||||
if self.overlapping_node and self.overlapping_node:get_scroll_button_index(x, y) then
|
||||
if overlapping_node:get_scroll_button_index(x, y) or overlapping_node:is_in_tab_area(x, y) then
|
||||
core.request_cursor("arrow")
|
||||
elseif div and (self.overlapping_node and not self.overlapping_node.active_view:scrollbar_overlaps_point(x, y)) then
|
||||
elseif div and not self.overlapping_view:scrollbar_overlaps_point(x, y) then
|
||||
core.request_cursor(div.type == "hsplit" and "sizeh" or "sizev")
|
||||
elseif tab_index then
|
||||
core.request_cursor("arrow")
|
||||
elseif self.overlapping_node then
|
||||
core.request_cursor(self.overlapping_node.active_view.cursor)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function RootView:on_mouse_left()
|
||||
if self.overlapping_node then
|
||||
self.overlapping_node:on_mouse_left()
|
||||
if self.overlapping_view then
|
||||
self.overlapping_view:on_mouse_left()
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -334,6 +387,50 @@ function RootView:on_text_input(...)
|
|||
core.active_view:on_text_input(...)
|
||||
end
|
||||
|
||||
function RootView:on_touch_pressed(x, y, ...)
|
||||
local touched_node = self.root_node:get_child_overlapping_point(x, y)
|
||||
self.touched_view = touched_node and touched_node.active_view
|
||||
end
|
||||
|
||||
function RootView:on_touch_released(x, y, ...)
|
||||
self.touched_view = nil
|
||||
end
|
||||
|
||||
function RootView:on_touch_moved(x, y, dx, dy, ...)
|
||||
if not self.touched_view then return end
|
||||
if core.active_view == core.nag_view then
|
||||
core.active_view:on_touch_moved(x, y, dx, dy, ...)
|
||||
return
|
||||
end
|
||||
|
||||
if self.dragged_divider then
|
||||
local node = self.dragged_divider
|
||||
if node.type == "hsplit" then
|
||||
x = common.clamp(x, 0, self.root_node.size.x * 0.95)
|
||||
resize_child_node(node, "x", x, dx)
|
||||
elseif node.type == "vsplit" then
|
||||
y = common.clamp(y, 0, self.root_node.size.y * 0.95)
|
||||
resize_child_node(node, "y", y, dy)
|
||||
end
|
||||
node.divider = common.clamp(node.divider, 0.01, 0.99)
|
||||
return
|
||||
end
|
||||
|
||||
local dn = self.dragged_node
|
||||
if dn and not dn.dragging then
|
||||
-- start dragging only after enough movement
|
||||
dn.dragging = common.distance(x, y, dn.drag_start_x, dn.drag_start_y) > style.tab_width * .05
|
||||
if dn.dragging then
|
||||
core.request_cursor("hand")
|
||||
end
|
||||
end
|
||||
|
||||
-- avoid sending on_touch_moved events when dragging tabs
|
||||
if dn then return end
|
||||
|
||||
self.touched_view:on_touch_moved(x, y, dx, dy, ...)
|
||||
end
|
||||
|
||||
function RootView:on_ime_text_editing(...)
|
||||
core.active_view:on_ime_text_editing(...)
|
||||
end
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
-- this file is used by lite-xl to setup the Lua environment when starting
|
||||
VERSION = "@PROJECT_VERSION@"
|
||||
MOD_VERSION = "3"
|
||||
MOD_VERSION_MAJOR = 3
|
||||
MOD_VERSION_MINOR = 0
|
||||
MOD_VERSION_PATCH = 0
|
||||
MOD_VERSION_STRING = string.format("%d.%d.%d", MOD_VERSION_MAJOR, MOD_VERSION_MINOR, MOD_VERSION_PATCH)
|
||||
|
||||
SCALE = tonumber(os.getenv("LITE_SCALE") or os.getenv("GDK_SCALE") or os.getenv("QT_SCALE_FACTOR")) or SCALE
|
||||
PATHSEP = package.config:sub(1, 1)
|
||||
|
@ -9,7 +12,7 @@ EXEDIR = EXEFILE:match("^(.+)[/\\][^/\\]+$")
|
|||
if MACOS_RESOURCES then
|
||||
DATADIR = MACOS_RESOURCES
|
||||
else
|
||||
local prefix = EXEDIR:match("^(.+)[/\\]bin$")
|
||||
local prefix = os.getenv('LITE_PREFIX') or EXEDIR:match("^(.+)[/\\]bin$")
|
||||
DATADIR = prefix and (prefix .. PATHSEP .. 'share' .. PATHSEP .. 'lite-xl') or (EXEDIR .. PATHSEP .. 'data')
|
||||
end
|
||||
USERDIR = (system.get_file_info(EXEDIR .. PATHSEP .. 'user') and (EXEDIR .. PATHSEP .. 'user'))
|
||||
|
@ -22,7 +25,7 @@ package.path = DATADIR .. '/?/init.lua;' .. package.path
|
|||
package.path = USERDIR .. '/?.lua;' .. package.path
|
||||
package.path = USERDIR .. '/?/init.lua;' .. package.path
|
||||
|
||||
local suffix = PLATFORM == "Mac OS X" and 'lib' or (PLATFORM == "Windows" and 'dll' or 'so')
|
||||
local suffix = PLATFORM == "Windows" and 'dll' or 'so'
|
||||
package.cpath =
|
||||
USERDIR .. '/?.' .. ARCH .. "." .. suffix .. ";" ..
|
||||
USERDIR .. '/?/init.' .. ARCH .. "." .. suffix .. ";" ..
|
||||
|
@ -35,8 +38,8 @@ package.cpath =
|
|||
|
||||
package.native_plugins = {}
|
||||
package.searchers = { package.searchers[1], package.searchers[2], function(modname)
|
||||
local path = package.searchpath(modname, package.cpath)
|
||||
if not path then return nil end
|
||||
local path, err = package.searchpath(modname, package.cpath)
|
||||
if not path then return err end
|
||||
return system.load_native_plugin, path
|
||||
end }
|
||||
|
||||
|
|
|
@ -241,6 +241,21 @@ function StatusView:register_docview_items()
|
|||
tooltip = "line : column"
|
||||
})
|
||||
|
||||
self:add_item({
|
||||
predicate = predicate_docview,
|
||||
name = "doc:selections",
|
||||
alignment = StatusView.Item.LEFT,
|
||||
get_item = function()
|
||||
local dv = core.active_view
|
||||
local nsel = math.floor(#dv.doc.selections / 4)
|
||||
if nsel > 1 then
|
||||
return { style.text, nsel, " selections" }
|
||||
end
|
||||
|
||||
return {}
|
||||
end
|
||||
})
|
||||
|
||||
self:add_item({
|
||||
predicate = predicate_docview,
|
||||
name = "doc:indentation",
|
||||
|
@ -974,6 +989,12 @@ function StatusView:on_mouse_pressed(button, x, y, clicks)
|
|||
end
|
||||
|
||||
|
||||
function StatusView:on_mouse_left()
|
||||
StatusView.super.on_mouse_left(self)
|
||||
self.hovered_item = {}
|
||||
end
|
||||
|
||||
|
||||
function StatusView:on_mouse_moved(x, y, dx, dy)
|
||||
if not self.visible then return end
|
||||
StatusView.super.on_mouse_moved(self, x, y, dx, dy)
|
||||
|
|
|
@ -1,13 +1,23 @@
|
|||
local common = require "core.common"
|
||||
local style = {}
|
||||
|
||||
style.padding = { x = common.round(14 * SCALE), y = common.round(7 * SCALE) }
|
||||
style.divider_size = common.round(1 * SCALE)
|
||||
style.scrollbar_size = common.round(4 * SCALE)
|
||||
style.expanded_scrollbar_size = common.round(12 * SCALE)
|
||||
style.caret_width = common.round(2 * SCALE)
|
||||
style.tab_width = common.round(170 * SCALE)
|
||||
|
||||
style.padding = {
|
||||
x = common.round(14 * SCALE),
|
||||
y = common.round(7 * SCALE),
|
||||
}
|
||||
|
||||
style.margin = {
|
||||
tab = {
|
||||
top = common.round(-style.divider_size * SCALE)
|
||||
}
|
||||
}
|
||||
|
||||
-- The function renderer.font.load can accept an option table as a second optional argument.
|
||||
-- It shoud be like the following:
|
||||
--
|
||||
|
|
|
@ -44,7 +44,7 @@ local function find(string, field)
|
|||
end
|
||||
|
||||
function syntax.get(filename, header)
|
||||
return find(common.basename(filename), "files")
|
||||
return (filename and find(filename, "files"))
|
||||
or (header and find(header, "headers"))
|
||||
or plain_text_syntax
|
||||
end
|
||||
|
|
|
@ -112,6 +112,12 @@ function TitleView:on_mouse_pressed(button, x, y, clicks)
|
|||
end
|
||||
|
||||
|
||||
function TitleView:on_mouse_left()
|
||||
TitleView.super.on_mouse_left(self)
|
||||
self.hovered_item = nil
|
||||
end
|
||||
|
||||
|
||||
function TitleView:on_mouse_moved(px, py, ...)
|
||||
if self.size.y == 0 then return end
|
||||
TitleView.super.on_mouse_moved(self, px, py, ...)
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
local core = require "core"
|
||||
local syntax = require "core.syntax"
|
||||
local config = require "core.config"
|
||||
|
||||
local tokenizer = {}
|
||||
local bad_patterns = {}
|
||||
|
||||
local function push_token(t, type, text)
|
||||
if not text or #text == 0 then return end
|
||||
type = type or "normal"
|
||||
local prev_type = t[#t-1]
|
||||
local prev_text = t[#t]
|
||||
if prev_type and (prev_type == type or prev_text:ufind("^%s*$")) then
|
||||
if prev_type and (prev_type == type or (prev_text:ufind("^%s*$") and type ~= "incomplete")) then
|
||||
t[#t-1] = type
|
||||
t[#t] = prev_text .. text
|
||||
else
|
||||
|
@ -26,11 +28,8 @@ local function push_tokens(t, syn, pattern, full_text, find_results)
|
|||
-- Each position spans characters from i_n to ((i_n+1) - 1), to form
|
||||
-- consecutive spans of text.
|
||||
--
|
||||
-- If i_1 is not equal to start, start is automatically inserted at
|
||||
-- that index.
|
||||
if find_results[3] ~= find_results[1] then
|
||||
-- Insert the start index at i_1 to make iterating easier
|
||||
table.insert(find_results, 3, find_results[1])
|
||||
end
|
||||
-- Copy the ending index to the end of the table, so that an ending index
|
||||
-- always follows a starting index after position 3 in the table.
|
||||
table.insert(find_results, find_results[2] + 1)
|
||||
|
@ -40,9 +39,11 @@ local function push_tokens(t, syn, pattern, full_text, find_results)
|
|||
local fin = find_results[i + 1] - 1
|
||||
local type = pattern.type[i - 2]
|
||||
-- ↑ (i - 2) to convert from [3; n] to [1; n]
|
||||
if fin >= start then
|
||||
local text = full_text:usub(start, fin)
|
||||
push_token(t, syn.symbols[text] or type, text)
|
||||
end
|
||||
end
|
||||
else
|
||||
local start, fin = find_results[1], find_results[2]
|
||||
local text = full_text:usub(start, fin)
|
||||
|
@ -128,15 +129,29 @@ end
|
|||
---@param incoming_syntax table
|
||||
---@param text string
|
||||
---@param state string
|
||||
function tokenizer.tokenize(incoming_syntax, text, state)
|
||||
local res = {}
|
||||
function tokenizer.tokenize(incoming_syntax, text, state, resume)
|
||||
local res
|
||||
local i = 1
|
||||
|
||||
state = state or string.char(0)
|
||||
|
||||
if #incoming_syntax.patterns == 0 then
|
||||
return { "normal", text }
|
||||
return { "normal", text }, state
|
||||
end
|
||||
|
||||
state = state or string.char(0)
|
||||
if resume then
|
||||
res = resume.res
|
||||
-- Remove "incomplete" tokens
|
||||
while res[#res-1] == "incomplete" do
|
||||
table.remove(res, #res)
|
||||
table.remove(res, #res)
|
||||
end
|
||||
i = resume.i
|
||||
state = resume.state
|
||||
end
|
||||
|
||||
res = res or {}
|
||||
|
||||
-- incoming_syntax : the parent syntax of the file.
|
||||
-- state : a string of bytes representing syntax state (see above)
|
||||
|
||||
|
@ -224,6 +239,7 @@ function tokenizer.tokenize(incoming_syntax, text, state)
|
|||
res[1] = char_pos_1
|
||||
res[2] = char_pos_2
|
||||
end
|
||||
if not res[1] then return end
|
||||
if res[1] and target[3] then
|
||||
-- Check to see if the escaped character is there,
|
||||
-- and if it is not itself escaped.
|
||||
|
@ -235,21 +251,39 @@ function tokenizer.tokenize(incoming_syntax, text, state)
|
|||
if count % 2 == 0 then
|
||||
-- The match is not escaped, so confirm it
|
||||
break
|
||||
elseif not close then
|
||||
-- The *open* match is escaped, so avoid it
|
||||
return
|
||||
else
|
||||
-- The match is escaped, so avoid it
|
||||
res[1] = false
|
||||
end
|
||||
end
|
||||
until not res[1] or not close or not target[3]
|
||||
until at_start or not close or not target[3]
|
||||
return table.unpack(res)
|
||||
end
|
||||
|
||||
local text_len = text:ulen()
|
||||
local start_time = system.get_time()
|
||||
local starting_i = i
|
||||
while i <= text_len do
|
||||
-- Every 200 chars, check if we're out of time
|
||||
if i - starting_i > 200 then
|
||||
starting_i = i
|
||||
if system.get_time() - start_time > 0.5 / config.fps then
|
||||
-- We're out of time
|
||||
push_token(res, "incomplete", string.usub(text, i))
|
||||
return res, string.char(0), {
|
||||
res = res,
|
||||
i = i,
|
||||
state = state
|
||||
}
|
||||
end
|
||||
end
|
||||
-- continue trying to match the end pattern of a pair if we have a state set
|
||||
if current_pattern_idx > 0 then
|
||||
local p = current_syntax.patterns[current_pattern_idx]
|
||||
local s, e = find_text(text, p, i, false, true)
|
||||
-- Use the first token type specified in the type table for the "middle"
|
||||
-- part of the subsyntax.
|
||||
local token_type = type(p.type) == "table" and p.type[1] or p.type
|
||||
|
||||
local cont = true
|
||||
-- If we're in subsyntax mode, always check to see if we end our syntax
|
||||
|
@ -262,7 +296,7 @@ function tokenizer.tokenize(incoming_syntax, text, state)
|
|||
-- treat the bit after as a token to be normally parsed
|
||||
-- (as it's the syntax delimiter).
|
||||
if ss and (s == nil or ss < s) then
|
||||
push_token(res, p.type, text:usub(i, ss - 1))
|
||||
push_token(res, token_type, text:usub(i, ss - 1))
|
||||
i = ss
|
||||
cont = false
|
||||
end
|
||||
|
@ -271,11 +305,11 @@ function tokenizer.tokenize(incoming_syntax, text, state)
|
|||
-- continue on as normal.
|
||||
if cont then
|
||||
if s then
|
||||
push_token(res, p.type, text:usub(i, e))
|
||||
push_token(res, token_type, text:usub(i, e))
|
||||
set_subsyntax_pattern_idx(0)
|
||||
i = e + 1
|
||||
else
|
||||
push_token(res, p.type, text:usub(i))
|
||||
push_token(res, token_type, text:usub(i))
|
||||
break
|
||||
end
|
||||
end
|
||||
|
@ -284,9 +318,10 @@ function tokenizer.tokenize(incoming_syntax, text, state)
|
|||
-- we're ending early in the middle of a delimiter, or
|
||||
-- just normally, upon finding a token.
|
||||
while subsyntax_info do
|
||||
local s, e = find_text(text, subsyntax_info, i, true, true)
|
||||
local find_results = { find_text(text, subsyntax_info, i, true, true) }
|
||||
local s, e = find_results[1], find_results[2]
|
||||
if s then
|
||||
push_token(res, subsyntax_info.type, text:usub(i, e))
|
||||
push_tokens(res, current_syntax, subsyntax_info, text, find_results)
|
||||
-- On finding unescaped delimiter, pop it.
|
||||
pop_subsyntax()
|
||||
i = e + 1
|
||||
|
|
|
@ -108,6 +108,10 @@ function View:get_h_scrollable_size()
|
|||
end
|
||||
|
||||
|
||||
function View:supports_text_input()
|
||||
return false
|
||||
end
|
||||
|
||||
---@param x number
|
||||
---@param y number
|
||||
---@return boolean
|
||||
|
@ -244,6 +248,23 @@ function View:get_content_bounds()
|
|||
return x, y, x + self.size.x, y + self.size.y
|
||||
end
|
||||
|
||||
---@param x number
|
||||
---@param y number
|
||||
---@param dx number
|
||||
---@param dy number
|
||||
---@param i number
|
||||
function View:on_touch_moved(x, y, dx, dy, i)
|
||||
if not self.scrollable then return end
|
||||
if self.dragging_scrollbar then
|
||||
local delta = self:get_scrollable_size() / self.size.y * dy
|
||||
self.scroll.to.y = self.scroll.to.y + delta
|
||||
end
|
||||
self.hovered_scrollbar = self:scrollbar_overlaps_point(x, y)
|
||||
|
||||
self.scroll.to.y = self.scroll.to.y + -dy
|
||||
self.scroll.to.x = self.scroll.to.x + -dx
|
||||
end
|
||||
|
||||
|
||||
---@return number x
|
||||
---@return number y
|
||||
|
|
|
@ -631,7 +631,6 @@ end
|
|||
command.add(predicate, {
|
||||
["autocomplete:complete"] = function(dv)
|
||||
local doc = dv.doc
|
||||
local line, col = doc:get_selection()
|
||||
local item = suggestions[suggestions_idx]
|
||||
local text = item.text
|
||||
local inserted = false
|
||||
|
@ -640,9 +639,23 @@ command.add(predicate, {
|
|||
end
|
||||
if not inserted then
|
||||
local current_partial = get_partial_symbol()
|
||||
doc:insert(line, col, text)
|
||||
doc:remove(line, col, line, col - #current_partial)
|
||||
doc:set_selection(line, col + #text - #current_partial)
|
||||
local sz = #current_partial
|
||||
|
||||
for idx, line1, col1, line2, col2 in doc:get_selections(true) do
|
||||
local n = col1 - 1
|
||||
local line = doc.lines[line1]
|
||||
for i = 1, sz + 1 do
|
||||
local j = sz - i
|
||||
local subline = line:sub(n - j, n)
|
||||
local subpartial = current_partial:sub(i, -1)
|
||||
if subpartial == subline then
|
||||
doc:remove(line1, col1, line2, n - j)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
doc:text_input(item.text)
|
||||
end
|
||||
reset_suggestions()
|
||||
end,
|
||||
|
|
|
@ -46,8 +46,8 @@ end
|
|||
local function check_prompt_reload(doc)
|
||||
if doc and doc.deferred_reload then
|
||||
core.nag_view:show("File Changed", doc.filename .. " has changed. Reload this file?", {
|
||||
{ font = style.font, text = "Yes", default_yes = true },
|
||||
{ font = style.font, text = "No" , default_no = true }
|
||||
{ text = "Yes", default_yes = true },
|
||||
{ text = "No", default_no = true }
|
||||
}, function(item)
|
||||
if item.text == "Yes" then reload_doc(doc) end
|
||||
doc.deferred_reload = false
|
||||
|
|
|
@ -4,6 +4,7 @@ local command = require "core.command"
|
|||
local keymap = require "core.keymap"
|
||||
local ContextMenu = require "core.contextmenu"
|
||||
local RootView = require "core.rootview"
|
||||
local config = require "core.config"
|
||||
|
||||
local menu = ContextMenu()
|
||||
local on_view_mouse_pressed = RootView.on_view_mouse_pressed
|
||||
|
@ -61,18 +62,24 @@ keymap.add { ["up"] = "context:focus-previous" }
|
|||
keymap.add { ["down"] = "context:focus-next" }
|
||||
keymap.add { ["escape"] = "context:hide" }
|
||||
|
||||
if require("plugins.scale") then
|
||||
menu:register("core.docview", {
|
||||
|
||||
local cmds = {
|
||||
{ text = "Cut", command = "doc:cut" },
|
||||
{ text = "Copy", command = "doc:copy" },
|
||||
{ text = "Paste", command = "doc:paste" },
|
||||
{ text = "Font +", command = "scale:increase" },
|
||||
{ text = "Font -", command = "scale:decrease" },
|
||||
{ text = "Font Reset", command = "scale:reset" },
|
||||
ContextMenu.DIVIDER,
|
||||
{ text = "Find", command = "find-replace:find" },
|
||||
{ text = "Replace", command = "find-replace:replace" }
|
||||
})
|
||||
}
|
||||
|
||||
if config.plugins.scale ~= false and require("plugins.scale") then
|
||||
table.move(cmds, 4, 6, 7)
|
||||
cmds[4] = { text = "Font +", command = "scale:increase" }
|
||||
cmds[5] = { text = "Font -", command = "scale:decrease" }
|
||||
cmds[6] = { text = "Font Reset", command = "scale:reset" }
|
||||
end
|
||||
|
||||
menu:register("core.docview", cmds)
|
||||
|
||||
|
||||
return menu
|
||||
|
|
|
@ -37,7 +37,11 @@ local function optimal_indent_from_stat(stat)
|
|||
elseif
|
||||
indent > stat[y]
|
||||
and
|
||||
(
|
||||
indent_occurrences_more_than_once(stat, y)
|
||||
or
|
||||
(y == count and stat[y] > 1)
|
||||
)
|
||||
then
|
||||
score = 0
|
||||
break
|
||||
|
@ -118,10 +122,10 @@ local function get_comment_patterns(syntax, _loop)
|
|||
end
|
||||
if type(pattern.regex) == "table" then
|
||||
table.insert(comments, {
|
||||
"r", regex.compile(startp), regex.compile(pattern.regex[2])
|
||||
"r", regex.compile(startp), regex.compile(pattern.regex[2]), r=startp
|
||||
})
|
||||
elseif not_is_string then
|
||||
table.insert(comments, {"r", regex.compile(startp)})
|
||||
table.insert(comments, {"r", regex.compile(startp), r=startp})
|
||||
end
|
||||
end
|
||||
elseif pattern.syntax then
|
||||
|
@ -152,6 +156,25 @@ local function get_comment_patterns(syntax, _loop)
|
|||
table.insert(comments, {"p", "^%s*" .. block_comment[1], block_comment[2]})
|
||||
end
|
||||
end
|
||||
-- Put comments first and strings last
|
||||
table.sort(comments, function(c1, c2)
|
||||
local comment1, comment2 = false, false
|
||||
if
|
||||
(c1[1] == "p" and string.find(c1[2], "^%s*", 1, true))
|
||||
or
|
||||
(c1[1] == "r" and string.find(c1["r"], "^\\s*", 1, true))
|
||||
then
|
||||
comment1 = true
|
||||
end
|
||||
if
|
||||
(c2[1] == "p" and string.find(c2[2], "^%s*", 1, true))
|
||||
or
|
||||
(c2[1] == "r" and string.find(c2["r"], "^\\s*", 1, true))
|
||||
then
|
||||
comment2 = true
|
||||
end
|
||||
return comment1 and not comment2
|
||||
end)
|
||||
comments_cache[syntax] = comments
|
||||
if #comments > 0 then
|
||||
return comments
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
-- mod-version:3
|
||||
|
||||
local core = require "core"
|
||||
local style = require "core.style"
|
||||
local DocView = require "core.docview"
|
||||
local common = require "core.common"
|
||||
local command = require "core.command"
|
||||
local config = require "core.config"
|
||||
local Highlighter = require "core.doc.highlighter"
|
||||
|
||||
config.plugins.drawwhitespace = common.merge({
|
||||
enabled = true,
|
||||
enabled = false,
|
||||
show_leading = true,
|
||||
show_trailing = true,
|
||||
show_middle = true,
|
||||
show_selected_only = false,
|
||||
|
||||
show_middle_min = 1,
|
||||
|
||||
|
@ -41,7 +44,7 @@ config.plugins.drawwhitespace = common.merge({
|
|||
description = "Disable or enable the drawing of white spaces.",
|
||||
path = "enabled",
|
||||
type = "toggle",
|
||||
default = true
|
||||
default = false
|
||||
},
|
||||
{
|
||||
label = "Show Leading",
|
||||
|
@ -64,6 +67,13 @@ config.plugins.drawwhitespace = common.merge({
|
|||
type = "toggle",
|
||||
default = true,
|
||||
},
|
||||
{
|
||||
label = "Show Selected Only",
|
||||
description = "Only draw whitespaces if it is within a selection.",
|
||||
path = "show_selected_only",
|
||||
type = "toggle",
|
||||
default = false,
|
||||
},
|
||||
{
|
||||
label = "Show Trailing as Error",
|
||||
description = "Uses an error square to spot them easily, requires 'Show Trailing' enabled.",
|
||||
|
@ -292,14 +302,59 @@ function DocView:draw_line_text(idx, x, y)
|
|||
for i=1,#cache,4 do
|
||||
local tx = cache[i + 1] + x
|
||||
local tw = cache[i + 2]
|
||||
if tx <= x2 then
|
||||
local sub = cache[i]
|
||||
local color = cache[i + 3]
|
||||
if tx + tw >= x1 then
|
||||
tx = renderer.draw_text(font, sub, tx, ty, color)
|
||||
local partials = {}
|
||||
if config.plugins.drawwhitespace.show_selected_only and self.doc:has_any_selection() then
|
||||
for _, l1, c1, l2, c2 in self.doc:get_selections(true) do
|
||||
if idx > l1 and idx < l2 then
|
||||
-- Between selection lines, so everything is selected
|
||||
table.insert(partials, false)
|
||||
elseif idx == l1 and idx == l2 then
|
||||
-- Both ends of the selection are on the same line
|
||||
local _x1 = math.max(cache[i + 1], self:get_col_x_offset(idx, c1))
|
||||
local _x2 = math.min((cache[i + 1] + tw), self:get_col_x_offset(idx, c2))
|
||||
if _x1 < _x2 then
|
||||
table.insert(partials, {_x1 + x, 0, _x2 - _x1, math.huge})
|
||||
end
|
||||
elseif idx >= l1 and idx <= l2 then
|
||||
-- On one of the selection ends
|
||||
if idx == l1 then -- Start of the selection
|
||||
local _x = math.max(cache[i + 1], self:get_col_x_offset(idx, c1))
|
||||
table.insert(partials, {_x + x, 0, math.huge, math.huge})
|
||||
else -- End of the selection
|
||||
local _x = math.min((cache[i + 1] + tw), self:get_col_x_offset(idx, c2))
|
||||
table.insert(partials, {0, 0, _x + x, math.huge})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if #partials == 0 and not config.plugins.drawwhitespace.show_selected_only then
|
||||
renderer.draw_text(font, sub, tx, ty, color)
|
||||
else
|
||||
for _, p in pairs(partials) do
|
||||
if p then core.push_clip_rect(table.unpack(p)) end
|
||||
renderer.draw_text(font, sub, tx, ty, color)
|
||||
if p then core.pop_clip_rect() end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return draw_line_text(self, idx, x, y)
|
||||
end
|
||||
|
||||
|
||||
command.add(nil, {
|
||||
["draw-whitespace:toggle"] = function()
|
||||
config.plugins.drawwhitespace.enabled = not config.drawwhitespace.enabled
|
||||
end,
|
||||
|
||||
["draw-whitespace:disable"] = function()
|
||||
config.plugins.drawwhitespace.enabled = false
|
||||
end,
|
||||
|
||||
["draw-whitespace:enable"] = function()
|
||||
config.plugins.drawwhitespace.enabled = true
|
||||
end,
|
||||
})
|
||||
|
|
|
@ -147,6 +147,7 @@ syntax.add {
|
|||
{ pattern = { "```go", "```" }, type = "string", syntax = ".go" },
|
||||
{ pattern = { "```lobster", "```" }, type = "string", syntax = ".lobster" },
|
||||
{ pattern = { "```liquid", "```" }, type = "string", syntax = ".liquid" },
|
||||
{ pattern = { "```nix", "```" }, type = "string", syntax = ".nix" },
|
||||
{ pattern = { "```", "```" }, type = "string" },
|
||||
{ pattern = { "``", "``" }, type = "string" },
|
||||
{ pattern = { "%f[\\`]%`[%S]", "`" }, type = "string" },
|
||||
|
|
|
@ -15,6 +15,8 @@ config.plugins.lineguide = common.merge({
|
|||
-- 120,
|
||||
config.line_limit
|
||||
},
|
||||
use_custom_color = false,
|
||||
custom_color = style.selection,
|
||||
-- The config specification used by gui generators
|
||||
config_spec = {
|
||||
name = "Line Guide",
|
||||
|
@ -63,7 +65,21 @@ config.plugins.lineguide = common.merge({
|
|||
end
|
||||
return new_rulers
|
||||
end
|
||||
}
|
||||
},
|
||||
{
|
||||
label = "Use Custom Color",
|
||||
description = "Enable the utilization of a custom line color.",
|
||||
path = "use_custom_color",
|
||||
type = "toggle",
|
||||
default = false
|
||||
},
|
||||
{
|
||||
label = "Custom Color",
|
||||
description = "Applied when the above toggle is enabled.",
|
||||
path = "custom_color",
|
||||
type = "color",
|
||||
default = style.selection
|
||||
},
|
||||
}
|
||||
}, config.plugins.lineguide)
|
||||
|
||||
|
@ -79,8 +95,6 @@ end
|
|||
|
||||
local draw_overlay = DocView.draw_overlay
|
||||
function DocView:draw_overlay(...)
|
||||
draw_overlay(self, ...)
|
||||
|
||||
if
|
||||
type(config.plugins.lineguide) == "table"
|
||||
and
|
||||
|
@ -88,10 +102,12 @@ function DocView:draw_overlay(...)
|
|||
and
|
||||
self:is(DocView)
|
||||
then
|
||||
local conf = config.plugins.lineguide
|
||||
local line_x = self:get_line_screen_position(1)
|
||||
local character_width = self:get_font():get_width("n")
|
||||
local ruler_width = config.plugins.lineguide.width
|
||||
local ruler_color = style.guide or style.selection
|
||||
local ruler_color = conf.use_custom_color and conf.custom_color
|
||||
or (style.guide or style.selection)
|
||||
|
||||
for k,v in ipairs(config.plugins.lineguide.rulers) do
|
||||
local ruler = get_ruler(v)
|
||||
|
@ -106,6 +122,8 @@ function DocView:draw_overlay(...)
|
|||
end
|
||||
end
|
||||
end
|
||||
-- everything else like the cursor above the line guides
|
||||
draw_overlay(self, ...)
|
||||
end
|
||||
|
||||
command.add(nil, {
|
||||
|
|
|
@ -310,7 +310,7 @@ local function get_line_col_from_index_and_x(docview, idx, x)
|
|||
end
|
||||
|
||||
|
||||
local open_files = {}
|
||||
local open_files = setmetatable({ }, { __mode = "k" })
|
||||
|
||||
local old_doc_insert = Doc.raw_insert
|
||||
function Doc:raw_insert(line, col, text, undo_stack, time)
|
||||
|
@ -485,7 +485,7 @@ local old_draw_line_body = DocView.draw_line_body
|
|||
function DocView:draw_line_body(line, x, y)
|
||||
if not self.wrapped_settings then return old_draw_line_body(self, line, x, y) end
|
||||
local lh = self:get_line_height()
|
||||
local idx0 = get_line_idx_col_count(self, line)
|
||||
local idx0, _, count = get_line_idx_col_count(self, line)
|
||||
for lidx, line1, col1, line2, col2 in self.doc:get_selections(true) do
|
||||
if line >= line1 and line <= line2 then
|
||||
if line1 ~= line then col1 = 1 end
|
||||
|
@ -493,12 +493,14 @@ function DocView:draw_line_body(line, x, y)
|
|||
if col1 ~= col2 then
|
||||
local idx1, ncol1 = get_line_idx_col_count(self, line, col1)
|
||||
local idx2, ncol2 = get_line_idx_col_count(self, line, col2)
|
||||
local start = 0
|
||||
for i = idx1, idx2 do
|
||||
local x1, x2 = x + (idx1 == i and self:get_col_x_offset(line1, col1) or 0)
|
||||
if idx2 == i then
|
||||
x2 = x + self:get_col_x_offset(line, col2)
|
||||
else
|
||||
x2 = x + self:get_col_x_offset(line, get_idx_line_length(self, i, line) + 1, true)
|
||||
start = start + get_idx_line_length(self, i, line)
|
||||
x2 = x + self:get_col_x_offset(line, start + 1, true)
|
||||
end
|
||||
renderer.draw_rect(x1, y + (i - idx0) * lh, x2 - x1, lh, style.selection)
|
||||
end
|
||||
|
@ -514,7 +516,6 @@ function DocView:draw_line_body(line, x, y)
|
|||
end
|
||||
end
|
||||
if draw_highlight then
|
||||
local _, _, count = get_line_idx_col_count(self, line)
|
||||
for i=1,count do
|
||||
self:draw_line_highlight(x + self.scroll.x, y + lh * (i - 1))
|
||||
end
|
||||
|
|
|
@ -37,7 +37,7 @@ local function find_all_matches_in_file(t, filename, fn)
|
|||
table.insert(t, { file = filename, text = (start_index > 1 and "..." or "") .. line:sub(start_index, 256 + start_index), line = n, col = s })
|
||||
core.redraw = true
|
||||
end
|
||||
if n % 100 == 0 then coroutine.yield() end
|
||||
if n % 100 == 0 then coroutine.yield(0) end
|
||||
n = n + 1
|
||||
core.redraw = true
|
||||
end
|
||||
|
|
|
@ -25,11 +25,16 @@ local function set_scale(scale)
|
|||
scale = common.clamp(scale, 0.2, 6)
|
||||
|
||||
-- save scroll positions
|
||||
local scrolls = {}
|
||||
local v_scrolls = {}
|
||||
local h_scrolls = {}
|
||||
for _, view in ipairs(core.root_view.root_node:get_children()) do
|
||||
local n = view:get_scrollable_size()
|
||||
if n ~= math.huge and not view:is(CommandView) and n > view.size.y then
|
||||
scrolls[view] = view.scroll.y / (n - view.size.y)
|
||||
if n ~= math.huge and n > view.size.y then
|
||||
v_scrolls[view] = view.scroll.y / (n - view.size.y)
|
||||
end
|
||||
local hn = view:get_h_scrollable_size()
|
||||
if hn ~= math.huge and hn > view.size.x then
|
||||
h_scrolls[view] = view.scroll.x / (hn - view.size.x)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -43,6 +48,7 @@ local function set_scale(scale)
|
|||
style.padding.y = style.padding.y * s
|
||||
style.divider_size = style.divider_size * s
|
||||
style.scrollbar_size = style.scrollbar_size * s
|
||||
style.expanded_scrollbar_size = style.expanded_scrollbar_size * s
|
||||
style.caret_width = style.caret_width * s
|
||||
style.tab_width = style.tab_width * s
|
||||
|
||||
|
@ -58,10 +64,14 @@ local function set_scale(scale)
|
|||
end
|
||||
|
||||
-- restore scroll positions
|
||||
for view, n in pairs(scrolls) do
|
||||
for view, n in pairs(v_scrolls) do
|
||||
view.scroll.y = n * (view:get_scrollable_size() - view.size.y)
|
||||
view.scroll.to.y = view.scroll.y
|
||||
end
|
||||
for view, hn in pairs(h_scrolls) do
|
||||
view.scroll.x = hn * (view:get_h_scrollable_size() - view.size.x)
|
||||
view.scroll.to.x = view.scroll.x
|
||||
end
|
||||
|
||||
core.redraw = true
|
||||
end
|
||||
|
|
|
@ -48,7 +48,7 @@ end
|
|||
|
||||
function ToolbarView:get_icon_width()
|
||||
local max_width = 0
|
||||
for i,v in ipairs(self.toolbar_commands) do max_width = math.max(max_width, self.toolbar_font:get_width(v.symbol)) end
|
||||
for i,v in ipairs(self.toolbar_commands) do max_width = math.max(max_width, (v.font or self.toolbar_font):get_width(v.symbol)) end
|
||||
return max_width
|
||||
end
|
||||
|
||||
|
@ -83,7 +83,7 @@ function ToolbarView:draw()
|
|||
|
||||
for item, x, y, w, h in self:each_item() do
|
||||
local color = item == self.hovered_item and command.is_valid(item.command) and style.text or style.dim
|
||||
common.draw_text(self.toolbar_font, color, item.symbol, nil, x, y, 0, h)
|
||||
common.draw_text(item.font or self.toolbar_font, color, item.symbol, nil, x, y, 0, h)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -100,6 +100,16 @@ function ToolbarView:on_mouse_pressed(button, x, y, clicks)
|
|||
end
|
||||
|
||||
|
||||
function ToolbarView:on_mouse_left()
|
||||
ToolbarView.super.on_mouse_left(self)
|
||||
if self.tooltip then
|
||||
core.status_view:remove_tooltip()
|
||||
self.tooltip = false
|
||||
end
|
||||
self.hovered_item = nil
|
||||
end
|
||||
|
||||
|
||||
function ToolbarView:on_mouse_moved(px, py, ...)
|
||||
if not self.visible then return end
|
||||
ToolbarView.super.on_mouse_moved(self, px, py, ...)
|
||||
|
|
|
@ -9,10 +9,15 @@ local View = require "core.view"
|
|||
local ContextMenu = require "core.contextmenu"
|
||||
local RootView = require "core.rootview"
|
||||
local CommandView = require "core.commandview"
|
||||
local DocView = require "core.docview"
|
||||
|
||||
config.plugins.treeview = common.merge({
|
||||
-- Default treeview width
|
||||
size = 200 * SCALE
|
||||
size = 200 * SCALE,
|
||||
highlight_focused_file = true,
|
||||
expand_dirs_to_focused_file = false,
|
||||
scroll_to_focused_file = false,
|
||||
animate_scroll_to_focused_file = true
|
||||
}, config.plugins.treeview)
|
||||
|
||||
local tooltip_offset = style.font:get_height()
|
||||
|
@ -46,7 +51,7 @@ function TreeView:new()
|
|||
self.target_size = config.plugins.treeview.size
|
||||
self.cache = {}
|
||||
self.tooltip = { x = 0, y = 0, begin = 0, alpha = 0 }
|
||||
self.cursor_pos = { x = 0, y = 0 }
|
||||
self.last_scroll_y = 0
|
||||
|
||||
self.item_icon_width = 0
|
||||
self.item_text_spacing = 0
|
||||
|
@ -169,18 +174,71 @@ function TreeView:each_item()
|
|||
end
|
||||
|
||||
|
||||
function TreeView:set_selection(selection, selection_y)
|
||||
function TreeView:set_selection(selection, selection_y, center, instant)
|
||||
self.selected_item = selection
|
||||
if selection and selection_y
|
||||
and (selection_y <= 0 or selection_y >= self.size.y) then
|
||||
|
||||
local lh = self:get_item_height()
|
||||
if selection_y >= self.size.y - lh then
|
||||
if not center and selection_y >= self.size.y - lh then
|
||||
selection_y = selection_y - self.size.y + lh
|
||||
end
|
||||
local _, y = self:get_content_offset()
|
||||
self.scroll.to.y = selection and (selection_y - y)
|
||||
if center then
|
||||
selection_y = selection_y - (self.size.y - lh) / 2
|
||||
end
|
||||
local _, y = self:get_content_offset()
|
||||
self.scroll.to.y = selection_y - y
|
||||
self.scroll.to.y = common.clamp(self.scroll.to.y, 0, self:get_scrollable_size() - self.size.y)
|
||||
if instant then
|
||||
self.scroll.y = self.scroll.to.y
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---Sets the selection to the file with the specified path.
|
||||
---
|
||||
---@param path string #Absolute path of item to select
|
||||
---@param expand boolean #Expand dirs leading to the item
|
||||
---@param scroll_to boolean #Scroll to make the item visible
|
||||
---@param instant boolean #Don't animate the scroll
|
||||
---@return table? #The selected item
|
||||
function TreeView:set_selection_to_path(path, expand, scroll_to, instant)
|
||||
local to_select, to_select_y
|
||||
local let_it_finish, done
|
||||
::restart::
|
||||
for item, x,y,w,h in self:each_item() do
|
||||
if not done then
|
||||
if item.type == "dir" then
|
||||
local _, to = string.find(path, item.abs_filename..PATHSEP, 1, true)
|
||||
if to and to == #item.abs_filename + #PATHSEP then
|
||||
to_select, to_select_y = item, y
|
||||
if expand and not item.expanded then
|
||||
-- Use TreeView:toggle_expand to update the directory structure.
|
||||
-- Directly using item.expanded doesn't update the cached tree.
|
||||
self:toggle_expand(true, item)
|
||||
-- Because we altered the size of the TreeView
|
||||
-- and because TreeView:get_scrollable_size uses self.count_lines
|
||||
-- which gets updated only when TreeView:each_item finishes,
|
||||
-- we can't stop here or we risk that the scroll
|
||||
-- gets clamped by View:clamp_scroll_position.
|
||||
let_it_finish = true
|
||||
-- We need to restart the process because if TreeView:toggle_expand
|
||||
-- altered the cache, TreeView:each_item risks looping indefinitely.
|
||||
goto restart
|
||||
end
|
||||
end
|
||||
else
|
||||
if item.abs_filename == path then
|
||||
to_select, to_select_y = item, y
|
||||
done = true
|
||||
if not let_it_finish then break end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if to_select then
|
||||
self:set_selection(to_select, scroll_to and to_select_y, true, instant)
|
||||
end
|
||||
return to_select
|
||||
end
|
||||
|
||||
|
||||
|
@ -193,10 +251,9 @@ function TreeView:get_text_bounding_box(item, x, y, w, h)
|
|||
end
|
||||
|
||||
|
||||
|
||||
function TreeView:on_mouse_moved(px, py, ...)
|
||||
if not self.visible then return end
|
||||
self.cursor_pos.x = px
|
||||
self.cursor_pos.y = py
|
||||
if TreeView.super.on_mouse_moved(self, px, py, ...) then
|
||||
-- mouse movement handled by the View (scrollbar)
|
||||
self.hovered_item = nil
|
||||
|
@ -223,6 +280,12 @@ function TreeView:on_mouse_moved(px, py, ...)
|
|||
end
|
||||
|
||||
|
||||
function TreeView:on_mouse_left()
|
||||
TreeView.super.on_mouse_left(self)
|
||||
self.hovered_item = nil
|
||||
end
|
||||
|
||||
|
||||
function TreeView:update()
|
||||
-- update width
|
||||
local dest = self.visible and self.target_size or 0
|
||||
|
@ -233,7 +296,7 @@ function TreeView:update()
|
|||
self:move_towards(self.size, "x", dest, nil, "treeview")
|
||||
end
|
||||
|
||||
if not self.visible then return end
|
||||
if self.size.x == 0 or self.size.y == 0 or not self.visible then return end
|
||||
|
||||
local duration = system.get_time() - self.tooltip.begin
|
||||
if self.hovered_item and self.tooltip.x and duration > tooltip_delay then
|
||||
|
@ -246,10 +309,30 @@ function TreeView:update()
|
|||
self.item_text_spacing = style.icon_font:get_width("f") / 2
|
||||
|
||||
-- this will make sure hovered_item is updated
|
||||
-- we don't want events when the thing is scrolling fast
|
||||
local dy = math.abs(self.scroll.to.y - self.scroll.y)
|
||||
if self.scroll.to.y ~= 0 and dy < self:get_item_height() then
|
||||
self:on_mouse_moved(self.cursor_pos.x, self.cursor_pos.y, 0, 0)
|
||||
local dy = math.abs(self.last_scroll_y - self.scroll.y)
|
||||
if dy > 0 then
|
||||
self:on_mouse_moved(core.root_view.mouse.x, core.root_view.mouse.y, 0, 0)
|
||||
self.last_scroll_y = self.scroll.y
|
||||
end
|
||||
|
||||
local config = config.plugins.treeview
|
||||
if config.highlight_focused_file then
|
||||
-- Try to only highlight when we actually change tabs
|
||||
local current_node = core.root_view:get_active_node()
|
||||
local current_active_view = core.active_view
|
||||
if current_node and not current_node.locked
|
||||
and current_active_view ~= self and current_active_view ~= self.last_active_view then
|
||||
self.selected_item = nil
|
||||
self.last_active_view = current_active_view
|
||||
if DocView:is_extended_by(current_active_view) then
|
||||
local abs_filename = current_active_view.doc
|
||||
and current_active_view.doc.abs_filename or ""
|
||||
self:set_selection_to_path(abs_filename,
|
||||
config.expand_dirs_to_focused_file,
|
||||
config.scroll_to_focused_file,
|
||||
not config.animate_scroll_to_focused_file)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
TreeView.super.update(self)
|
||||
|
@ -422,8 +505,8 @@ function TreeView:get_previous(item)
|
|||
end
|
||||
|
||||
|
||||
function TreeView:toggle_expand(toggle)
|
||||
local item = self.selected_item
|
||||
function TreeView:toggle_expand(toggle, item)
|
||||
item = item or self.selected_item
|
||||
|
||||
if not item then return end
|
||||
|
||||
|
@ -441,6 +524,11 @@ function TreeView:toggle_expand(toggle)
|
|||
end
|
||||
|
||||
|
||||
function TreeView:open_doc(filename)
|
||||
core.root_view:open_doc(core.open_doc(filename))
|
||||
end
|
||||
|
||||
|
||||
-- init
|
||||
local view = TreeView()
|
||||
local node = core.root_view:get_active_node()
|
||||
|
@ -518,15 +606,19 @@ local function is_primary_project_folder(path)
|
|||
return core.project_dir == path
|
||||
end
|
||||
|
||||
menu:register(function() return view.hovered_item end, {
|
||||
|
||||
local function treeitem() return view.hovered_item or view.selected_item end
|
||||
|
||||
|
||||
menu:register(function() return core.active_view:is(TreeView) and treeitem() end, {
|
||||
{ text = "Open in System", command = "treeview:open-in-system" },
|
||||
ContextMenu.DIVIDER
|
||||
})
|
||||
|
||||
menu:register(
|
||||
function()
|
||||
return view.hovered_item
|
||||
and not is_project_folder(view.hovered_item.abs_filename)
|
||||
local item = treeitem()
|
||||
return core.active_view:is(TreeView) and item and not is_project_folder(item.abs_filename)
|
||||
end,
|
||||
{
|
||||
{ text = "Rename", command = "treeview:rename" },
|
||||
|
@ -536,7 +628,8 @@ menu:register(
|
|||
|
||||
menu:register(
|
||||
function()
|
||||
return view.hovered_item and view.hovered_item.type == "dir"
|
||||
local item = treeitem()
|
||||
return core.active_view:is(TreeView) and item and item.type == "dir"
|
||||
end,
|
||||
{
|
||||
{ text = "New File", command = "treeview:new-file" },
|
||||
|
@ -546,9 +639,10 @@ menu:register(
|
|||
|
||||
menu:register(
|
||||
function()
|
||||
return view.hovered_item
|
||||
and not is_primary_project_folder(view.hovered_item.abs_filename)
|
||||
and is_project_folder(view.hovered_item.abs_filename)
|
||||
local item = treeitem()
|
||||
return core.active_view:is(TreeView) and item
|
||||
and not is_primary_project_folder(item.abs_filename)
|
||||
and is_project_folder(item.abs_filename)
|
||||
end,
|
||||
{
|
||||
{ text = "Remove directory", command = "treeview:remove-project-directory" },
|
||||
|
@ -589,7 +683,10 @@ command.add(nil, {
|
|||
end
|
||||
})
|
||||
|
||||
command.add(TreeView, {
|
||||
command.add(
|
||||
function()
|
||||
return not menu.show_context_menu and core.active_view:extends(TreeView), TreeView
|
||||
end, {
|
||||
["treeview:next"] = function()
|
||||
local item, _, item_y = view:get_next(view.selected_item)
|
||||
view:set_selection(item, item_y)
|
||||
|
@ -610,8 +707,7 @@ command.add(TreeView, {
|
|||
if core.last_active_view and core.active_view == view then
|
||||
core.set_active_view(core.last_active_view)
|
||||
end
|
||||
local doc_filename = core.normalize_to_project_dir(item.abs_filename)
|
||||
core.root_view:open_doc(core.open_doc(doc_filename))
|
||||
view:open_doc(core.normalize_to_project_dir(item.abs_filename))
|
||||
end)
|
||||
end
|
||||
end,
|
||||
|
@ -657,22 +753,33 @@ command.add(TreeView, {
|
|||
view:toggle_expand(true)
|
||||
end
|
||||
end,
|
||||
|
||||
["treeview-context:show"] = function()
|
||||
if view.hovered_item then
|
||||
menu:show(core.root_view.mouse.x, core.root_view.mouse.y)
|
||||
return
|
||||
end
|
||||
|
||||
local item = view.selected_item
|
||||
if not item then return end
|
||||
|
||||
local x, y
|
||||
for _i, _x, _y, _w, _h in view:each_item() do
|
||||
if _i == item then
|
||||
x = _x + _w / 2
|
||||
y = _y + _h / 2
|
||||
break
|
||||
end
|
||||
end
|
||||
menu:show(x, y)
|
||||
end
|
||||
})
|
||||
|
||||
|
||||
local function treeitem() return view.hovered_item or view.selected_item end
|
||||
|
||||
|
||||
command.add(
|
||||
function()
|
||||
local item = treeitem()
|
||||
return item ~= nil
|
||||
and (
|
||||
core.active_view == view or core.active_view == menu
|
||||
or (view.toolbar and core.active_view == view.toolbar)
|
||||
-- sometimes the context menu is shown on top of statusbar
|
||||
or core.active_view == core.status_view
|
||||
), item
|
||||
return item ~= nil and (core.active_view == view or menu.show_context_menu), item
|
||||
end, {
|
||||
["treeview:delete"] = function(item)
|
||||
local filename = item.abs_filename
|
||||
|
@ -685,8 +792,8 @@ command.add(
|
|||
local file_type = file_info.type == "dir" and "Directory" or "File"
|
||||
-- Ask before deleting
|
||||
local opt = {
|
||||
{ font = style.font, text = "Yes", default_yes = true },
|
||||
{ font = style.font, text = "No" , default_no = true }
|
||||
{ text = "Yes", default_yes = true },
|
||||
{ text = "No", default_no = true }
|
||||
}
|
||||
core.nag_view:show(
|
||||
string.format("Delete %s", file_type),
|
||||
|
@ -759,7 +866,7 @@ command.add(
|
|||
local file = io.open(doc_filename, "a+")
|
||||
file:write("")
|
||||
file:close()
|
||||
core.root_view:open_doc(core.open_doc(doc_filename))
|
||||
view:open_doc(doc_filename)
|
||||
core.log("Created %s", doc_filename)
|
||||
end,
|
||||
suggest = function(text)
|
||||
|
@ -800,7 +907,8 @@ command.add(
|
|||
local projectsearch = pcall(require, "plugins.projectsearch")
|
||||
if projectsearch then
|
||||
menu:register(function()
|
||||
return view.hovered_item and view.hovered_item.type == "dir"
|
||||
local item = treeitem()
|
||||
return item and item.type == "dir"
|
||||
end, {
|
||||
{ text = "Find in directory", command = "treeview:search-in-directory" }
|
||||
})
|
||||
|
@ -825,6 +933,25 @@ command.add(function()
|
|||
})
|
||||
|
||||
|
||||
command.add(
|
||||
function()
|
||||
return menu.show_context_menu == true and core.active_view:is(TreeView)
|
||||
end, {
|
||||
["treeview-context:focus-previous"] = function()
|
||||
menu:focus_previous()
|
||||
end,
|
||||
["treeview-context:focus-next"] = function()
|
||||
menu:focus_next()
|
||||
end,
|
||||
["treeview-context:hide"] = function()
|
||||
menu:hide()
|
||||
end,
|
||||
["treeview-context:on-selected"] = function()
|
||||
menu:call_selected_item()
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
keymap.add {
|
||||
["ctrl+\\"] = "treeview:toggle",
|
||||
["up"] = "treeview:previous",
|
||||
|
@ -840,6 +967,15 @@ keymap.add {
|
|||
["ctrl+lclick"] = "treeview:new-folder"
|
||||
}
|
||||
|
||||
keymap.add {
|
||||
["menu"] = "treeview-context:show",
|
||||
["return"] = "treeview-context:on-selected",
|
||||
["up"] = "treeview-context:focus-previous",
|
||||
["down"] = "treeview-context:focus-next",
|
||||
["escape"] = "treeview-context:hide"
|
||||
}
|
||||
|
||||
|
||||
-- The config specification used by gui generators
|
||||
config.plugins.treeview.config_spec = {
|
||||
name = "Treeview",
|
||||
|
|
|
@ -8,7 +8,7 @@ local Doc = require "core.doc"
|
|||
---@field enabled boolean
|
||||
---@field trim_empty_end_lines boolean
|
||||
config.plugins.trimwhitespace = common.merge({
|
||||
enabled = true,
|
||||
enabled = false,
|
||||
trim_empty_end_lines = false,
|
||||
config_spec = {
|
||||
name = "Trim Whitespace",
|
||||
|
@ -17,7 +17,7 @@ config.plugins.trimwhitespace = common.merge({
|
|||
description = "Disable or enable the trimming of white spaces by default.",
|
||||
path = "enabled",
|
||||
type = "toggle",
|
||||
default = true
|
||||
default = false
|
||||
},
|
||||
{
|
||||
label = "Trim Empty End Lines",
|
||||
|
|
|
@ -107,10 +107,12 @@ function system.set_window_bordered(bordered) end
|
|||
---When then window is run borderless (without system decorations), this
|
||||
---function allows to set the size of the different regions that allow
|
||||
---for custom window management.
|
||||
---To disable custom window management, call this function without any
|
||||
---arguments
|
||||
---
|
||||
---@param title_height number
|
||||
---@param controls_width number Width of window controls (maximize,minimize and close buttons, etc).
|
||||
---@param resize_border number The amount of pixels reserved for resizing
|
||||
---@param title_height? number Height of the window decoration
|
||||
---@param controls_width? number Width of window controls (maximize,minimize and close buttons, etc).
|
||||
---@param resize_border? number The amount of pixels reserved for resizing
|
||||
function system.set_window_hit_test(title_height, controls_width, resize_border) end
|
||||
|
||||
---
|
||||
|
@ -316,9 +318,11 @@ function system.load_native_plugin(name, path) end
|
|||
---Compares two paths in the order used by TreeView.
|
||||
---
|
||||
---@param path1 string
|
||||
---@param type1 system.fileinfotype
|
||||
---@param path2 string
|
||||
---@param type2 system.fileinfotype
|
||||
---@return boolean compare_result True if path1 < path2
|
||||
function system.path_compare(path1, path2) end
|
||||
function system.path_compare(path1, type1, path2, type2) end
|
||||
|
||||
|
||||
return system
|
||||
|
|
14
meson.build
14
meson.build
|
@ -36,6 +36,7 @@ conf_data = configuration_data()
|
|||
conf_data.set('PROJECT_BUILD_DIR', meson.current_build_dir())
|
||||
conf_data.set('PROJECT_SOURCE_DIR', meson.current_source_dir())
|
||||
conf_data.set('PROJECT_VERSION', version)
|
||||
conf_data.set('PROJECT_ASSEMBLY_VERSION', meson.project_version() + '.0')
|
||||
|
||||
#===============================================================================
|
||||
# Compiler Settings
|
||||
|
@ -63,10 +64,6 @@ lite_cargs += '-DLITE_ARCH_TUPLE="@0@"'.format(arch_tuple)
|
|||
# Linker Settings
|
||||
#===============================================================================
|
||||
lite_link_args = []
|
||||
if cc.get_id() == 'gcc' and get_option('buildtype') == 'release'
|
||||
lite_link_args += ['-static-libgcc']
|
||||
endif
|
||||
|
||||
if host_machine.system() == 'darwin'
|
||||
lite_link_args += ['-framework', 'CoreServices', '-framework', 'Foundation']
|
||||
endif
|
||||
|
@ -169,6 +166,11 @@ if get_option('portable') or host_machine.system() == 'windows'
|
|||
lite_bindir = '/'
|
||||
lite_docdir = '/doc'
|
||||
lite_datadir = '/data'
|
||||
configure_file(
|
||||
input: 'resources/windows/lite-xl.exe.manifest.in',
|
||||
output: 'lite-xl.exe.manifest',
|
||||
configuration: conf_data
|
||||
)
|
||||
elif get_option('bundle') and host_machine.system() == 'darwin'
|
||||
lite_cargs += '-DMACOS_USE_BUNDLE'
|
||||
lite_bindir = 'Contents/MacOS'
|
||||
|
@ -190,10 +192,10 @@ else
|
|||
install_data('resources/icons/lite-xl.svg',
|
||||
install_dir : 'share/icons/hicolor/scalable/apps'
|
||||
)
|
||||
install_data('resources/linux/org.lite_xl.lite_xl.desktop',
|
||||
install_data('resources/linux/com.lite_xl.LiteXL.desktop',
|
||||
install_dir : 'share/applications'
|
||||
)
|
||||
install_data('resources/linux/org.lite_xl.lite_xl.appdata.xml',
|
||||
install_data('resources/linux/com.lite_xl.LiteXL.appdata.xml',
|
||||
install_dir : 'share/metainfo'
|
||||
)
|
||||
endif
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
# Resources
|
||||
|
||||
This folder contains resources that is used for building or packaging the project.
|
||||
|
||||
### Build
|
||||
|
||||
- `cross/*.txt`: Meson [cross files][1] for cross-compiling lite-xl on other platforms.
|
||||
|
||||
### Packaging
|
||||
|
||||
- `icons/icon.{icns,ico,inl,rc,svg}`: lite-xl icon in various formats.
|
||||
- `linux/com.lite_xl.LiteXL.appdata.xml`: AppStream metadata.
|
||||
- `linux/com.lite_xl.LiteXL.desktop`: Desktop file for Linux desktops.
|
||||
- `macos/appdmg.png`: Background image for packaging MacOS DMGs.
|
||||
- `macos/Info.plist.in`: Template for generating `info.plist` on MacOS. See `macos/macos-retina-display.md` for details.
|
||||
- `windows/001-lua-unicode.diff`: Patch for allowing Lua to load files with UTF-8 filenames on Windows.
|
||||
|
||||
### Development
|
||||
|
||||
- `include/lite_xl_plugin_api.h`: Native plugin API header. See the contents of `lite_xl_plugin_api.h` for more details.
|
||||
|
||||
|
||||
[1]: https://mesonbuild.com/Cross-compilation.html
|
|
@ -1,3 +1,6 @@
|
|||
# cross file for compiling on MacOS ARM (Apple Sillicon).
|
||||
# use this file by running meson setup --cross-file resources/cross/macos_arm64.txt <builddir>
|
||||
|
||||
[host_machine]
|
||||
system = 'darwin'
|
||||
cpu_family = 'aarch64'
|
|
@ -0,0 +1,46 @@
|
|||
# cross file for WASM.
|
||||
# use this file by running meson setup --cross-file resources/cross/wasm.txt <builddir>
|
||||
|
||||
[constants]
|
||||
|
||||
# a list of functions that can run without being asyncified; proceed with caution
|
||||
asyncify_ignores = '["SDL_BlitScaled","SDL_UpperBlitScaled","SDL_MapRGB*","SDL_FillRect","SDL_FreeSurface","SDL_CreateRGBSurface","SDL_GetWindowSurface","SDL_PollEvent","SDL_CreateSystemCursor","SDL_SetWindowTitle","SDL_SetCursor","SDL_GetWindowSize","SDL_GetWindowPosition","lua_push*","lua_rawget*","luaL_check*","pcre2*","FT_*","Bezier_*","g_*","FT_*","ft_*","TT_*","tt_*","__*","*printf","gray_*","fopen","fclose","fread","fflush","qsort","sift"]'
|
||||
|
||||
# enable advising for optimizing the list above; disable this to prevent flooding logs
|
||||
asyncify_advise = '0'
|
||||
|
||||
# initial heap size in bytes; make sure it is not too low (around 64mb - 250mb)
|
||||
initial_heap = '104857600'
|
||||
|
||||
|
||||
[binaries]
|
||||
c = 'emcc'
|
||||
cpp = 'em++'
|
||||
ar = 'emar'
|
||||
strip = 'emstrip'
|
||||
cmake = ['emmake', 'cmake']
|
||||
pkg-config = ['emconfigure', 'pkg-config']
|
||||
sdl2-config = ['emconfigure', 'sdl2-config']
|
||||
|
||||
|
||||
[properties]
|
||||
needs_exe_wrapper = true
|
||||
|
||||
|
||||
[built-in options]
|
||||
c_args = []
|
||||
c_link_args = []
|
||||
cpp_args = []
|
||||
cpp_link_args = []
|
||||
|
||||
|
||||
[project options]
|
||||
buildtype = 'release'
|
||||
c_link_args = ['-s', 'ALLOW_MEMORY_GROWTH=1', '-s', 'INITIAL_MEMORY=' + initial_heap, '-s', 'ASYNCIFY=1', '-s', 'ASYNCIFY_ADVISE=' + asyncify_advise, '-s', 'ASYNCIFY_STACK_SIZE=6144', '-s', 'ASYNCIFY_REMOVE=' + asyncify_ignores, '-s', 'FORCE_FILESYSTEM=1']
|
||||
|
||||
|
||||
[host_machine]
|
||||
system = 'emscripten'
|
||||
cpu_family = 'wasm32'
|
||||
cpu = 'wasm32'
|
||||
endian = 'little'
|
File diff suppressed because it is too large
Load Diff
|
@ -1,12 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<component type="desktop">
|
||||
<id>org.lite_xl.lite_xl</id>
|
||||
<id>com.lite_xl.LiteXL</id>
|
||||
<metadata_license>MIT</metadata_license>
|
||||
<project_license>MIT</project_license>
|
||||
<name>Lite XL</name>
|
||||
<summary>A lightweight text editor written in Lua</summary>
|
||||
<content_rating type="oars-1.0" />
|
||||
<launchable type="desktop-id">org.lite_xl.lite_xl.desktop</launchable>
|
||||
<launchable type="desktop-id">com.lite_xl.LiteXL.desktop</launchable>
|
||||
|
||||
<description>
|
||||
<p>
|
|
@ -1,783 +0,0 @@
|
|||
#ifndef LITE_XL_PLUGIN_API
|
||||
#define LITE_XL_PLUGIN_API
|
||||
/**
|
||||
The lite_xl plugin API is quite simple. Any shared library can be a plugin file, so long
|
||||
as it has an entrypoint that looks like the following, where xxxxx is the plugin name:
|
||||
#include "lite_xl_plugin_api.h"
|
||||
int luaopen_lite_xl_xxxxx(lua_State* L, void* XL) {
|
||||
lite_xl_plugin_init(XL);
|
||||
...
|
||||
return 1;
|
||||
}
|
||||
In linux, to compile this file, you'd do: 'gcc -o xxxxx.so -shared xxxxx.c'. Simple!
|
||||
Due to the way the API is structured, you *should not* link or include lua libraries.
|
||||
This file was automatically generated. DO NOT MODIFY DIRECTLY.
|
||||
|
||||
UNLESS you're us, and you had to modify this file manually to get it ready for 2.1.
|
||||
|
||||
Go figure.
|
||||
|
||||
**/
|
||||
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h> // for BUFSIZ? this is kinda weird
|
||||
|
||||
/** luaconf.h **/
|
||||
|
||||
#ifndef lconfig_h
|
||||
#define lconfig_h
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
#if !defined(LUA_ANSI) && defined(__STRICT_ANSI__)
|
||||
#define LUA_ANSI
|
||||
#endif
|
||||
#if !defined(LUA_ANSI) && defined(_WIN32) && !defined(_WIN32_WCE)
|
||||
#define LUA_WIN
|
||||
#endif
|
||||
#if defined(LUA_WIN)
|
||||
#define LUA_DL_DLL
|
||||
#define LUA_USE_AFORMAT
|
||||
#endif
|
||||
#if defined(LUA_USE_LINUX)
|
||||
#define LUA_USE_POSIX
|
||||
#define LUA_USE_DLOPEN
|
||||
#define LUA_USE_READLINE
|
||||
#define LUA_USE_STRTODHEX
|
||||
#define LUA_USE_AFORMAT
|
||||
#define LUA_USE_LONGLONG
|
||||
#endif
|
||||
#if defined(LUA_USE_MACOSX)
|
||||
#define LUA_USE_POSIX
|
||||
#define LUA_USE_DLOPEN
|
||||
#define LUA_USE_READLINE
|
||||
#define LUA_USE_STRTODHEX
|
||||
#define LUA_USE_AFORMAT
|
||||
#define LUA_USE_LONGLONG
|
||||
#endif
|
||||
#if defined(LUA_USE_POSIX)
|
||||
#define LUA_USE_MKSTEMP
|
||||
#define LUA_USE_ISATTY
|
||||
#define LUA_USE_POPEN
|
||||
#define LUA_USE_ULONGJMP
|
||||
#define LUA_USE_GMTIME_R
|
||||
#endif
|
||||
#if defined(_WIN32)
|
||||
#define LUA_LDIR "!\\lua\\"
|
||||
#define LUA_CDIR "!\\"
|
||||
#define LUA_PATH_DEFAULT LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" LUA_CDIR"?.lua;" LUA_CDIR"?\\init.lua;" ".\\?.lua"
|
||||
#define LUA_CPATH_DEFAULT LUA_CDIR"?.dll;" LUA_CDIR"loadall.dll;" ".\\?.dll"
|
||||
#else
|
||||
#define LUA_VDIR LUA_VERSION_MAJOR "." LUA_VERSION_MINOR "/"
|
||||
#define LUA_ROOT "/usr/local/"
|
||||
#define LUA_LDIR LUA_ROOT "share/lua/" LUA_VDIR
|
||||
#define LUA_CDIR LUA_ROOT "lib/lua/" LUA_VDIR
|
||||
#define LUA_PATH_DEFAULT LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua;" "./?.lua"
|
||||
#define LUA_CPATH_DEFAULT LUA_CDIR"?.so;" LUA_CDIR"loadall.so;" "./?.so"
|
||||
#endif
|
||||
#if defined(_WIN32)
|
||||
#define LUA_DIRSEP "\\"
|
||||
#else
|
||||
#define LUA_DIRSEP "/"
|
||||
#endif
|
||||
#define LUA_ENV "_ENV"
|
||||
#if defined(LUA_BUILD_AS_DLL)
|
||||
#if defined(LUA_CORE) || defined(LUA_LIB)
|
||||
#define LUA_API __declspec(dllexport)
|
||||
#else
|
||||
#define LUA_API __declspec(dllimport)
|
||||
#endif
|
||||
#else
|
||||
#define LUA_API extern
|
||||
#endif
|
||||
#define LUALIB_API LUA_API
|
||||
#define LUAMOD_API LUALIB_API
|
||||
#if defined(__GNUC__) && ((__GNUC__*100 + __GNUC_MINOR__) >= 302) && defined(__ELF__)
|
||||
#define LUAI_FUNC __attribute__((visibility("hidden"))) extern
|
||||
#define LUAI_DDEC LUAI_FUNC
|
||||
#define LUAI_DDEF
|
||||
#else
|
||||
#define LUAI_FUNC extern
|
||||
#define LUAI_DDEC extern
|
||||
#define LUAI_DDEF
|
||||
#endif
|
||||
#define LUA_QL(x) "'" x "'"
|
||||
#define LUA_QS LUA_QL("%s")
|
||||
#define LUA_IDSIZE 60
|
||||
#if defined(LUA_LIB) || defined(lua_c)
|
||||
#include <stdio.h>
|
||||
#define luai_writestring(s,l) fwrite((s), sizeof(char), (l), stdout)
|
||||
#define luai_writeline() (luai_writestring("\n", 1), fflush(stdout))
|
||||
#endif
|
||||
#define luai_writestringerror(s,p) (fprintf(stderr, (s), (p)), fflush(stderr))
|
||||
#define LUAI_MAXSHORTLEN 40
|
||||
#if defined(LUA_COMPAT_ALL)
|
||||
#define LUA_COMPAT_UNPACK
|
||||
#define LUA_COMPAT_LOADERS
|
||||
#define lua_cpcall(L,f,u) (lua_pushcfunction(L, (f)), lua_pushlightuserdata(L,(u)), lua_pcall(L,1,0,0))
|
||||
#define LUA_COMPAT_LOG10
|
||||
#define LUA_COMPAT_LOADSTRING
|
||||
#define LUA_COMPAT_MAXN
|
||||
#define lua_strlen(L,i) lua_rawlen(L, (i))
|
||||
#define lua_objlen(L,i) lua_rawlen(L, (i))
|
||||
#define lua_equal(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPEQ)
|
||||
#define lua_lessthan(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPLT)
|
||||
#define LUA_COMPAT_MODULE
|
||||
#endif
|
||||
#if INT_MAX-20 < 32760
|
||||
#define LUAI_BITSINT 16
|
||||
#elif INT_MAX > 2147483640L
|
||||
#define LUAI_BITSINT 32
|
||||
#else
|
||||
#error "you must define LUA_BITSINT with number of bits in an integer"
|
||||
#endif
|
||||
#if LUAI_BITSINT >= 32
|
||||
#define LUA_INT32 int
|
||||
#define LUAI_UMEM size_t
|
||||
#define LUAI_MEM ptrdiff_t
|
||||
#else
|
||||
#define LUA_INT32 long
|
||||
#define LUAI_UMEM unsigned long
|
||||
#define LUAI_MEM long
|
||||
#endif
|
||||
#if LUAI_BITSINT >= 32
|
||||
#define LUAI_MAXSTACK 1000000
|
||||
#else
|
||||
#define LUAI_MAXSTACK 15000
|
||||
#endif
|
||||
#define LUAI_FIRSTPSEUDOIDX (-LUAI_MAXSTACK - 1000)
|
||||
#define LUAL_BUFFERSIZE BUFSIZ
|
||||
#define LUA_NUMBER_DOUBLE
|
||||
#define LUA_NUMBER double
|
||||
#define LUAI_UACNUMBER double
|
||||
#define LUA_NUMBER_SCAN "%lf"
|
||||
#define LUA_NUMBER_FMT "%.14g"
|
||||
#define lua_number2str(s,n) sprintf((s), LUA_NUMBER_FMT, (n))
|
||||
#define LUAI_MAXNUMBER2STR 32
|
||||
#define l_mathop(x) (x)
|
||||
#define lua_str2number(s,p) strtod((s), (p))
|
||||
#if defined(LUA_USE_STRTODHEX)
|
||||
#define lua_strx2number(s,p) strtod((s), (p))
|
||||
#endif
|
||||
#if defined(lobject_c) || defined(lvm_c)
|
||||
#include <math.h>
|
||||
#define luai_nummod(L,a,b) ((a) - l_mathop(floor)((a)/(b))*(b))
|
||||
#define luai_numpow(L,a,b) (l_mathop(pow)(a,b))
|
||||
#endif
|
||||
#if defined(LUA_CORE)
|
||||
#define luai_numadd(L,a,b) ((a)+(b))
|
||||
#define luai_numsub(L,a,b) ((a)-(b))
|
||||
#define luai_nummul(L,a,b) ((a)*(b))
|
||||
#define luai_numdiv(L,a,b) ((a)/(b))
|
||||
#define luai_numunm(L,a) (-(a))
|
||||
#define luai_numeq(a,b) ((a)==(b))
|
||||
#define luai_numlt(L,a,b) ((a)<(b))
|
||||
#define luai_numle(L,a,b) ((a)<=(b))
|
||||
#define luai_numisnan(L,a) (!luai_numeq((a), (a)))
|
||||
#endif
|
||||
#define LUA_INTEGER ptrdiff_t
|
||||
#define LUA_UNSIGNED unsigned LUA_INT32
|
||||
#if defined(LUA_NUMBER_DOUBLE) && !defined(LUA_ANSI)
|
||||
#if defined(LUA_WIN) && defined(_MSC_VER) && defined(_M_IX86)
|
||||
#define LUA_MSASMTRICK
|
||||
#define LUA_IEEEENDIAN 0
|
||||
#define LUA_NANTRICK
|
||||
#elif defined(__i386__) || defined(__i386) || defined(__X86__)
|
||||
#define LUA_IEEE754TRICK
|
||||
#define LUA_IEEELL
|
||||
#define LUA_IEEEENDIAN 0
|
||||
#define LUA_NANTRICK
|
||||
#elif defined(__x86_64)
|
||||
#define LUA_IEEE754TRICK
|
||||
#define LUA_IEEEENDIAN 0
|
||||
#elif defined(__POWERPC__) || defined(__ppc__)
|
||||
#define LUA_IEEE754TRICK
|
||||
#define LUA_IEEEENDIAN 1
|
||||
#else
|
||||
#define LUA_IEEE754TRICK
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/** lua.h **/
|
||||
|
||||
typedef struct lua_State lua_State;
|
||||
typedef int (*lua_CFunction) (lua_State *L);
|
||||
typedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz);
|
||||
typedef int (*lua_Writer) (lua_State *L, const void* p, size_t sz, void* ud);
|
||||
typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);
|
||||
typedef LUA_NUMBER lua_Number;
|
||||
typedef LUA_INTEGER lua_Integer;
|
||||
typedef LUA_UNSIGNED lua_Unsigned;
|
||||
typedef struct lua_Debug lua_Debug;
|
||||
typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);
|
||||
struct lua_Debug {
|
||||
int event;
|
||||
const char *name;
|
||||
const char *namewhat;
|
||||
const char *what;
|
||||
const char *source;
|
||||
int currentline;
|
||||
int linedefined;
|
||||
int lastlinedefined;
|
||||
unsigned char nups;
|
||||
unsigned char nparams;
|
||||
char isvararg;
|
||||
char istailcall;
|
||||
char short_src[LUA_IDSIZE];
|
||||
struct CallInfo *i_ci;
|
||||
};
|
||||
static lua_State *(*lua_newstate) (lua_Alloc f, void *ud);
|
||||
static void (*lua_close) (lua_State *L);
|
||||
static lua_State *(*lua_newthread) (lua_State *L);
|
||||
static lua_CFunction (*lua_atpanic) (lua_State *L, lua_CFunction panicf);
|
||||
static const lua_Number *(*lua_version) (lua_State *L);
|
||||
static int (*lua_absindex) (lua_State *L, int idx);
|
||||
static int (*lua_gettop) (lua_State *L);
|
||||
static void (*lua_settop) (lua_State *L, int idx);
|
||||
static void (*lua_pushvalue) (lua_State *L, int idx);
|
||||
static void (*lua_copy) (lua_State *L, int fromidx, int toidx);
|
||||
static int (*lua_checkstack) (lua_State *L, int sz);
|
||||
static void (*lua_xmove) (lua_State *from, lua_State *to, int n);
|
||||
static int (*lua_isnumber) (lua_State *L, int idx);
|
||||
static int (*lua_isstring) (lua_State *L, int idx);
|
||||
static int (*lua_iscfunction) (lua_State *L, int idx);
|
||||
static int (*lua_isuserdata) (lua_State *L, int idx);
|
||||
static int (*lua_type) (lua_State *L, int idx);
|
||||
static const char *(*lua_typename) (lua_State *L, int tp);
|
||||
static lua_Number (*lua_tonumberx) (lua_State *L, int idx, int *isnum);
|
||||
static lua_Integer (*lua_tointegerx) (lua_State *L, int idx, int *isnum);
|
||||
static lua_Unsigned (*lua_tounsignedx) (lua_State *L, int idx, int *isnum);
|
||||
static int (*lua_toboolean) (lua_State *L, int idx);
|
||||
static const char *(*lua_tolstring) (lua_State *L, int idx, size_t *len);
|
||||
static size_t (*lua_rawlen) (lua_State *L, int idx);
|
||||
static lua_CFunction (*lua_tocfunction) (lua_State *L, int idx);
|
||||
static void *(*lua_touserdata) (lua_State *L, int idx);
|
||||
static lua_State *(*lua_tothread) (lua_State *L, int idx);
|
||||
static const void *(*lua_topointer) (lua_State *L, int idx);
|
||||
static void (*lua_arith) (lua_State *L, int op);
|
||||
static int (*lua_rawequal) (lua_State *L, int idx1, int idx2);
|
||||
static int (*lua_compare) (lua_State *L, int idx1, int idx2, int op);
|
||||
static void (*lua_pushnil) (lua_State *L);
|
||||
static void (*lua_pushnumber) (lua_State *L, lua_Number n);
|
||||
static void (*lua_pushinteger) (lua_State *L, lua_Integer n);
|
||||
static void (*lua_pushunsigned) (lua_State *L, lua_Unsigned n);
|
||||
static const char *(*lua_pushlstring) (lua_State *L, const char *s, size_t l);
|
||||
static const char *(*lua_pushstring) (lua_State *L, const char *s);
|
||||
static const char *(*lua_pushvfstring) (lua_State *L, const char *fmt, va_list argp);
|
||||
static const char *(*lua_pushfstring) (lua_State *L, const char *fmt, ...);
|
||||
static void (*lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n);
|
||||
static void (*lua_pushboolean) (lua_State *L, int b);
|
||||
static void (*lua_pushlightuserdata) (lua_State *L, void *p);
|
||||
static int (*lua_pushthread) (lua_State *L);
|
||||
static void (*lua_getglobal) (lua_State *L, const char *var);
|
||||
static void (*lua_gettable) (lua_State *L, int idx);
|
||||
static void (*lua_getfield) (lua_State *L, int idx, const char *k);
|
||||
static void (*lua_rawget) (lua_State *L, int idx);
|
||||
static void (*lua_rawgeti) (lua_State *L, int idx, int n);
|
||||
static void (*lua_rawgetp) (lua_State *L, int idx, const void *p);
|
||||
static void (*lua_createtable) (lua_State *L, int narr, int nrec);
|
||||
static void *(*lua_newuserdata) (lua_State *L, size_t sz);
|
||||
static void *(*lua_newuserdatauv) (lua_State *L, size_t sz, int nuvalue);
|
||||
static int (*lua_getmetatable) (lua_State *L, int objindex);
|
||||
static void (*lua_getuservalue) (lua_State *L, int idx);
|
||||
static void (*lua_getiuservalue) (lua_State *L, int idx, int n);
|
||||
static void (*lua_setglobal) (lua_State *L, const char *var);
|
||||
static void (*lua_settable) (lua_State *L, int idx);
|
||||
static void (*lua_setfield) (lua_State *L, int idx, const char *k);
|
||||
static void (*lua_rawset) (lua_State *L, int idx);
|
||||
static void (*lua_rawseti) (lua_State *L, int idx, int n);
|
||||
static void (*lua_rawsetp) (lua_State *L, int idx, const void *p);
|
||||
static int (*lua_setmetatable) (lua_State *L, int objindex);
|
||||
static void (*lua_setuservalue) (lua_State *L, int idx);
|
||||
static void (*lua_setiuservalue) (lua_State *L, int idx, int n);
|
||||
static void (*lua_callk) (lua_State *L, int nargs, int nresults, int ctx, lua_CFunction k);
|
||||
static int (*lua_getctx) (lua_State *L, int *ctx);
|
||||
static int (*lua_pcallk) (lua_State *L, int nargs, int nresults, int errfunc, int ctx, lua_CFunction k);
|
||||
static int (*lua_load) (lua_State *L, lua_Reader reader, void *dt, const char *chunkname, const char *mode);
|
||||
static int (*lua_dump) (lua_State *L, lua_Writer writer, void *data, int strip);
|
||||
static int (*lua_yieldk) (lua_State *L, int nresults, int ctx, lua_CFunction k);
|
||||
static int (*lua_resume) (lua_State *L, lua_State *from, int narg);
|
||||
static int (*lua_status) (lua_State *L);
|
||||
static int (*lua_gc) (lua_State *L, int what, int data);
|
||||
static int (*lua_error) (lua_State *L);
|
||||
static int (*lua_next) (lua_State *L, int idx);
|
||||
static void (*lua_concat) (lua_State *L, int n);
|
||||
static void (*lua_len) (lua_State *L, int idx);
|
||||
static lua_Alloc (*lua_getallocf) (lua_State *L, void **ud);
|
||||
static void (*lua_setallocf) (lua_State *L, lua_Alloc f, void *ud);
|
||||
static int (*lua_getstack) (lua_State *L, int level, lua_Debug *ar);
|
||||
static int (*lua_getinfo) (lua_State *L, const char *what, lua_Debug *ar);
|
||||
static const char *(*lua_getlocal) (lua_State *L, const lua_Debug *ar, int n);
|
||||
static const char *(*lua_setlocal) (lua_State *L, const lua_Debug *ar, int n);
|
||||
static const char *(*lua_getupvalue) (lua_State *L, int funcindex, int n);
|
||||
static const char *(*lua_setupvalue) (lua_State *L, int funcindex, int n);
|
||||
static void *(*lua_upvalueid) (lua_State *L, int fidx, int n);
|
||||
static void (*lua_upvaluejoin) (lua_State *L, int fidx1, int n1, int fidx2, int n2);
|
||||
static int (*lua_sethook) (lua_State *L, lua_Hook func, int mask, int count);
|
||||
static lua_Hook (*lua_gethook) (lua_State *L);
|
||||
static int (*lua_gethookmask) (lua_State *L);
|
||||
static int (*lua_gethookcount) (lua_State *L);
|
||||
#define lua_h
|
||||
#define LUA_VERSION_MAJOR "5"
|
||||
#define LUA_VERSION_MINOR "2"
|
||||
#define LUA_VERSION_NUM 502
|
||||
#define LUA_VERSION_RELEASE "4"
|
||||
#define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR
|
||||
#define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE
|
||||
#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2015 Lua.org, PUC-Rio"
|
||||
#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes"
|
||||
#define LUA_SIGNATURE "\033Lua"
|
||||
#define LUA_MULTRET (-1)
|
||||
#define LUA_REGISTRYINDEX LUAI_FIRSTPSEUDOIDX
|
||||
#define lua_upvalueindex(i) (LUA_REGISTRYINDEX - (i))
|
||||
#define LUA_OK 0
|
||||
#define LUA_YIELD 1
|
||||
#define LUA_ERRRUN 2
|
||||
#define LUA_ERRSYNTAX 3
|
||||
#define LUA_ERRMEM 4
|
||||
#define LUA_ERRGCMM 5
|
||||
#define LUA_ERRERR 6
|
||||
#define LUA_TNONE (-1)
|
||||
#define LUA_TNIL 0
|
||||
#define LUA_TBOOLEAN 1
|
||||
#define LUA_TLIGHTUSERDATA 2
|
||||
#define LUA_TNUMBER 3
|
||||
#define LUA_TSTRING 4
|
||||
#define LUA_TTABLE 5
|
||||
#define LUA_TFUNCTION 6
|
||||
#define LUA_TUSERDATA 7
|
||||
#define LUA_TTHREAD 8
|
||||
#define LUA_NUMTAGS 9
|
||||
#define LUA_MINSTACK 20
|
||||
#define LUA_RIDX_MAINTHREAD 1
|
||||
#define LUA_RIDX_GLOBALS 2
|
||||
#define LUA_RIDX_LAST LUA_RIDX_GLOBALS
|
||||
#define LUA_OPADD 0
|
||||
#define LUA_OPSUB 1
|
||||
#define LUA_OPMUL 2
|
||||
#define LUA_OPDIV 3
|
||||
#define LUA_OPMOD 4
|
||||
#define LUA_OPPOW 5
|
||||
#define LUA_OPUNM 6
|
||||
#define LUA_OPEQ 0
|
||||
#define LUA_OPLT 1
|
||||
#define LUA_OPLE 2
|
||||
#define lua_call(L,n,r) lua_callk(L, (n), (r), 0, NULL)
|
||||
#define lua_pcall(L,n,r,f) lua_pcallk(L, (n), (r), (f), 0, NULL)
|
||||
#define lua_yield(L,n) lua_yieldk(L, (n), 0, NULL)
|
||||
#define LUA_GCSTOP 0
|
||||
#define LUA_GCRESTART 1
|
||||
#define LUA_GCCOLLECT 2
|
||||
#define LUA_GCCOUNT 3
|
||||
#define LUA_GCCOUNTB 4
|
||||
#define LUA_GCSTEP 5
|
||||
#define LUA_GCSETPAUSE 6
|
||||
#define LUA_GCSETSTEPMUL 7
|
||||
#define LUA_GCSETMAJORINC 8
|
||||
#define LUA_GCISRUNNING 9
|
||||
#define LUA_GCGEN 10
|
||||
#define LUA_GCINC 11
|
||||
#define lua_tonumber(L,i) lua_tonumberx(L,i,NULL)
|
||||
#define lua_tointeger(L,i) lua_tointegerx(L,i,NULL)
|
||||
#define lua_tounsigned(L,i) lua_tounsignedx(L,i,NULL)
|
||||
#define lua_pop(L,n) lua_settop(L, -(n)-1)
|
||||
#define lua_newtable(L) lua_createtable(L, 0, 0)
|
||||
#define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n)))
|
||||
#define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0)
|
||||
#define lua_isfunction(L,n) (lua_type(L, (n)) == LUA_TFUNCTION)
|
||||
#define lua_istable(L,n) (lua_type(L, (n)) == LUA_TTABLE)
|
||||
#define lua_islightuserdata(L,n) (lua_type(L, (n)) == LUA_TLIGHTUSERDATA)
|
||||
#define lua_isnil(L,n) (lua_type(L, (n)) == LUA_TNIL)
|
||||
#define lua_isboolean(L,n) (lua_type(L, (n)) == LUA_TBOOLEAN)
|
||||
#define lua_isthread(L,n) (lua_type(L, (n)) == LUA_TTHREAD)
|
||||
#define lua_isnone(L,n) (lua_type(L, (n)) == LUA_TNONE)
|
||||
#define lua_isnoneornil(L, n) (lua_type(L, (n)) <= 0)
|
||||
#define lua_pushliteral(L, s) lua_pushlstring(L, "" s, (sizeof(s)/sizeof(char))-1)
|
||||
#define lua_pushglobaltable(L) lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS)
|
||||
#define lua_tostring(L,i) lua_tolstring(L, (i), NULL)
|
||||
#define lua_insert(L,idx) lua_rotate(L, (idx), 1)
|
||||
#define lua_replace(L,idx) (lua_copy(L, -1, (idx)), lua_pop(L, 1))
|
||||
#define lua_remove(L,idx) (lua_rotate(L, (idx), -1), lua_pop(L, 1))
|
||||
#define LUA_HOOKCALL 0
|
||||
#define LUA_HOOKRET 1
|
||||
#define LUA_HOOKLINE 2
|
||||
#define LUA_HOOKCOUNT 3
|
||||
#define LUA_HOOKTAILCALL 4
|
||||
#define LUA_MASKCALL (1 << LUA_HOOKCALL)
|
||||
#define LUA_MASKRET (1 << LUA_HOOKRET)
|
||||
#define LUA_MASKLINE (1 << LUA_HOOKLINE)
|
||||
#define LUA_MASKCOUNT (1 << LUA_HOOKCOUNT)
|
||||
static lua_State * __lite_xl_fallback_lua_newstate (lua_Alloc f, void *ud) { fputs("warning: lua_newstate is a stub", stderr); }
|
||||
static void __lite_xl_fallback_lua_close (lua_State *L) { fputs("warning: lua_close is a stub", stderr); }
|
||||
static lua_State * __lite_xl_fallback_lua_newthread (lua_State *L) { fputs("warning: lua_newthread is a stub", stderr); }
|
||||
static lua_CFunction __lite_xl_fallback_lua_atpanic (lua_State *L, lua_CFunction panicf) { fputs("warning: lua_atpanic is a stub", stderr); }
|
||||
static const lua_Number * __lite_xl_fallback_lua_version (lua_State *L) { fputs("warning: lua_version is a stub", stderr); }
|
||||
static int __lite_xl_fallback_lua_absindex (lua_State *L, int idx) { fputs("warning: lua_absindex is a stub", stderr); }
|
||||
static int __lite_xl_fallback_lua_gettop (lua_State *L) { fputs("warning: lua_gettop is a stub", stderr); }
|
||||
static void __lite_xl_fallback_lua_settop (lua_State *L, int idx) { fputs("warning: lua_settop is a stub", stderr); }
|
||||
static void __lite_xl_fallback_lua_pushvalue (lua_State *L, int idx) { fputs("warning: lua_pushvalue is a stub", stderr); }
|
||||
static void __lite_xl_fallback_lua_copy (lua_State *L, int fromidx, int toidx) { fputs("warning: lua_copy is a stub", stderr); }
|
||||
static int __lite_xl_fallback_lua_checkstack (lua_State *L, int sz) { fputs("warning: lua_checkstack is a stub", stderr); }
|
||||
static void __lite_xl_fallback_lua_xmove (lua_State *from, lua_State *to, int n) { fputs("warning: lua_xmove is a stub", stderr); }
|
||||
static int __lite_xl_fallback_lua_isnumber (lua_State *L, int idx) { fputs("warning: lua_isnumber is a stub", stderr); }
|
||||
static int __lite_xl_fallback_lua_isstring (lua_State *L, int idx) { fputs("warning: lua_isstring is a stub", stderr); }
|
||||
static int __lite_xl_fallback_lua_iscfunction (lua_State *L, int idx) { fputs("warning: lua_iscfunction is a stub", stderr); }
|
||||
static int __lite_xl_fallback_lua_isuserdata (lua_State *L, int idx) { fputs("warning: lua_isuserdata is a stub", stderr); }
|
||||
static int __lite_xl_fallback_lua_type (lua_State *L, int idx) { fputs("warning: lua_type is a stub", stderr); }
|
||||
static const char * __lite_xl_fallback_lua_typename (lua_State *L, int tp) { fputs("warning: lua_typename is a stub", stderr); }
|
||||
static lua_Number __lite_xl_fallback_lua_tonumberx (lua_State *L, int idx, int *isnum) { fputs("warning: lua_tonumberx is a stub", stderr); }
|
||||
static lua_Integer __lite_xl_fallback_lua_tointegerx (lua_State *L, int idx, int *isnum) { fputs("warning: lua_tointegerx is a stub", stderr); }
|
||||
static lua_Unsigned __lite_xl_fallback_lua_tounsignedx (lua_State *L, int idx, int *isnum) { fputs("warning: lua_tounsignedx is a stub", stderr); }
|
||||
static int __lite_xl_fallback_lua_toboolean (lua_State *L, int idx) { fputs("warning: lua_toboolean is a stub", stderr); }
|
||||
static const char * __lite_xl_fallback_lua_tolstring (lua_State *L, int idx, size_t *len) { fputs("warning: lua_tolstring is a stub", stderr); }
|
||||
static size_t __lite_xl_fallback_lua_rawlen (lua_State *L, int idx) { fputs("warning: lua_rawlen is a stub", stderr); }
|
||||
static lua_CFunction __lite_xl_fallback_lua_tocfunction (lua_State *L, int idx) { fputs("warning: lua_tocfunction is a stub", stderr); }
|
||||
static void * __lite_xl_fallback_lua_touserdata (lua_State *L, int idx) { fputs("warning: lua_touserdata is a stub", stderr); }
|
||||
static lua_State * __lite_xl_fallback_lua_tothread (lua_State *L, int idx) { fputs("warning: lua_tothread is a stub", stderr); }
|
||||
static const void * __lite_xl_fallback_lua_topointer (lua_State *L, int idx) { fputs("warning: lua_topointer is a stub", stderr); }
|
||||
static void __lite_xl_fallback_lua_arith (lua_State *L, int op) { fputs("warning: lua_arith is a stub", stderr); }
|
||||
static int __lite_xl_fallback_lua_rawequal (lua_State *L, int idx1, int idx2) { fputs("warning: lua_rawequal is a stub", stderr); }
|
||||
static int __lite_xl_fallback_lua_compare (lua_State *L, int idx1, int idx2, int op) { fputs("warning: lua_compare is a stub", stderr); }
|
||||
static void __lite_xl_fallback_lua_pushnil (lua_State *L) { fputs("warning: lua_pushnil is a stub", stderr); }
|
||||
static void __lite_xl_fallback_lua_pushnumber (lua_State *L, lua_Number n) { fputs("warning: lua_pushnumber is a stub", stderr); }
|
||||
static void __lite_xl_fallback_lua_pushinteger (lua_State *L, lua_Integer n) { fputs("warning: lua_pushinteger is a stub", stderr); }
|
||||
static void __lite_xl_fallback_lua_pushunsigned (lua_State *L, lua_Unsigned n) { fputs("warning: lua_pushunsigned is a stub", stderr); }
|
||||
static const char * __lite_xl_fallback_lua_pushlstring (lua_State *L, const char *s, size_t l) { fputs("warning: lua_pushlstring is a stub", stderr); }
|
||||
static const char * __lite_xl_fallback_lua_pushstring (lua_State *L, const char *s) { fputs("warning: lua_pushstring is a stub", stderr); }
|
||||
static const char * __lite_xl_fallback_lua_pushvfstring (lua_State *L, const char *fmt, va_list argp) { fputs("warning: lua_pushvfstring is a stub", stderr); }
|
||||
static const char * __lite_xl_fallback_lua_pushfstring (lua_State *L, const char *fmt, ...) { fputs("warning: lua_pushfstring is a stub", stderr); }
|
||||
static void __lite_xl_fallback_lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) { fputs("warning: lua_pushcclosure is a stub", stderr); }
|
||||
static void __lite_xl_fallback_lua_pushboolean (lua_State *L, int b) { fputs("warning: lua_pushboolean is a stub", stderr); }
|
||||
static void __lite_xl_fallback_lua_pushlightuserdata (lua_State *L, void *p) { fputs("warning: lua_pushlightuserdata is a stub", stderr); }
|
||||
static int __lite_xl_fallback_lua_pushthread (lua_State *L) { fputs("warning: lua_pushthread is a stub", stderr); }
|
||||
static void __lite_xl_fallback_lua_getglobal (lua_State *L, const char *var) { fputs("warning: lua_getglobal is a stub", stderr); }
|
||||
static void __lite_xl_fallback_lua_gettable (lua_State *L, int idx) { fputs("warning: lua_gettable is a stub", stderr); }
|
||||
static void __lite_xl_fallback_lua_getfield (lua_State *L, int idx, const char *k) { fputs("warning: lua_getfield is a stub", stderr); }
|
||||
static void __lite_xl_fallback_lua_rawget (lua_State *L, int idx) { fputs("warning: lua_rawget is a stub", stderr); }
|
||||
static void __lite_xl_fallback_lua_rawgeti (lua_State *L, int idx, int n) { fputs("warning: lua_rawgeti is a stub", stderr); }
|
||||
static void __lite_xl_fallback_lua_rawgetp (lua_State *L, int idx, const void *p) { fputs("warning: lua_rawgetp is a stub", stderr); }
|
||||
static void __lite_xl_fallback_lua_createtable (lua_State *L, int narr, int nrec) { fputs("warning: lua_createtable is a stub", stderr); }
|
||||
static void * __lite_xl_fallback_lua_newuserdata (lua_State *L, size_t sz) { fputs("warning: lua_newuserdata is a stub", stderr); }
|
||||
static void * __lite_xl_fallback_lua_newuserdatauv (lua_State *L, size_t sz, int nuvalue) { fputs("warning: lua_newuserdatauv is a stub", stderr); }
|
||||
static int __lite_xl_fallback_lua_getmetatable (lua_State *L, int objindex) { fputs("warning: lua_getmetatable is a stub", stderr); }
|
||||
static void __lite_xl_fallback_lua_getuservalue (lua_State *L, int idx) { fputs("warning: lua_getuservalue is a stub", stderr); }
|
||||
static void __lite_xl_fallback_lua_getiuservalue (lua_State *L, int idx, int n) { fputs("warning: lua_getiuservalue is a stub", stderr); }
|
||||
static void __lite_xl_fallback_lua_setglobal (lua_State *L, const char *var) { fputs("warning: lua_setglobal is a stub", stderr); }
|
||||
static void __lite_xl_fallback_lua_settable (lua_State *L, int idx) { fputs("warning: lua_settable is a stub", stderr); }
|
||||
static void __lite_xl_fallback_lua_setfield (lua_State *L, int idx, const char *k) { fputs("warning: lua_setfield is a stub", stderr); }
|
||||
static void __lite_xl_fallback_lua_rawset (lua_State *L, int idx) { fputs("warning: lua_rawset is a stub", stderr); }
|
||||
static void __lite_xl_fallback_lua_rawseti (lua_State *L, int idx, int n) { fputs("warning: lua_rawseti is a stub", stderr); }
|
||||
static void __lite_xl_fallback_lua_rawsetp (lua_State *L, int idx, const void *p) { fputs("warning: lua_rawsetp is a stub", stderr); }
|
||||
static int __lite_xl_fallback_lua_setmetatable (lua_State *L, int objindex) { fputs("warning: lua_setmetatable is a stub", stderr); }
|
||||
static void __lite_xl_fallback_lua_setuservalue (lua_State *L, int idx) { fputs("warning: lua_setuservalue is a stub", stderr); }
|
||||
static void __lite_xl_fallback_lua_setiuservalue (lua_State *L, int idx, int n) { fputs("warning: lua_setiuservalue is a stub", stderr); }
|
||||
static void __lite_xl_fallback_lua_callk (lua_State *L, int nargs, int nresults, int ctx, lua_CFunction k) { fputs("warning: lua_callk is a stub", stderr); }
|
||||
static int __lite_xl_fallback_lua_getctx (lua_State *L, int *ctx) { fputs("warning: lua_getctx is a stub", stderr); }
|
||||
static int __lite_xl_fallback_lua_pcallk (lua_State *L, int nargs, int nresults, int errfunc, int ctx, lua_CFunction k) { fputs("warning: lua_pcallk is a stub", stderr); }
|
||||
static int __lite_xl_fallback_lua_load (lua_State *L, lua_Reader reader, void *dt, const char *chunkname, const char *mode) { fputs("warning: lua_load is a stub", stderr); }
|
||||
static int __lite_xl_fallback_lua_dump (lua_State *L, lua_Writer writer, void *data, int strip) { fputs("warning: lua_dump is a stub", stderr); }
|
||||
static int __lite_xl_fallback_lua_yieldk (lua_State *L, int nresults, int ctx, lua_CFunction k) { fputs("warning: lua_yieldk is a stub", stderr); }
|
||||
static int __lite_xl_fallback_lua_resume (lua_State *L, lua_State *from, int narg) { fputs("warning: lua_resume is a stub", stderr); }
|
||||
static int __lite_xl_fallback_lua_status (lua_State *L) { fputs("warning: lua_status is a stub", stderr); }
|
||||
static int __lite_xl_fallback_lua_gc (lua_State *L, int what, int data) { fputs("warning: lua_gc is a stub", stderr); }
|
||||
static int __lite_xl_fallback_lua_error (lua_State *L) { fputs("warning: lua_error is a stub", stderr); }
|
||||
static int __lite_xl_fallback_lua_next (lua_State *L, int idx) { fputs("warning: lua_next is a stub", stderr); }
|
||||
static void __lite_xl_fallback_lua_concat (lua_State *L, int n) { fputs("warning: lua_concat is a stub", stderr); }
|
||||
static void __lite_xl_fallback_lua_len (lua_State *L, int idx) { fputs("warning: lua_len is a stub", stderr); }
|
||||
static lua_Alloc __lite_xl_fallback_lua_getallocf (lua_State *L, void **ud) { fputs("warning: lua_getallocf is a stub", stderr); }
|
||||
static void __lite_xl_fallback_lua_setallocf (lua_State *L, lua_Alloc f, void *ud) { fputs("warning: lua_setallocf is a stub", stderr); }
|
||||
static int __lite_xl_fallback_lua_getstack (lua_State *L, int level, lua_Debug *ar) { fputs("warning: lua_getstack is a stub", stderr); }
|
||||
static int __lite_xl_fallback_lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) { fputs("warning: lua_getinfo is a stub", stderr); }
|
||||
static const char * __lite_xl_fallback_lua_getlocal (lua_State *L, const lua_Debug *ar, int n) { fputs("warning: lua_getlocal is a stub", stderr); }
|
||||
static const char * __lite_xl_fallback_lua_setlocal (lua_State *L, const lua_Debug *ar, int n) { fputs("warning: lua_setlocal is a stub", stderr); }
|
||||
static const char * __lite_xl_fallback_lua_getupvalue (lua_State *L, int funcindex, int n) { fputs("warning: lua_getupvalue is a stub", stderr); }
|
||||
static const char * __lite_xl_fallback_lua_setupvalue (lua_State *L, int funcindex, int n) { fputs("warning: lua_setupvalue is a stub", stderr); }
|
||||
static void * __lite_xl_fallback_lua_upvalueid (lua_State *L, int fidx, int n) { fputs("warning: lua_upvalueid is a stub", stderr); }
|
||||
static void __lite_xl_fallback_lua_upvaluejoin (lua_State *L, int fidx1, int n1, int fidx2, int n2) { fputs("warning: lua_upvaluejoin is a stub", stderr); }
|
||||
static int __lite_xl_fallback_lua_sethook (lua_State *L, lua_Hook func, int mask, int count) { fputs("warning: lua_sethook is a stub", stderr); }
|
||||
static lua_Hook __lite_xl_fallback_lua_gethook (lua_State *L) { fputs("warning: lua_gethook is a stub", stderr); }
|
||||
static int __lite_xl_fallback_lua_gethookmask (lua_State *L) { fputs("warning: lua_gethookmask is a stub", stderr); }
|
||||
static int __lite_xl_fallback_lua_gethookcount (lua_State *L) { fputs("warning: lua_gethookcount is a stub", stderr); }
|
||||
|
||||
|
||||
/** lauxlib.h **/
|
||||
|
||||
typedef struct luaL_Reg {
|
||||
const char *name;
|
||||
lua_CFunction func;
|
||||
} luaL_Reg;
|
||||
typedef struct luaL_Buffer {
|
||||
char *b;
|
||||
size_t size;
|
||||
size_t n;
|
||||
lua_State *L;
|
||||
char initb[LUAL_BUFFERSIZE];
|
||||
} luaL_Buffer;
|
||||
typedef struct luaL_Stream {
|
||||
FILE *f;
|
||||
lua_CFunction closef;
|
||||
} luaL_Stream;
|
||||
static void (*luaL_checkversion_) (lua_State *L, lua_Number ver);
|
||||
static int (*luaL_getmetafield) (lua_State *L, int obj, const char *e);
|
||||
static int (*luaL_callmeta) (lua_State *L, int obj, const char *e);
|
||||
static const char *(*luaL_tolstring) (lua_State *L, int idx, size_t *len);
|
||||
static int (*luaL_argerror) (lua_State *L, int numarg, const char *extramsg);
|
||||
static const char *(*luaL_checklstring) (lua_State *L, int numArg, size_t *l);
|
||||
static const char *(*luaL_optlstring) (lua_State *L, int numArg, const char *def, size_t *l);
|
||||
static lua_Number (*luaL_checknumber) (lua_State *L, int numArg);
|
||||
static lua_Number (*luaL_optnumber) (lua_State *L, int nArg, lua_Number def);
|
||||
static lua_Integer (*luaL_checkinteger) (lua_State *L, int numArg);
|
||||
static lua_Integer (*luaL_optinteger) (lua_State *L, int nArg, lua_Integer def);
|
||||
static lua_Unsigned (*luaL_checkunsigned) (lua_State *L, int numArg);
|
||||
static lua_Unsigned (*luaL_optunsigned) (lua_State *L, int numArg, lua_Unsigned def);
|
||||
static void (*luaL_checkstack) (lua_State *L, int sz, const char *msg);
|
||||
static void (*luaL_checktype) (lua_State *L, int narg, int t);
|
||||
static void (*luaL_checkany) (lua_State *L, int narg);
|
||||
static int (*luaL_newmetatable) (lua_State *L, const char *tname);
|
||||
static void (*luaL_setmetatable) (lua_State *L, const char *tname);
|
||||
static void *(*luaL_testudata) (lua_State *L, int ud, const char *tname);
|
||||
static void *(*luaL_checkudata) (lua_State *L, int ud, const char *tname);
|
||||
static void (*luaL_where) (lua_State *L, int lvl);
|
||||
static int (*luaL_error) (lua_State *L, const char *fmt, ...);
|
||||
static int (*luaL_typeerror) (lua_State *L, int narg, const char *tname);
|
||||
static int (*luaL_checkoption) (lua_State *L, int narg, const char *def, const char *const lst[]);
|
||||
static int (*luaL_fileresult) (lua_State *L, int stat, const char *fname);
|
||||
static int (*luaL_execresult) (lua_State *L, int stat);
|
||||
static int (*luaL_ref) (lua_State *L, int t);
|
||||
static void (*luaL_unref) (lua_State *L, int t, int ref);
|
||||
static int (*luaL_loadfilex) (lua_State *L, const char *filename, const char *mode);
|
||||
static int (*luaL_loadbufferx) (lua_State *L, const char *buff, size_t sz, const char *name, const char *mode);
|
||||
static int (*luaL_loadstring) (lua_State *L, const char *s);
|
||||
static lua_State *(*luaL_newstate) (void);
|
||||
static int (*luaL_len) (lua_State *L, int idx);
|
||||
static const char *(*luaL_gsub) (lua_State *L, const char *s, const char *p, const char *r);
|
||||
static void (*luaL_setfuncs) (lua_State *L, const luaL_Reg *l, int nup);
|
||||
static int (*luaL_getsubtable) (lua_State *L, int idx, const char *fname);
|
||||
static void (*luaL_traceback) (lua_State *L, lua_State *L1, const char *msg, int level);
|
||||
static void (*luaL_requiref) (lua_State *L, const char *modname, lua_CFunction openf, int glb);
|
||||
static void (*luaL_buffinit) (lua_State *L, luaL_Buffer *B);
|
||||
static char *(*luaL_prepbuffsize) (luaL_Buffer *B, size_t sz);
|
||||
static void (*luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l);
|
||||
static void (*luaL_addstring) (luaL_Buffer *B, const char *s);
|
||||
static void (*luaL_addvalue) (luaL_Buffer *B);
|
||||
static void (*luaL_pushresult) (luaL_Buffer *B);
|
||||
static void (*luaL_pushresultsize) (luaL_Buffer *B, size_t sz);
|
||||
static char *(*luaL_buffinitsize) (lua_State *L, luaL_Buffer *B, size_t sz);
|
||||
static void (*luaL_openlibs) (lua_State *L);
|
||||
#define lauxlib_h
|
||||
#define LUA_ERRFILE (LUA_ERRERR+1)
|
||||
#define luaL_checkversion(L) luaL_checkversion_(L, LUA_VERSION_NUM)
|
||||
#define LUA_NOREF (-2)
|
||||
#define LUA_REFNIL (-1)
|
||||
#define luaL_loadfile(L,f) luaL_loadfilex(L,f,NULL)
|
||||
#define luaL_newlibtable(L,l) lua_createtable(L, 0, sizeof(l)/sizeof((l)[0]) - 1)
|
||||
#define luaL_newlib(L,l) (luaL_newlibtable(L,l), luaL_setfuncs(L,l,0))
|
||||
#define luaL_argcheck(L, cond,numarg,extramsg) ((void)((cond) || luaL_argerror(L, (numarg), (extramsg))))
|
||||
#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL))
|
||||
#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL))
|
||||
#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n)))
|
||||
#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d)))
|
||||
#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n)))
|
||||
#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d)))
|
||||
#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i)))
|
||||
#define luaL_dofile(L, fn) (luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0))
|
||||
#define luaL_dostring(L, s) (luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0))
|
||||
#define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n)))
|
||||
#define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n)))
|
||||
#define luaL_loadbuffer(L,s,sz,n) luaL_loadbufferx(L,s,sz,n,NULL)
|
||||
#define luaL_addchar(B,c) ((void)((B)->n < (B)->size || luaL_prepbuffsize((B), 1)), ((B)->b[(B)->n++] = (c)))
|
||||
#define luaL_addsize(B,s) ((B)->n += (s))
|
||||
#define luaL_prepbuffer(B) luaL_prepbuffsize(B, LUAL_BUFFERSIZE)
|
||||
#define LUA_FILEHANDLE "FILE*"
|
||||
static void __lite_xl_fallback_luaL_checkversion_ (lua_State *L, lua_Number ver) { fputs("warning: luaL_checkversion_ is a stub", stderr); }
|
||||
static int __lite_xl_fallback_luaL_getmetafield (lua_State *L, int obj, const char *e) { fputs("warning: luaL_getmetafield is a stub", stderr); }
|
||||
static int __lite_xl_fallback_luaL_callmeta (lua_State *L, int obj, const char *e) { fputs("warning: luaL_callmeta is a stub", stderr); }
|
||||
static const char * __lite_xl_fallback_luaL_tolstring (lua_State *L, int idx, size_t *len) { fputs("warning: luaL_tolstring is a stub", stderr); }
|
||||
static int __lite_xl_fallback_luaL_argerror (lua_State *L, int numarg, const char *extramsg) { fputs("warning: luaL_argerror is a stub", stderr); }
|
||||
static const char * __lite_xl_fallback_luaL_checklstring (lua_State *L, int numArg, size_t *l) { fputs("warning: luaL_checklstring is a stub", stderr); }
|
||||
static const char * __lite_xl_fallback_luaL_optlstring (lua_State *L, int numArg, const char *def, size_t *l) { fputs("warning: luaL_optlstring is a stub", stderr); }
|
||||
static lua_Number __lite_xl_fallback_luaL_checknumber (lua_State *L, int numArg) { fputs("warning: luaL_checknumber is a stub", stderr); }
|
||||
static lua_Number __lite_xl_fallback_luaL_optnumber (lua_State *L, int nArg, lua_Number def) { fputs("warning: luaL_optnumber is a stub", stderr); }
|
||||
static lua_Integer __lite_xl_fallback_luaL_checkinteger (lua_State *L, int numArg) { fputs("warning: luaL_checkinteger is a stub", stderr); }
|
||||
static lua_Integer __lite_xl_fallback_luaL_optinteger (lua_State *L, int nArg, lua_Integer def) { fputs("warning: luaL_optinteger is a stub", stderr); }
|
||||
static lua_Unsigned __lite_xl_fallback_luaL_checkunsigned (lua_State *L, int numArg) { fputs("warning: luaL_checkunsigned is a stub", stderr); }
|
||||
static lua_Unsigned __lite_xl_fallback_luaL_optunsigned (lua_State *L, int numArg, lua_Unsigned def) { fputs("warning: luaL_optunsigned is a stub", stderr); }
|
||||
static void __lite_xl_fallback_luaL_checkstack (lua_State *L, int sz, const char *msg) { fputs("warning: luaL_checkstack is a stub", stderr); }
|
||||
static void __lite_xl_fallback_luaL_checktype (lua_State *L, int narg, int t) { fputs("warning: luaL_checktype is a stub", stderr); }
|
||||
static void __lite_xl_fallback_luaL_checkany (lua_State *L, int narg) { fputs("warning: luaL_checkany is a stub", stderr); }
|
||||
static int __lite_xl_fallback_luaL_newmetatable (lua_State *L, const char *tname) { fputs("warning: luaL_newmetatable is a stub", stderr); }
|
||||
static void __lite_xl_fallback_luaL_setmetatable (lua_State *L, const char *tname) { fputs("warning: luaL_setmetatable is a stub", stderr); }
|
||||
static void * __lite_xl_fallback_luaL_testudata (lua_State *L, int ud, const char *tname) { fputs("warning: luaL_testudata is a stub", stderr); }
|
||||
static void * __lite_xl_fallback_luaL_checkudata (lua_State *L, int ud, const char *tname) { fputs("warning: luaL_checkudata is a stub", stderr); }
|
||||
static void __lite_xl_fallback_luaL_where (lua_State *L, int lvl) { fputs("warning: luaL_where is a stub", stderr); }
|
||||
static int __lite_xl_fallback_luaL_error (lua_State *L, const char *fmt, ...) { fputs("warning: luaL_error is a stub", stderr); }
|
||||
static int __lite_xl_fallback_luaL_typeerror (lua_State *L, int narg, const char *tname) { fputs("warning: luaL_typeerror is a stub", stderr); }
|
||||
static int __lite_xl_fallback_luaL_checkoption (lua_State *L, int narg, const char *def, const char *const lst[]) { fputs("warning: luaL_checkoption is a stub", stderr); }
|
||||
static int __lite_xl_fallback_luaL_fileresult (lua_State *L, int stat, const char *fname) { fputs("warning: luaL_fileresult is a stub", stderr); }
|
||||
static int __lite_xl_fallback_luaL_execresult (lua_State *L, int stat) { fputs("warning: luaL_execresult is a stub", stderr); }
|
||||
static int __lite_xl_fallback_luaL_ref (lua_State *L, int t) { fputs("warning: luaL_ref is a stub", stderr); }
|
||||
static void __lite_xl_fallback_luaL_unref (lua_State *L, int t, int ref) { fputs("warning: luaL_unref is a stub", stderr); }
|
||||
static int __lite_xl_fallback_luaL_loadfilex (lua_State *L, const char *filename, const char *mode) { fputs("warning: luaL_loadfilex is a stub", stderr); }
|
||||
static int __lite_xl_fallback_luaL_loadbufferx (lua_State *L, const char *buff, size_t sz, const char *name, const char *mode) { fputs("warning: luaL_loadbufferx is a stub", stderr); }
|
||||
static int __lite_xl_fallback_luaL_loadstring (lua_State *L, const char *s) { fputs("warning: luaL_loadstring is a stub", stderr); }
|
||||
static lua_State * __lite_xl_fallback_luaL_newstate (void) { fputs("warning: luaL_newstate is a stub", stderr); }
|
||||
static int __lite_xl_fallback_luaL_len (lua_State *L, int idx) { fputs("warning: luaL_len is a stub", stderr); }
|
||||
static const char * __lite_xl_fallback_luaL_gsub (lua_State *L, const char *s, const char *p, const char *r) { fputs("warning: luaL_gsub is a stub", stderr); }
|
||||
static void __lite_xl_fallback_luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { fputs("warning: luaL_setfuncs is a stub", stderr); }
|
||||
static int __lite_xl_fallback_luaL_getsubtable (lua_State *L, int idx, const char *fname) { fputs("warning: luaL_getsubtable is a stub", stderr); }
|
||||
static void __lite_xl_fallback_luaL_traceback (lua_State *L, lua_State *L1, const char *msg, int level) { fputs("warning: luaL_traceback is a stub", stderr); }
|
||||
static void __lite_xl_fallback_luaL_requiref (lua_State *L, const char *modname, lua_CFunction openf, int glb) { fputs("warning: luaL_requiref is a stub", stderr); }
|
||||
static void __lite_xl_fallback_luaL_buffinit (lua_State *L, luaL_Buffer *B) { fputs("warning: luaL_buffinit is a stub", stderr); }
|
||||
static char * __lite_xl_fallback_luaL_prepbuffsize (luaL_Buffer *B, size_t sz) { fputs("warning: luaL_prepbuffsize is a stub", stderr); }
|
||||
static void __lite_xl_fallback_luaL_addlstring (luaL_Buffer *B, const char *s, size_t l) { fputs("warning: luaL_addlstring is a stub", stderr); }
|
||||
static void __lite_xl_fallback_luaL_addstring (luaL_Buffer *B, const char *s) { fputs("warning: luaL_addstring is a stub", stderr); }
|
||||
static void __lite_xl_fallback_luaL_addvalue (luaL_Buffer *B) { fputs("warning: luaL_addvalue is a stub", stderr); }
|
||||
static void __lite_xl_fallback_luaL_pushresult (luaL_Buffer *B) { fputs("warning: luaL_pushresult is a stub", stderr); }
|
||||
static void __lite_xl_fallback_luaL_pushresultsize (luaL_Buffer *B, size_t sz) { fputs("warning: luaL_pushresultsize is a stub", stderr); }
|
||||
static char * __lite_xl_fallback_luaL_buffinitsize (lua_State *L, luaL_Buffer *B, size_t sz) { fputs("warning: luaL_buffinitsize is a stub", stderr); }
|
||||
static void __lite_xl_fallback_luaL_openlibs (lua_State *L) { fputs("warning: luaL_openlibs is a stub", stderr); }
|
||||
|
||||
#define IMPORT_SYMBOL(name, ret, ...) name = (name = (ret (*) (__VA_ARGS__)) symbol(#name), name == NULL ? &__lite_xl_fallback_##name : name)
|
||||
static void lite_xl_plugin_init(void *XL) {
|
||||
void* (*symbol)(const char *) = (void* (*) (const char *)) XL;
|
||||
IMPORT_SYMBOL(lua_newstate, lua_State *, lua_Alloc f, void *ud);
|
||||
IMPORT_SYMBOL(lua_close, void , lua_State *L);
|
||||
IMPORT_SYMBOL(lua_newthread, lua_State *, lua_State *L);
|
||||
IMPORT_SYMBOL(lua_atpanic, lua_CFunction , lua_State *L, lua_CFunction panicf);
|
||||
IMPORT_SYMBOL(lua_version, const lua_Number *, lua_State *L);
|
||||
IMPORT_SYMBOL(lua_absindex, int , lua_State *L, int idx);
|
||||
IMPORT_SYMBOL(lua_gettop, int , lua_State *L);
|
||||
IMPORT_SYMBOL(lua_settop, void , lua_State *L, int idx);
|
||||
IMPORT_SYMBOL(lua_pushvalue, void , lua_State *L, int idx);
|
||||
IMPORT_SYMBOL(lua_copy, void , lua_State *L, int fromidx, int toidx);
|
||||
IMPORT_SYMBOL(lua_checkstack, int , lua_State *L, int sz);
|
||||
IMPORT_SYMBOL(lua_xmove, void , lua_State *from, lua_State *to, int n);
|
||||
IMPORT_SYMBOL(lua_isnumber, int , lua_State *L, int idx);
|
||||
IMPORT_SYMBOL(lua_isstring, int , lua_State *L, int idx);
|
||||
IMPORT_SYMBOL(lua_iscfunction, int , lua_State *L, int idx);
|
||||
IMPORT_SYMBOL(lua_isuserdata, int , lua_State *L, int idx);
|
||||
IMPORT_SYMBOL(lua_type, int , lua_State *L, int idx);
|
||||
IMPORT_SYMBOL(lua_typename, const char *, lua_State *L, int tp);
|
||||
IMPORT_SYMBOL(lua_tonumberx, lua_Number , lua_State *L, int idx, int *isnum);
|
||||
IMPORT_SYMBOL(lua_tointegerx, lua_Integer , lua_State *L, int idx, int *isnum);
|
||||
IMPORT_SYMBOL(lua_tounsignedx, lua_Unsigned , lua_State *L, int idx, int *isnum);
|
||||
IMPORT_SYMBOL(lua_toboolean, int , lua_State *L, int idx);
|
||||
IMPORT_SYMBOL(lua_tolstring, const char *, lua_State *L, int idx, size_t *len);
|
||||
IMPORT_SYMBOL(lua_rawlen, size_t , lua_State *L, int idx);
|
||||
IMPORT_SYMBOL(lua_tocfunction, lua_CFunction , lua_State *L, int idx);
|
||||
IMPORT_SYMBOL(lua_touserdata, void *, lua_State *L, int idx);
|
||||
IMPORT_SYMBOL(lua_tothread, lua_State *, lua_State *L, int idx);
|
||||
IMPORT_SYMBOL(lua_topointer, const void *, lua_State *L, int idx);
|
||||
IMPORT_SYMBOL(lua_arith, void , lua_State *L, int op);
|
||||
IMPORT_SYMBOL(lua_rawequal, int , lua_State *L, int idx1, int idx2);
|
||||
IMPORT_SYMBOL(lua_compare, int , lua_State *L, int idx1, int idx2, int op);
|
||||
IMPORT_SYMBOL(lua_pushnil, void , lua_State *L);
|
||||
IMPORT_SYMBOL(lua_pushnumber, void , lua_State *L, lua_Number n);
|
||||
IMPORT_SYMBOL(lua_pushinteger, void , lua_State *L, lua_Integer n);
|
||||
IMPORT_SYMBOL(lua_pushunsigned, void , lua_State *L, lua_Unsigned n);
|
||||
IMPORT_SYMBOL(lua_pushlstring, const char *, lua_State *L, const char *s, size_t l);
|
||||
IMPORT_SYMBOL(lua_pushstring, const char *, lua_State *L, const char *s);
|
||||
IMPORT_SYMBOL(lua_pushvfstring, const char *, lua_State *L, const char *fmt, va_list argp);
|
||||
IMPORT_SYMBOL(lua_pushfstring, const char *, lua_State *L, const char *fmt, ...);
|
||||
IMPORT_SYMBOL(lua_pushcclosure, void , lua_State *L, lua_CFunction fn, int n);
|
||||
IMPORT_SYMBOL(lua_pushboolean, void , lua_State *L, int b);
|
||||
IMPORT_SYMBOL(lua_pushlightuserdata, void , lua_State *L, void *p);
|
||||
IMPORT_SYMBOL(lua_pushthread, int , lua_State *L);
|
||||
IMPORT_SYMBOL(lua_getglobal, void , lua_State *L, const char *var);
|
||||
IMPORT_SYMBOL(lua_gettable, void , lua_State *L, int idx);
|
||||
IMPORT_SYMBOL(lua_getfield, void , lua_State *L, int idx, const char *k);
|
||||
IMPORT_SYMBOL(lua_rawget, void , lua_State *L, int idx);
|
||||
IMPORT_SYMBOL(lua_rawgeti, void , lua_State *L, int idx, int n);
|
||||
IMPORT_SYMBOL(lua_rawgetp, void , lua_State *L, int idx, const void *p);
|
||||
IMPORT_SYMBOL(lua_createtable, void , lua_State *L, int narr, int nrec);
|
||||
IMPORT_SYMBOL(lua_newuserdata, void *, lua_State *L, size_t sz);
|
||||
IMPORT_SYMBOL(lua_newuserdatauv, void *, lua_State *L, size_t sz, int nuvalue);
|
||||
IMPORT_SYMBOL(lua_getmetatable, int , lua_State *L, int objindex);
|
||||
IMPORT_SYMBOL(lua_getuservalue, void , lua_State *L, int idx);
|
||||
IMPORT_SYMBOL(lua_getiuservalue, void , lua_State *L, int idx, int n);
|
||||
IMPORT_SYMBOL(lua_setglobal, void , lua_State *L, const char *var);
|
||||
IMPORT_SYMBOL(lua_settable, void , lua_State *L, int idx);
|
||||
IMPORT_SYMBOL(lua_setfield, void , lua_State *L, int idx, const char *k);
|
||||
IMPORT_SYMBOL(lua_rawset, void , lua_State *L, int idx);
|
||||
IMPORT_SYMBOL(lua_rawseti, void , lua_State *L, int idx, int n);
|
||||
IMPORT_SYMBOL(lua_rawsetp, void , lua_State *L, int idx, const void *p);
|
||||
IMPORT_SYMBOL(lua_setmetatable, int , lua_State *L, int objindex);
|
||||
IMPORT_SYMBOL(lua_setuservalue, void , lua_State *L, int idx);
|
||||
IMPORT_SYMBOL(lua_setiuservalue, void , lua_State *L, int idx, int n);
|
||||
IMPORT_SYMBOL(lua_callk, void , lua_State *L, int nargs, int nresults, int ctx, lua_CFunction k);
|
||||
IMPORT_SYMBOL(lua_getctx, int , lua_State *L, int *ctx);
|
||||
IMPORT_SYMBOL(lua_pcallk, int , lua_State *L, int nargs, int nresults, int errfunc, int ctx, lua_CFunction k);
|
||||
IMPORT_SYMBOL(lua_load, int , lua_State *L, lua_Reader reader, void *dt, const char *chunkname, const char *mode);
|
||||
IMPORT_SYMBOL(lua_dump, int , lua_State *L, lua_Writer writer, void *data, int strip);
|
||||
IMPORT_SYMBOL(lua_yieldk, int , lua_State *L, int nresults, int ctx, lua_CFunction k);
|
||||
IMPORT_SYMBOL(lua_resume, int , lua_State *L, lua_State *from, int narg);
|
||||
IMPORT_SYMBOL(lua_status, int , lua_State *L);
|
||||
IMPORT_SYMBOL(lua_gc, int , lua_State *L, int what, int data);
|
||||
IMPORT_SYMBOL(lua_error, int , lua_State *L);
|
||||
IMPORT_SYMBOL(lua_next, int , lua_State *L, int idx);
|
||||
IMPORT_SYMBOL(lua_concat, void , lua_State *L, int n);
|
||||
IMPORT_SYMBOL(lua_len, void , lua_State *L, int idx);
|
||||
IMPORT_SYMBOL(lua_getallocf, lua_Alloc , lua_State *L, void **ud);
|
||||
IMPORT_SYMBOL(lua_setallocf, void , lua_State *L, lua_Alloc f, void *ud);
|
||||
IMPORT_SYMBOL(lua_getstack, int , lua_State *L, int level, lua_Debug *ar);
|
||||
IMPORT_SYMBOL(lua_getinfo, int , lua_State *L, const char *what, lua_Debug *ar);
|
||||
IMPORT_SYMBOL(lua_getlocal, const char *, lua_State *L, const lua_Debug *ar, int n);
|
||||
IMPORT_SYMBOL(lua_setlocal, const char *, lua_State *L, const lua_Debug *ar, int n);
|
||||
IMPORT_SYMBOL(lua_getupvalue, const char *, lua_State *L, int funcindex, int n);
|
||||
IMPORT_SYMBOL(lua_setupvalue, const char *, lua_State *L, int funcindex, int n);
|
||||
IMPORT_SYMBOL(lua_upvalueid, void *, lua_State *L, int fidx, int n);
|
||||
IMPORT_SYMBOL(lua_upvaluejoin, void , lua_State *L, int fidx1, int n1, int fidx2, int n2);
|
||||
IMPORT_SYMBOL(lua_sethook, int , lua_State *L, lua_Hook func, int mask, int count);
|
||||
IMPORT_SYMBOL(lua_gethook, lua_Hook , lua_State *L);
|
||||
IMPORT_SYMBOL(lua_gethookmask, int , lua_State *L);
|
||||
IMPORT_SYMBOL(lua_gethookcount, int , lua_State *L);
|
||||
IMPORT_SYMBOL(luaL_checkversion_, void , lua_State *L, lua_Number ver);
|
||||
IMPORT_SYMBOL(luaL_getmetafield, int , lua_State *L, int obj, const char *e);
|
||||
IMPORT_SYMBOL(luaL_callmeta, int , lua_State *L, int obj, const char *e);
|
||||
IMPORT_SYMBOL(luaL_tolstring, const char *, lua_State *L, int idx, size_t *len);
|
||||
IMPORT_SYMBOL(luaL_argerror, int , lua_State *L, int numarg, const char *extramsg);
|
||||
IMPORT_SYMBOL(luaL_checklstring, const char *, lua_State *L, int numArg, size_t *l);
|
||||
IMPORT_SYMBOL(luaL_optlstring, const char *, lua_State *L, int numArg, const char *def, size_t *l);
|
||||
IMPORT_SYMBOL(luaL_checknumber, lua_Number , lua_State *L, int numArg);
|
||||
IMPORT_SYMBOL(luaL_optnumber, lua_Number , lua_State *L, int nArg, lua_Number def);
|
||||
IMPORT_SYMBOL(luaL_checkinteger, lua_Integer , lua_State *L, int numArg);
|
||||
IMPORT_SYMBOL(luaL_optinteger, lua_Integer , lua_State *L, int nArg, lua_Integer def);
|
||||
IMPORT_SYMBOL(luaL_checkunsigned, lua_Unsigned , lua_State *L, int numArg);
|
||||
IMPORT_SYMBOL(luaL_optunsigned, lua_Unsigned , lua_State *L, int numArg, lua_Unsigned def);
|
||||
IMPORT_SYMBOL(luaL_checkstack, void , lua_State *L, int sz, const char *msg);
|
||||
IMPORT_SYMBOL(luaL_checktype, void , lua_State *L, int narg, int t);
|
||||
IMPORT_SYMBOL(luaL_checkany, void , lua_State *L, int narg);
|
||||
IMPORT_SYMBOL(luaL_newmetatable, int , lua_State *L, const char *tname);
|
||||
IMPORT_SYMBOL(luaL_setmetatable, void , lua_State *L, const char *tname);
|
||||
IMPORT_SYMBOL(luaL_testudata, void *, lua_State *L, int ud, const char *tname);
|
||||
IMPORT_SYMBOL(luaL_checkudata, void *, lua_State *L, int ud, const char *tname);
|
||||
IMPORT_SYMBOL(luaL_where, void , lua_State *L, int lvl);
|
||||
IMPORT_SYMBOL(luaL_error, int , lua_State *L, const char *fmt, ...);
|
||||
IMPORT_SYMBOL(luaL_typeerror, int , lua_State *L, int narg, const char *tname);
|
||||
IMPORT_SYMBOL(luaL_checkoption, int , lua_State *L, int narg, const char *def, const char *const lst[]);
|
||||
IMPORT_SYMBOL(luaL_fileresult, int , lua_State *L, int stat, const char *fname);
|
||||
IMPORT_SYMBOL(luaL_execresult, int , lua_State *L, int stat);
|
||||
IMPORT_SYMBOL(luaL_ref, int , lua_State *L, int t);
|
||||
IMPORT_SYMBOL(luaL_unref, void , lua_State *L, int t, int ref);
|
||||
IMPORT_SYMBOL(luaL_loadfilex, int , lua_State *L, const char *filename, const char *mode);
|
||||
IMPORT_SYMBOL(luaL_loadbufferx, int , lua_State *L, const char *buff, size_t sz, const char *name, const char *mode);
|
||||
IMPORT_SYMBOL(luaL_loadstring, int , lua_State *L, const char *s);
|
||||
IMPORT_SYMBOL(luaL_newstate, lua_State *, void);
|
||||
IMPORT_SYMBOL(luaL_len, int , lua_State *L, int idx);
|
||||
IMPORT_SYMBOL(luaL_gsub, const char *, lua_State *L, const char *s, const char *p, const char *r);
|
||||
IMPORT_SYMBOL(luaL_setfuncs, void , lua_State *L, const luaL_Reg *l, int nup);
|
||||
IMPORT_SYMBOL(luaL_getsubtable, int , lua_State *L, int idx, const char *fname);
|
||||
IMPORT_SYMBOL(luaL_traceback, void , lua_State *L, lua_State *L1, const char *msg, int level);
|
||||
IMPORT_SYMBOL(luaL_requiref, void , lua_State *L, const char *modname, lua_CFunction openf, int glb);
|
||||
IMPORT_SYMBOL(luaL_buffinit, void , lua_State *L, luaL_Buffer *B);
|
||||
IMPORT_SYMBOL(luaL_prepbuffsize, char *, luaL_Buffer *B, size_t sz);
|
||||
IMPORT_SYMBOL(luaL_addlstring, void , luaL_Buffer *B, const char *s, size_t l);
|
||||
IMPORT_SYMBOL(luaL_addstring, void , luaL_Buffer *B, const char *s);
|
||||
IMPORT_SYMBOL(luaL_addvalue, void , luaL_Buffer *B);
|
||||
IMPORT_SYMBOL(luaL_pushresult, void , luaL_Buffer *B);
|
||||
IMPORT_SYMBOL(luaL_pushresultsize, void , luaL_Buffer *B, size_t sz);
|
||||
IMPORT_SYMBOL(luaL_buffinitsize, char *, lua_State *L, luaL_Buffer *B, size_t sz);
|
||||
IMPORT_SYMBOL(luaL_openlibs, void, lua_State* L);
|
||||
}
|
||||
#endif
|
|
@ -1,6 +1,6 @@
|
|||
diff -ruN lua-5.4.4/meson.build lua-5.4.4-mod/meson.build
|
||||
--- lua-5.4.4/meson.build 2022-11-16 10:33:38.424383300 +0800
|
||||
+++ lua-5.4.4-mod/meson.build 2022-11-16 09:40:57.697918000 +0800
|
||||
diff -ruN lua-5.4.4\meson.build lua-5.4.4-patched\meson.build
|
||||
--- lua-5.4.4\meson.build Wed Feb 22 18:16:56 2023
|
||||
+++ lua-5.4.4-patched\meson.build Wed Feb 22 04:10:01 2023
|
||||
@@ -85,6 +85,7 @@
|
||||
'src/lutf8lib.c',
|
||||
'src/lvm.c',
|
||||
|
@ -9,9 +9,9 @@ diff -ruN lua-5.4.4/meson.build lua-5.4.4-mod/meson.build
|
|||
dependencies: lua_lib_deps,
|
||||
version: meson.project_version(),
|
||||
soversion: lua_versions[0] + '.' + lua_versions[1],
|
||||
diff -ruN lua-5.4.4/src/luaconf.h lua-5.4.4-mod/src/luaconf.h
|
||||
--- lua-5.4.4/src/luaconf.h 2022-01-13 19:24:43.000000000 +0800
|
||||
+++ lua-5.4.4-mod/src/luaconf.h 2022-11-16 09:40:57.703926000 +0800
|
||||
diff -ruN lua-5.4.4\src\luaconf.h lua-5.4.4-patched\src\luaconf.h
|
||||
--- lua-5.4.4\src\luaconf.h Thu Jan 13 19:24:43 2022
|
||||
+++ lua-5.4.4-patched\src\luaconf.h Wed Feb 22 04:10:02 2023
|
||||
@@ -782,5 +782,15 @@
|
||||
|
||||
|
||||
|
@ -28,9 +28,9 @@ diff -ruN lua-5.4.4/src/luaconf.h lua-5.4.4-mod/src/luaconf.h
|
|||
+
|
||||
#endif
|
||||
|
||||
diff -ruN lua-5.4.4/src/Makefile lua-5.4.4-mod/src/Makefile
|
||||
--- lua-5.4.4/src/Makefile 2021-07-15 22:01:52.000000000 +0800
|
||||
+++ lua-5.4.4-mod/src/Makefile 2022-11-16 09:40:57.708921800 +0800
|
||||
diff -ruN lua-5.4.4\src\Makefile lua-5.4.4-patched\src\Makefile
|
||||
--- lua-5.4.4\src\Makefile Thu Jul 15 22:01:52 2021
|
||||
+++ lua-5.4.4-patched\src\Makefile Wed Feb 22 04:10:02 2023
|
||||
@@ -33,7 +33,7 @@
|
||||
PLATS= guess aix bsd c89 freebsd generic linux linux-readline macosx mingw posix solaris
|
||||
|
||||
|
@ -40,10 +40,10 @@ diff -ruN lua-5.4.4/src/Makefile lua-5.4.4-mod/src/Makefile
|
|||
LIB_O= lauxlib.o lbaselib.o lcorolib.o ldblib.o liolib.o lmathlib.o loadlib.o loslib.o lstrlib.o ltablib.o lutf8lib.o linit.o
|
||||
BASE_O= $(CORE_O) $(LIB_O) $(MYOBJS)
|
||||
|
||||
diff -ruN lua-5.4.4/src/utf8_wrappers.c lua-5.4.4-mod/src/utf8_wrappers.c
|
||||
--- lua-5.4.4/src/utf8_wrappers.c 1970-01-01 07:30:00.000000000 +0730
|
||||
+++ lua-5.4.4-mod/src/utf8_wrappers.c 2022-11-16 10:09:04.583866600 +0800
|
||||
@@ -0,0 +1,101 @@
|
||||
diff -ruN lua-5.4.4\src\utf8_wrappers.c lua-5.4.4-patched\src\utf8_wrappers.c
|
||||
--- lua-5.4.4\src\utf8_wrappers.c Thu Jan 01 08:00:00 1970
|
||||
+++ lua-5.4.4-patched\src\utf8_wrappers.c Wed Feb 22 18:13:45 2023
|
||||
@@ -0,0 +1,129 @@
|
||||
+/**
|
||||
+ * Wrappers to provide Unicode (UTF-8) support on Windows.
|
||||
+ *
|
||||
|
@ -58,12 +58,17 @@ diff -ruN lua-5.4.4/src/utf8_wrappers.c lua-5.4.4-mod/src/utf8_wrappers.c
|
|||
+#include <stdlib.h>
|
||||
+#include <errno.h>
|
||||
+
|
||||
+// A environment variable has the maximum length of 32767 characters
|
||||
+// including the terminator.
|
||||
+#define MAX_ENV_SIZE 32767
|
||||
+// Set a high limit in case long paths are enabled.
|
||||
+#define MAX_PATH_SIZE 4096
|
||||
+#define MAX_MODE_SIZE 128
|
||||
+// cmd.exe argument length is reportedly limited to 8192.
|
||||
+#define MAX_CMD_SIZE 8192
|
||||
+
|
||||
+static char env_value[MAX_ENV_SIZE];
|
||||
+
|
||||
+FILE *fopen_utf8(const char *pathname, const char *mode) {
|
||||
+ wchar_t pathname_w[MAX_PATH_SIZE];
|
||||
+ wchar_t mode_w[MAX_MODE_SIZE];
|
||||
|
@ -144,11 +149,34 @@ diff -ruN lua-5.4.4/src/utf8_wrappers.c lua-5.4.4-mod/src/utf8_wrappers.c
|
|||
+ }
|
||||
+ return LoadLibraryExW(pathname_w, hFile, dwFlags);
|
||||
+}
|
||||
+
|
||||
+char* getenv_utf8(const char *varname) {
|
||||
+ /** This implementation is not thread safe.
|
||||
+ * The string is only valid until the next call to getenv.
|
||||
+ * This behavior is allowed per POSIX.1-2017 where it was said that:
|
||||
+ * > The returned string pointer might be invalidated or the string content might be overwritten by a subsequent call to getenv(), setenv(), unsetenv(), or (if supported) putenv() but they shall not be affected by a call to any other function in this volume of POSIX.1-2017.
|
||||
+ * > The returned string pointer might also be invalidated if the calling thread is terminated.
|
||||
+ * > The getenv() function need not be thread-safe.
|
||||
+ */
|
||||
+ wchar_t *value_w;
|
||||
+ wchar_t varname_w[MAX_ENV_SIZE];
|
||||
+
|
||||
+ if (!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, varname, -1, varname_w, MAX_ENV_SIZE))
|
||||
+ return NULL;
|
||||
+ value_w = _wgetenv((const wchar_t *) varname_w);
|
||||
+ if (!value_w)
|
||||
+ return NULL;
|
||||
+
|
||||
+ if (!WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, value_w, -1, env_value, MAX_ENV_SIZE, NULL, NULL))
|
||||
+ return NULL;
|
||||
+
|
||||
+ return env_value;
|
||||
+}
|
||||
+#endif
|
||||
diff -ruN lua-5.4.4/src/utf8_wrappers.h lua-5.4.4-mod/src/utf8_wrappers.h
|
||||
--- lua-5.4.4/src/utf8_wrappers.h 1970-01-01 07:30:00.000000000 +0730
|
||||
+++ lua-5.4.4-mod/src/utf8_wrappers.h 2022-11-16 10:29:46.044102000 +0800
|
||||
@@ -0,0 +1,44 @@
|
||||
diff -ruN lua-5.4.4\src\utf8_wrappers.h lua-5.4.4-patched\src\utf8_wrappers.h
|
||||
--- lua-5.4.4\src\utf8_wrappers.h Thu Jan 01 08:00:00 1970
|
||||
+++ lua-5.4.4-patched\src\utf8_wrappers.h Wed Feb 22 18:09:48 2023
|
||||
@@ -0,0 +1,46 @@
|
||||
+/**
|
||||
+ * Wrappers to provide Unicode (UTF-8) support on Windows.
|
||||
+ *
|
||||
|
@ -180,9 +208,11 @@ diff -ruN lua-5.4.4/src/utf8_wrappers.h lua-5.4.4-mod/src/utf8_wrappers.h
|
|||
+int remove_utf8(const char *pathname);
|
||||
+int rename_utf8(const char *oldpath, const char *newpath);
|
||||
+int system_utf8(const char *command);
|
||||
+char *getenv_utf8(const char *varname);
|
||||
+#define remove remove_utf8
|
||||
+#define rename rename_utf8
|
||||
+#define system system_utf8
|
||||
+#define getenv getenv_utf8
|
||||
+#endif
|
||||
+
|
||||
+#ifdef loadlib_c
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<assemblyIdentity
|
||||
type="win32"
|
||||
name="LiteXL.LiteXL.LiteXL"
|
||||
version="@PROJECT_ASSEMBLY_VERSION@"
|
||||
/>
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<security>
|
||||
<requestedPrivileges>
|
||||
<requestedExecutionLevel level="asInvoker"/>
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity
|
||||
type="win32"
|
||||
name="Microsoft.Windows.Common-Controls"
|
||||
version="6.0.0.0"
|
||||
processorArchitecture="*"
|
||||
publicKeyToken="6595b64144ccf1df"
|
||||
language="*"
|
||||
/>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
|
||||
<asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
|
||||
<dpiAware>true</dpiAware>
|
||||
</asmv3:windowsSettings>
|
||||
</asmv3:application>
|
||||
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!--The ID below indicates application support for Windows Vista -->
|
||||
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
|
||||
<!--The ID below indicates application support for Windows 7 -->
|
||||
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
|
||||
<!--The ID below indicates application support for Windows 8 -->
|
||||
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
|
||||
<!--The ID below indicates application support for Windows 8.1 -->
|
||||
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
|
||||
<!--The ID below indicates application support for Windows 10 -->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
|
||||
</application>
|
||||
</compatibility>
|
||||
</assembly>
|
|
@ -0,0 +1,4 @@
|
|||
#define IDR_RT_MANIFEST1 1
|
||||
#define RT_MANIFEST 24
|
||||
|
||||
IDR_RT_MANIFEST1 RT_MANIFEST "lite-xl.exe.manifest"
|
|
@ -138,7 +138,7 @@ generate_appimage() {
|
|||
mv AppRun LiteXL.AppDir/
|
||||
# These could be symlinks but it seems they doesn't work with AppimageLauncher
|
||||
cp resources/icons/lite-xl.svg LiteXL.AppDir/
|
||||
cp resources/linux/org.lite_xl.lite_xl.desktop LiteXL.AppDir/
|
||||
cp resources/linux/com.lite_xl.LiteXL.desktop LiteXL.AppDir/
|
||||
|
||||
if [[ $ADDONS == true ]]; then
|
||||
addons_download "${BUILD_DIR}"
|
||||
|
@ -181,7 +181,7 @@ generate_appimage() {
|
|||
version="${version}-addons"
|
||||
fi
|
||||
|
||||
./appimagetool LiteXL.AppDir LiteXL${version}-${ARCH}.AppImage
|
||||
./appimagetool --appimage-extract-and-run LiteXL.AppDir LiteXL${version}-${ARCH}.AppImage
|
||||
}
|
||||
|
||||
setup_appimagetool
|
||||
|
|
|
@ -26,6 +26,13 @@ show_help() {
|
|||
echo " macOS: disabled when used with --bundle,"
|
||||
echo " Windows: Implicit being the only option."
|
||||
echo "-r --release Compile in release mode."
|
||||
echo " --cross-platform PLATFORM Cross compile for this platform."
|
||||
echo " The script will find the appropriate"
|
||||
echo " cross file in 'resources/cross'."
|
||||
echo " --cross-arch ARCH Cross compile for this architecture."
|
||||
echo " The script will find the appropriate"
|
||||
echo " cross file in 'resources/cross'."
|
||||
echo " --cross-file CROSS_FILE Cross compile with the given cross file."
|
||||
echo
|
||||
}
|
||||
|
||||
|
@ -40,6 +47,10 @@ main() {
|
|||
local portable
|
||||
local pgo
|
||||
local patch_lua
|
||||
local cross
|
||||
local cross_platform
|
||||
local cross_arch
|
||||
local cross_file
|
||||
|
||||
local lua_subproject_path
|
||||
|
||||
|
@ -87,6 +98,24 @@ main() {
|
|||
patch_lua="true"
|
||||
shift
|
||||
;;
|
||||
--cross-arch)
|
||||
cross="true"
|
||||
cross_arch="$2"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
--cross-platform)
|
||||
cross="true"
|
||||
cross_platform="$2"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
--cross-file)
|
||||
cross="true"
|
||||
cross_file="$2"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
-r|--release)
|
||||
build_type="release"
|
||||
shift
|
||||
|
@ -107,38 +136,67 @@ main() {
|
|||
portable=""
|
||||
fi
|
||||
|
||||
if [[ $CROSS_ARCH != "" ]]; then
|
||||
if [[ $platform == "macos" ]]; then
|
||||
macos_version_min=10.11
|
||||
if [[ $CROSS_ARCH == "arm64" ]]; then
|
||||
cross_file="--cross-file resources/macos/macos_arm64.conf"
|
||||
macos_version_min=11.0
|
||||
# if CROSS_ARCH is used, it will be picked up
|
||||
cross="${cross:-$CROSS_ARCH}"
|
||||
if [[ -n "$cross" ]]; then
|
||||
if [[ -n "$cross_file" ]] && ([[ -z "$cross_arch" ]] || [[ -z "$cross_platform" ]]); then
|
||||
echo "Warning: --cross-platform or --cross-platform not set; guessing it from the filename."
|
||||
# remove file extensions and directories from the path
|
||||
cross_file_name="${cross_file##*/}"
|
||||
cross_file_name="${cross_file_name%%.*}"
|
||||
# cross_platform is the string before encountering the first hyphen
|
||||
if [[ -z "$cross_platform" ]]; then
|
||||
cross_platform="${cross_file_name%%-*}"
|
||||
echo "Warning: Guessing --cross-platform $cross_platform"
|
||||
fi
|
||||
export MACOSX_DEPLOYMENT_TARGET=$macos_version_min
|
||||
export MIN_SUPPORTED_MACOSX_DEPLOYMENT_TARGET=$macos_version_min
|
||||
export CFLAGS=-mmacosx-version-min=$macos_version_min
|
||||
export CXXFLAGS=-mmacosx-version-min=$macos_version_min
|
||||
export LDFLAGS=-mmacosx-version-min=$macos_version_min
|
||||
# cross_arch is the string after encountering the first hyphen
|
||||
if [[ -z "$cross_arch" ]]; then
|
||||
cross_arch="${cross_file_name#*-}"
|
||||
echo "Warning: Guessing --cross-arch $cross_arch"
|
||||
fi
|
||||
fi
|
||||
platform="${cross_platform:-$platform}"
|
||||
arch="${cross_arch:-$arch}"
|
||||
cross_file=("--cross-file" "${cross_file:-resources/cross/$platform-$arch.txt}")
|
||||
# reload build_dir because platform and arch might change
|
||||
build_dir="$(get_default_build_dir "$platform" "$arch")"
|
||||
fi
|
||||
|
||||
# arch and platform specific stuff
|
||||
if [[ "$platform" == "macos" ]]; then
|
||||
macos_version_min="10.11"
|
||||
if [[ "$arch" == "arm64" ]]; then
|
||||
macos_version_min="11.0"
|
||||
fi
|
||||
export MACOSX_DEPLOYMENT_TARGET="$macos_version_min"
|
||||
export MIN_SUPPORTED_MACOSX_DEPLOYMENT_TARGET="$macos_version_min"
|
||||
export CFLAGS="-mmacosx-version-min=$macos_version_min"
|
||||
export CXXFLAGS="-mmacosx-version-min=$macos_version_min"
|
||||
export LDFLAGS="-mmacosx-version-min=$macos_version_min"
|
||||
fi
|
||||
|
||||
rm -rf "${build_dir}"
|
||||
|
||||
if [[ $patch_lua == "true" ]] && [[ ! -z $force_fallback ]]; then
|
||||
# download the subprojects so we can start patching before configure.
|
||||
# this will prevent reconfiguring the project.
|
||||
meson subprojects download
|
||||
lua_subproject_path=$(echo subprojects/lua-*/)
|
||||
if [[ -d $lua_subproject_path ]]; then
|
||||
patch -d $lua_subproject_path -p1 --forward < resources/windows/001-lua-unicode.diff
|
||||
fi
|
||||
fi
|
||||
|
||||
CFLAGS=$CFLAGS LDFLAGS=$LDFLAGS meson setup \
|
||||
--buildtype=$build_type \
|
||||
--prefix "$prefix" \
|
||||
$cross_file \
|
||||
"${cross_file[@]}" \
|
||||
$force_fallback \
|
||||
$bundle \
|
||||
$portable \
|
||||
$pgo \
|
||||
"${build_dir}"
|
||||
|
||||
lua_subproject_path=$(echo subprojects/lua-*/)
|
||||
if [[ $patch_lua == "true" ]] && [[ ! -z $force_fallback ]] && [[ -d $lua_subproject_path ]]; then
|
||||
patch -d $lua_subproject_path -p1 --forward < resources/windows/001-lua-unicode.diff
|
||||
fi
|
||||
|
||||
meson compile -C "${build_dir}"
|
||||
|
||||
if [[ $pgo != "" ]]; then
|
||||
|
|
|
@ -75,22 +75,20 @@ get_platform_name() {
|
|||
|
||||
get_platform_arch() {
|
||||
platform=$(get_platform_name)
|
||||
arch=$(uname -m)
|
||||
arch=${CROSS_ARCH:-$(uname -m)}
|
||||
if [[ $MSYSTEM != "" ]]; then
|
||||
if [[ $MSYSTEM == "MINGW64" ]]; then
|
||||
arch=x86_64
|
||||
else
|
||||
arch=i686
|
||||
fi
|
||||
elif [[ $CROSS_ARCH != "" ]]; then
|
||||
arch=$CROSS_ARCH
|
||||
fi
|
||||
echo "$arch"
|
||||
}
|
||||
|
||||
get_default_build_dir() {
|
||||
platform=$(get_platform_name)
|
||||
arch=$(get_platform_arch)
|
||||
platform="${1:-$(get_platform_name)}"
|
||||
arch="${2:-$(get_platform_arch)}"
|
||||
echo "build-$platform-$arch"
|
||||
}
|
||||
|
||||
|
|
|
@ -63,10 +63,10 @@ main() {
|
|||
elif [[ "$OSTYPE" == "msys" ]]; then
|
||||
if [[ $lhelper == true ]]; then
|
||||
pacman --noconfirm -S \
|
||||
${MINGW_PACKAGE_PREFIX}-{gcc,meson,ninja,ntldd,pkg-config,mesa} unzip
|
||||
${MINGW_PACKAGE_PREFIX}-{ca-certificates,gcc,meson,ninja,ntldd,pkg-config,mesa} unzip
|
||||
else
|
||||
pacman --noconfirm -S \
|
||||
${MINGW_PACKAGE_PREFIX}-{gcc,meson,ninja,ntldd,pkg-config,mesa,freetype,pcre2,SDL2} unzip
|
||||
${MINGW_PACKAGE_PREFIX}-{ca-certificates,gcc,meson,ninja,ntldd,pkg-config,mesa,freetype,pcre2,SDL2} unzip
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
#!/usr/bin/env bash
|
||||
set -ex
|
||||
|
||||
if [ ! -e "src/api/api.h" ]; then
|
||||
echo "Please run this script from the root directory of Lite XL."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
WORKDIR="work"
|
||||
DMGDIR="$1"
|
||||
|
||||
if [[ -z "$DMGDIR" ]]; then
|
||||
echo "Please provide a path containing the dmg files."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rm -rf "$WORKDIR"
|
||||
mkdir -p "$WORKDIR"
|
||||
|
||||
for dmg_path in "$DMGDIR"/*.dmg; do
|
||||
dmg="${dmg_path##*/}"
|
||||
dmg="${dmg%.dmg}"
|
||||
hdiutil attach -mountpoint "/Volumes/$dmg" "$dmg_path"
|
||||
if [[ ! -d "$WORKDIR/dmg" ]]; then
|
||||
ditto "/Volumes/$dmg/Lite XL.app" "Lite XL.app"
|
||||
fi
|
||||
cp "/Volumes/$dmg/Lite XL.app/Contents/MacOS/lite-xl" "$WORKDIR/$dmg-lite-xl"
|
||||
hdiutil detach "/Volumes/$dmg"
|
||||
done
|
||||
|
||||
lipo -create -output "Lite XL.app/Contents/MacOS/lite-xl" "$WORKDIR/"*-lite-xl
|
||||
|
||||
source scripts/appdmg.sh "$2"
|
|
@ -30,6 +30,8 @@ show_help() {
|
|||
echo "-r --release Strip debugging symbols."
|
||||
echo "-S --source Create a source code package,"
|
||||
echo " including subprojects dependencies."
|
||||
echo " --cross-platform PLATFORM The platform to package for."
|
||||
echo " --cross-arch ARCH The architecture to package for."
|
||||
echo
|
||||
}
|
||||
|
||||
|
@ -73,6 +75,9 @@ main() {
|
|||
local innosetup=false
|
||||
local release=false
|
||||
local source=false
|
||||
local cross
|
||||
local cross_arch
|
||||
local cross_platform
|
||||
|
||||
# store the current flags to easily pass them to appimage script
|
||||
local flags="$@"
|
||||
|
@ -143,6 +148,18 @@ main() {
|
|||
addons=true
|
||||
shift
|
||||
;;
|
||||
--cross-platform)
|
||||
cross=true
|
||||
cross_platform="$2"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
--cross-arch)
|
||||
cross=true
|
||||
cross_arch="$2"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
--debug)
|
||||
set -x
|
||||
shift
|
||||
|
@ -159,6 +176,12 @@ main() {
|
|||
|
||||
if [[ -n $1 ]]; then show_help; exit 1; fi
|
||||
|
||||
if [[ -n "$cross" ]]; then
|
||||
platform="${cross_platform:-$platform}"
|
||||
arch="${cross_arch:-$arch}"
|
||||
build_dir="$(get_default_build_dir "$platform" "$arch")"
|
||||
fi
|
||||
|
||||
# The source package doesn't require a previous build,
|
||||
# nor the following install step, so run it now.
|
||||
if [[ $source == true ]]; then source_package "lite-xl$version-src"; fi
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
struct dirmonitor_internal* init_dirmonitor() { return NULL; }
|
||||
void deinit_dirmonitor(struct dirmonitor_internal* monitor) { }
|
||||
int get_changes_dirmonitor(struct dirmonitor_internal* monitor, char* buffer, size_t len) { return -1; }
|
||||
int get_changes_dirmonitor(struct dirmonitor_internal* monitor, char* buffer, int len) { return -1; }
|
||||
int translate_changes_dirmonitor(struct dirmonitor_internal* monitor, char* buffer, int size, int (*callback)(int, const char*, void*), void* data) { return -1; }
|
||||
int add_dirmonitor(struct dirmonitor_internal* monitor, const char* path) { return -1; }
|
||||
void remove_dirmonitor(struct dirmonitor_internal* monitor, int fd) { }
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <SDL.h>
|
||||
#include <SDL_thread.h>
|
||||
#include <assert.h>
|
||||
|
||||
#if _WIN32
|
||||
|
@ -21,11 +22,30 @@
|
|||
#endif
|
||||
|
||||
#define READ_BUF_SIZE 2048
|
||||
#define PROCESS_TERM_TRIES 3
|
||||
#define PROCESS_TERM_DELAY 50
|
||||
#define PROCESS_KILL_LIST_NAME "__process_kill_list__"
|
||||
|
||||
#if _WIN32
|
||||
typedef HANDLE process_handle;
|
||||
|
||||
typedef DWORD process_error_t;
|
||||
typedef HANDLE process_stream_t;
|
||||
typedef HANDLE process_handle_t;
|
||||
|
||||
#define HANDLE_INVALID (INVALID_HANDLE_VALUE)
|
||||
#define PROCESS_GET_HANDLE(P) ((P)->process_information.hProcess)
|
||||
|
||||
static volatile long PipeSerialNumber;
|
||||
|
||||
#else
|
||||
typedef int process_handle;
|
||||
|
||||
typedef int process_error_t;
|
||||
typedef int process_stream_t;
|
||||
typedef pid_t process_handle_t;
|
||||
|
||||
#define HANDLE_INVALID (0)
|
||||
#define PROCESS_GET_HANDLE(P) ((P)->pid)
|
||||
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
|
@ -38,9 +58,25 @@ typedef struct {
|
|||
bool reading[2];
|
||||
char buffer[2][READ_BUF_SIZE];
|
||||
#endif
|
||||
process_handle child_pipes[3][2];
|
||||
process_stream_t child_pipes[3][2];
|
||||
} process_t;
|
||||
|
||||
typedef struct process_kill_s {
|
||||
int tries;
|
||||
uint32_t start_time;
|
||||
process_handle_t handle;
|
||||
struct process_kill_s *next;
|
||||
} process_kill_t;
|
||||
|
||||
typedef struct {
|
||||
bool stop;
|
||||
SDL_mutex *mutex;
|
||||
SDL_cond *has_work, *work_done;
|
||||
SDL_Thread *worker_thread;
|
||||
process_kill_t *head;
|
||||
process_kill_t *tail;
|
||||
} process_kill_list_t;
|
||||
|
||||
typedef enum {
|
||||
SIGNAL_KILL,
|
||||
SIGNAL_TERM,
|
||||
|
@ -63,60 +99,242 @@ typedef enum {
|
|||
REDIRECT_PARENT = -3,
|
||||
} filed_e;
|
||||
|
||||
static void close_fd(process_stream_t *handle) {
|
||||
if (*handle && *handle != HANDLE_INVALID) {
|
||||
#ifdef _WIN32
|
||||
static volatile long PipeSerialNumber;
|
||||
static void close_fd(HANDLE* handle) { if (*handle) CloseHandle(*handle); *handle = INVALID_HANDLE_VALUE; }
|
||||
CloseHandle(*handle);
|
||||
#else
|
||||
static void close_fd(int* fd) { if (*fd) close(*fd); *fd = 0; }
|
||||
close(*handle);
|
||||
#endif
|
||||
*handle = HANDLE_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int kill_list_worker(void *ud);
|
||||
|
||||
|
||||
static void kill_list_free(process_kill_list_t *list) {
|
||||
process_kill_t *node, *temp;
|
||||
SDL_WaitThread(list->worker_thread, NULL);
|
||||
SDL_DestroyMutex(list->mutex);
|
||||
SDL_DestroyCond(list->has_work);
|
||||
SDL_DestroyCond(list->work_done);
|
||||
node = list->head;
|
||||
while (node) {
|
||||
temp = node;
|
||||
node = node->next;
|
||||
free(temp);
|
||||
}
|
||||
memset(list, 0, sizeof(process_kill_list_t));
|
||||
}
|
||||
|
||||
|
||||
static bool kill_list_init(process_kill_list_t *list) {
|
||||
memset(list, 0, sizeof(process_kill_list_t));
|
||||
list->mutex = SDL_CreateMutex();
|
||||
list->has_work = SDL_CreateCond();
|
||||
list->work_done = SDL_CreateCond();
|
||||
list->head = list->tail = NULL;
|
||||
list->stop = false;
|
||||
if (!list->mutex || !list->has_work || !list->work_done) {
|
||||
kill_list_free(list);
|
||||
return false;
|
||||
}
|
||||
list->worker_thread = SDL_CreateThread(kill_list_worker, "process_kill", list);
|
||||
if (!list->worker_thread) {
|
||||
kill_list_free(list);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static void kill_list_push(process_kill_list_t *list, process_kill_t *task) {
|
||||
if (!list) return;
|
||||
task->next = NULL;
|
||||
if (list->tail) {
|
||||
list->tail->next = task;
|
||||
list->tail = task;
|
||||
} else {
|
||||
list->head = list->tail = task;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void kill_list_pop(process_kill_list_t *list) {
|
||||
if (!list || !list->head) return;
|
||||
process_kill_t *head = list->head;
|
||||
list->head = list->head->next;
|
||||
if (!list->head) list->tail = NULL;
|
||||
head->next = NULL;
|
||||
}
|
||||
|
||||
|
||||
static void kill_list_wait_all(process_kill_list_t *list) {
|
||||
SDL_LockMutex(list->mutex);
|
||||
// wait until list is empty
|
||||
while (list->head)
|
||||
SDL_CondWait(list->work_done, list->mutex);
|
||||
// tell the worker to stop
|
||||
list->stop = true;
|
||||
SDL_CondSignal(list->has_work);
|
||||
SDL_UnlockMutex(list->mutex);
|
||||
}
|
||||
|
||||
|
||||
static void process_handle_close(process_handle_t *handle) {
|
||||
#ifdef _WIN32
|
||||
if (*handle) {
|
||||
CloseHandle(*handle);
|
||||
*handle = NULL;
|
||||
}
|
||||
#endif
|
||||
(void) 0;
|
||||
}
|
||||
|
||||
|
||||
static bool process_handle_is_running(process_handle_t handle, int *status) {
|
||||
#ifdef _WIN32
|
||||
DWORD s;
|
||||
if (GetExitCodeProcess(handle, &s) && s != STILL_ACTIVE) {
|
||||
if (status != NULL)
|
||||
*status = s;
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
int s;
|
||||
if (waitpid(handle, &s, WNOHANG) != 0) {
|
||||
if (status != NULL)
|
||||
*status = WEXITSTATUS(s);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool process_handle_signal(process_handle_t handle, signal_e sig) {
|
||||
#if _WIN32
|
||||
switch(sig) {
|
||||
case SIGNAL_TERM: return GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, GetProcessId(handle));
|
||||
case SIGNAL_KILL: return TerminateProcess(handle, -1);
|
||||
case SIGNAL_INTERRUPT: return DebugBreakProcess(handle);
|
||||
}
|
||||
#else
|
||||
switch (sig) {
|
||||
case SIGNAL_TERM: return kill(-handle, SIGTERM) == 0; break;
|
||||
case SIGNAL_KILL: return kill(-handle, SIGKILL) == 0; break;
|
||||
case SIGNAL_INTERRUPT: return kill(-handle, SIGINT) == 0; break;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static int kill_list_worker(void *ud) {
|
||||
process_kill_list_t *list = (process_kill_list_t *) ud;
|
||||
process_kill_t *current_task;
|
||||
uint32_t delay;
|
||||
|
||||
while (true) {
|
||||
SDL_LockMutex(list->mutex);
|
||||
|
||||
// wait until we have work to do
|
||||
while (!list->head && !list->stop)
|
||||
SDL_CondWait(list->has_work, list->mutex); // LOCK MUTEX
|
||||
|
||||
if (list->stop) break;
|
||||
|
||||
while ((current_task = list->head)) {
|
||||
if ((SDL_GetTicks() - current_task->start_time) < PROCESS_TERM_DELAY)
|
||||
break;
|
||||
kill_list_pop(list);
|
||||
if (process_handle_is_running(current_task->handle, NULL)) {
|
||||
if (current_task->tries < PROCESS_TERM_TRIES)
|
||||
process_handle_signal(current_task->handle, SIGNAL_TERM);
|
||||
else if (current_task->tries == PROCESS_TERM_TRIES)
|
||||
process_handle_signal(current_task->handle, SIGNAL_KILL);
|
||||
else
|
||||
goto free_task;
|
||||
|
||||
// add the task back into the queue
|
||||
current_task->tries++;
|
||||
current_task->start_time = SDL_GetTicks();
|
||||
kill_list_push(list, current_task);
|
||||
} else {
|
||||
free_task:
|
||||
SDL_CondSignal(list->work_done);
|
||||
process_handle_close(¤t_task->handle);
|
||||
free(current_task);
|
||||
}
|
||||
}
|
||||
delay = list->head ? (list->head->start_time + PROCESS_TERM_DELAY) - SDL_GetTicks() : 0;
|
||||
SDL_UnlockMutex(list->mutex);
|
||||
SDL_Delay(delay);
|
||||
}
|
||||
SDL_UnlockMutex(list->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int push_error_string(lua_State *L, process_error_t err) {
|
||||
#ifdef _WIN32
|
||||
char *msg = NULL;
|
||||
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM
|
||||
| FORMAT_MESSAGE_ALLOCATE_BUFFER
|
||||
| FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL,
|
||||
err,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(char *) &msg,
|
||||
0,
|
||||
NULL);
|
||||
if (!msg)
|
||||
return 0;
|
||||
|
||||
lua_pushstring(L, msg);
|
||||
LocalFree(msg);
|
||||
#else
|
||||
lua_pushstring(L, strerror(err));
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void push_error(lua_State *L, const char *extra, process_error_t err) {
|
||||
const char *msg = "unknown error";
|
||||
extra = extra != NULL ? extra : "error";
|
||||
if (push_error_string(L, err))
|
||||
msg = lua_tostring(L, -1);
|
||||
lua_pushfstring(L, "%s: %s (%d)", extra, msg, err);
|
||||
}
|
||||
|
||||
static bool poll_process(process_t* proc, int timeout) {
|
||||
uint32_t ticks;
|
||||
|
||||
if (!proc->running)
|
||||
return false;
|
||||
uint32_t ticks = SDL_GetTicks();
|
||||
|
||||
if (timeout == WAIT_DEADLINE)
|
||||
timeout = proc->deadline;
|
||||
|
||||
ticks = SDL_GetTicks();
|
||||
do {
|
||||
#ifdef _WIN32
|
||||
DWORD exit_code = -1;
|
||||
if (!GetExitCodeProcess( proc->process_information.hProcess, &exit_code ) || exit_code != STILL_ACTIVE) {
|
||||
proc->returncode = exit_code;
|
||||
proc->running = false;
|
||||
break;
|
||||
}
|
||||
#else
|
||||
int status;
|
||||
pid_t wait_response = waitpid(proc->pid, &status, WNOHANG);
|
||||
if (wait_response != 0) {
|
||||
if (!process_handle_is_running(PROCESS_GET_HANDLE(proc), &status)) {
|
||||
proc->running = false;
|
||||
proc->returncode = WEXITSTATUS(status);
|
||||
proc->returncode = status;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
if (timeout)
|
||||
SDL_Delay(5);
|
||||
SDL_Delay(timeout >= 5 ? 5 : 0);
|
||||
} while (timeout == WAIT_INFINITE || (int)SDL_GetTicks() - ticks < timeout);
|
||||
|
||||
return proc->running;
|
||||
}
|
||||
|
||||
static bool signal_process(process_t* proc, signal_e sig) {
|
||||
bool terminate = false;
|
||||
#if _WIN32
|
||||
switch(sig) {
|
||||
case SIGNAL_TERM: terminate = GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, GetProcessId(proc->process_information.hProcess)); break;
|
||||
case SIGNAL_KILL: terminate = TerminateProcess(proc->process_information.hProcess, -1); break;
|
||||
case SIGNAL_INTERRUPT: DebugBreakProcess(proc->process_information.hProcess); break;
|
||||
}
|
||||
#else
|
||||
switch (sig) {
|
||||
case SIGNAL_TERM: terminate = kill(-proc->pid, SIGTERM) == 1; break;
|
||||
case SIGNAL_KILL: terminate = kill(-proc->pid, SIGKILL) == 1; break;
|
||||
case SIGNAL_INTERRUPT: kill(-proc->pid, SIGINT); break;
|
||||
}
|
||||
#endif
|
||||
if (terminate)
|
||||
if (process_handle_signal(PROCESS_GET_HANDLE(proc), sig))
|
||||
poll_process(proc, WAIT_NONE);
|
||||
return true;
|
||||
}
|
||||
|
@ -135,6 +353,10 @@ static int process_start(lua_State* L) {
|
|||
lua_pushinteger(L, (int)lua_objlen(L, 1));
|
||||
#endif
|
||||
cmd_len = luaL_checknumber(L, -1); lua_pop(L, 1);
|
||||
if (!cmd_len)
|
||||
// we have not allocated anything here yet, so we can skip cleanup code
|
||||
// don't do this anywhere else!
|
||||
return luaL_argerror(L, 1,"table cannot be empty");
|
||||
for (size_t i = 1; i <= cmd_len; ++i) {
|
||||
lua_pushinteger(L, i);
|
||||
lua_rawget(L, 1);
|
||||
|
@ -145,9 +367,6 @@ static int process_start(lua_State* L) {
|
|||
cmd[0] = luaL_checkstring(L, 1);
|
||||
cmd_len = 1;
|
||||
}
|
||||
// this should never trip
|
||||
// but if it does we are in deep trouble
|
||||
assert(cmd[0]);
|
||||
|
||||
if (arg_len > 1) {
|
||||
lua_getfield(L, 2, "env");
|
||||
|
@ -173,7 +392,7 @@ static int process_start(lua_State* L) {
|
|||
lua_getfield(L, 2, "stderr"); new_fds[STDERR_FD] = luaL_optnumber(L, -1, STDERR_FD);
|
||||
for (int stream = STDIN_FD; stream <= STDERR_FD; ++stream) {
|
||||
if (new_fds[stream] > STDERR_FD || new_fds[stream] < REDIRECT_PARENT) {
|
||||
lua_pushfstring(L, "redirect to handles, FILE* and paths are not supported");
|
||||
lua_pushfstring(L, "error: redirect to handles, FILE* and paths are not supported");
|
||||
retval = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
@ -207,20 +426,22 @@ static int process_start(lua_State* L) {
|
|||
self->child_pipes[i][0] = CreateNamedPipeA(pipeNameBuffer, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
|
||||
PIPE_TYPE_BYTE | PIPE_WAIT, 1, READ_BUF_SIZE, READ_BUF_SIZE, 0, NULL);
|
||||
if (self->child_pipes[i][0] == INVALID_HANDLE_VALUE) {
|
||||
lua_pushfstring(L, "Error creating read pipe: %d.", GetLastError());
|
||||
push_error(L, "cannot create pipe", GetLastError());
|
||||
retval = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
self->child_pipes[i][1] = CreateFileA(pipeNameBuffer, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (self->child_pipes[i][1] == INVALID_HANDLE_VALUE) {
|
||||
// prevent CloseHandle from messing up error codes
|
||||
DWORD err = GetLastError();
|
||||
CloseHandle(self->child_pipes[i][0]);
|
||||
lua_pushfstring(L, "Error creating write pipe: %d.", GetLastError());
|
||||
push_error(L, "cannot open pipe", err);
|
||||
retval = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
if (!SetHandleInformation(self->child_pipes[i][i == STDIN_FD ? 1 : 0], HANDLE_FLAG_INHERIT, 0) ||
|
||||
!SetHandleInformation(self->child_pipes[i][i == STDIN_FD ? 0 : 1], HANDLE_FLAG_INHERIT, 1)) {
|
||||
lua_pushfstring(L, "Error inheriting pipes: %d.", GetLastError());
|
||||
push_error(L, "cannot set pipe permission", GetLastError());
|
||||
retval = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
@ -284,7 +505,7 @@ static int process_start(lua_State* L) {
|
|||
if (env_len > 0)
|
||||
MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, environmentBlock, offset, (LPWSTR)wideEnvironmentBlock, sizeof(wideEnvironmentBlock));
|
||||
if (!CreateProcess(NULL, commandLine, NULL, NULL, true, (detach ? DETACHED_PROCESS : CREATE_NO_WINDOW) | CREATE_UNICODE_ENVIRONMENT, env_len > 0 ? wideEnvironmentBlock : NULL, cwd, &siStartInfo, &self->process_information)) {
|
||||
lua_pushfstring(L, "Error creating a process: %d.", GetLastError());
|
||||
push_error(L, NULL, GetLastError());
|
||||
retval = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
@ -293,19 +514,33 @@ static int process_start(lua_State* L) {
|
|||
CloseHandle(self->process_information.hProcess);
|
||||
CloseHandle(self->process_information.hThread);
|
||||
#else
|
||||
int control_pipe[2] = { 0 };
|
||||
for (int i = 0; i < 3; ++i) { // Make only the parents fd's non-blocking. Children should block.
|
||||
if (pipe(self->child_pipes[i]) || fcntl(self->child_pipes[i][i == STDIN_FD ? 1 : 0], F_SETFL, O_NONBLOCK) == -1) {
|
||||
lua_pushfstring(L, "Error creating pipes: %s", strerror(errno));
|
||||
push_error(L, "cannot create pipe", errno);
|
||||
retval = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
// create a pipe to get the exit code of exec()
|
||||
if (pipe(control_pipe) == -1) {
|
||||
lua_pushfstring(L, "Error creating control pipe: %s", strerror(errno));
|
||||
retval = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
if (fcntl(control_pipe[1], F_SETFD, FD_CLOEXEC) == -1) {
|
||||
lua_pushfstring(L, "Error setting FD_CLOEXEC: %s", strerror(errno));
|
||||
retval = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
self->pid = (long)fork();
|
||||
if (self->pid < 0) {
|
||||
lua_pushfstring(L, "Error running fork: %s.", strerror(errno));
|
||||
push_error(L, "cannot create child process", errno);
|
||||
retval = -1;
|
||||
goto cleanup;
|
||||
} else if (!self->pid) {
|
||||
// child process
|
||||
if (!detach)
|
||||
setpgid(0,0);
|
||||
for (int stream = 0; stream < 3; ++stream) {
|
||||
|
@ -320,18 +555,45 @@ static int process_start(lua_State* L) {
|
|||
for (set = 0; set < env_len && setenv(env_names[set], env_values[set], 1) == 0; ++set);
|
||||
if (set == env_len && (!detach || setsid() != -1) && (!cwd || chdir(cwd) != -1))
|
||||
execvp(cmd[0], (char** const)cmd);
|
||||
const char* msg = strerror(errno);
|
||||
size_t result = write(STDERR_FD, msg, strlen(msg)+1);
|
||||
_exit(result == strlen(msg)+1 ? -1 : -2);
|
||||
write(control_pipe[1], &errno, sizeof(errno));
|
||||
_exit(-1);
|
||||
}
|
||||
// close our write side so we can read from child
|
||||
close(control_pipe[1]);
|
||||
control_pipe[1] = 0;
|
||||
|
||||
// wait for child process to respond
|
||||
int sz, process_rc;
|
||||
while ((sz = read(control_pipe[0], &process_rc, sizeof(int))) == -1) {
|
||||
if (errno == EPIPE) break;
|
||||
if (errno != EINTR) {
|
||||
lua_pushfstring(L, "Error getting child process status: %s", strerror(errno));
|
||||
retval = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (sz) {
|
||||
// read something from pipe; exec failed
|
||||
int status;
|
||||
waitpid(self->pid, &status, 0);
|
||||
lua_pushfstring(L, "Error creating child process: %s", strerror(process_rc));
|
||||
retval = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
#endif
|
||||
cleanup:
|
||||
#ifndef _WIN32
|
||||
if (control_pipe[0]) close(control_pipe[0]);
|
||||
if (control_pipe[1]) close(control_pipe[1]);
|
||||
#endif
|
||||
for (size_t i = 0; i < env_len; ++i) {
|
||||
free((char*)env_names[i]);
|
||||
free((char*)env_values[i]);
|
||||
}
|
||||
for (int stream = 0; stream < 3; ++stream) {
|
||||
process_handle* pipe = &self->child_pipes[stream][stream == STDIN_FD ? 0 : 1];
|
||||
process_stream_t* pipe = &self->child_pipes[stream][stream == STDIN_FD ? 0 : 1];
|
||||
if (*pipe) {
|
||||
close_fd(pipe);
|
||||
}
|
||||
|
@ -347,7 +609,7 @@ static int g_read(lua_State* L, int stream, unsigned long read_size) {
|
|||
process_t* self = (process_t*) luaL_checkudata(L, 1, API_TYPE_PROCESS);
|
||||
long length = 0;
|
||||
if (stream != STDOUT_FD && stream != STDERR_FD)
|
||||
return luaL_error(L, "redirect to handles, FILE* and paths are not supported");
|
||||
return luaL_error(L, "error: redirect to handles, FILE* and paths are not supported");
|
||||
#if _WIN32
|
||||
int writable_stream_idx = stream - 1;
|
||||
if (self->reading[writable_stream_idx] || !ReadFile(self->child_pipes[stream][0], self->buffer[writable_stream_idx], READ_BUF_SIZE, NULL, &self->overlapped[writable_stream_idx])) {
|
||||
|
@ -395,9 +657,9 @@ static int f_write(lua_State* L) {
|
|||
#if _WIN32
|
||||
DWORD dwWritten;
|
||||
if (!WriteFile(self->child_pipes[STDIN_FD][1], data, data_size, &dwWritten, NULL)) {
|
||||
int lastError = GetLastError();
|
||||
push_error(L, NULL, GetLastError());
|
||||
signal_process(self, SIGNAL_TERM);
|
||||
return luaL_error(L, "error writing to process: %d", lastError);
|
||||
return lua_error(L);
|
||||
}
|
||||
length = dwWritten;
|
||||
#else
|
||||
|
@ -405,9 +667,9 @@ static int f_write(lua_State* L) {
|
|||
if (length < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
|
||||
length = 0;
|
||||
else if (length < 0) {
|
||||
const char* lastError = strerror(errno);
|
||||
push_error(L, "cannot write to child process", errno);
|
||||
signal_process(self, SIGNAL_TERM);
|
||||
return luaL_error(L, "error writing to process: %s", lastError);
|
||||
return lua_error(L);
|
||||
}
|
||||
#endif
|
||||
lua_pushinteger(L, length);
|
||||
|
@ -424,15 +686,7 @@ static int f_close_stream(lua_State* L) {
|
|||
|
||||
// Generic stuff below here.
|
||||
static int process_strerror(lua_State* L) {
|
||||
#if _WIN32
|
||||
return 1;
|
||||
#endif
|
||||
int error_code = luaL_checknumber(L, 1);
|
||||
if (error_code < 0)
|
||||
lua_pushstring(L, strerror(error_code));
|
||||
else
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
return push_error_string(L, luaL_checknumber(L, 1));
|
||||
}
|
||||
|
||||
static int f_tostring(lua_State* L) {
|
||||
|
@ -484,14 +738,39 @@ static int self_signal(lua_State* L, signal_e sig) {
|
|||
static int f_terminate(lua_State* L) { return self_signal(L, SIGNAL_TERM); }
|
||||
static int f_kill(lua_State* L) { return self_signal(L, SIGNAL_KILL); }
|
||||
static int f_interrupt(lua_State* L) { return self_signal(L, SIGNAL_INTERRUPT); }
|
||||
|
||||
static int f_gc(lua_State* L) {
|
||||
process_kill_list_t *list = NULL;
|
||||
process_kill_t *p = NULL;
|
||||
process_t* self = (process_t*) luaL_checkudata(L, 1, API_TYPE_PROCESS);
|
||||
if (!self->detached)
|
||||
|
||||
// get the kill_list for the lua_State
|
||||
if (lua_getfield(L, LUA_REGISTRYINDEX, PROCESS_KILL_LIST_NAME) == LUA_TUSERDATA)
|
||||
list = (process_kill_list_t *) lua_touserdata(L, -1);
|
||||
|
||||
if (poll_process(self, 0) && !self->detached) {
|
||||
// attempt to kill the process if still running and not detached
|
||||
signal_process(self, SIGNAL_TERM);
|
||||
if (!list || !list->worker_thread || !(p = malloc(sizeof(process_kill_t)))) {
|
||||
// use synchronous waiting
|
||||
if (poll_process(self, PROCESS_TERM_DELAY)) {
|
||||
signal_process(self, SIGNAL_KILL);
|
||||
poll_process(self, PROCESS_TERM_DELAY);
|
||||
}
|
||||
} else {
|
||||
// put the handle into a queue for asynchronous waiting
|
||||
p->handle = PROCESS_GET_HANDLE(self);
|
||||
p->start_time = SDL_GetTicks();
|
||||
p->tries = 1;
|
||||
SDL_LockMutex(list->mutex);
|
||||
kill_list_push(list, p);
|
||||
SDL_CondSignal(list->has_work);
|
||||
SDL_UnlockMutex(list->mutex);
|
||||
}
|
||||
}
|
||||
close_fd(&self->child_pipes[STDIN_FD ][1]);
|
||||
close_fd(&self->child_pipes[STDOUT_FD][0]);
|
||||
close_fd(&self->child_pipes[STDERR_FD][0]);
|
||||
poll_process(self, 10);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -501,11 +780,20 @@ static int f_running(lua_State* L) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
static const struct luaL_Reg lib[] = {
|
||||
static int process_gc(lua_State *L) {
|
||||
process_kill_list_t *list = NULL;
|
||||
// get the kill_list for the lua_State
|
||||
if (lua_getfield(L, LUA_REGISTRYINDEX, PROCESS_KILL_LIST_NAME) == LUA_TUSERDATA) {
|
||||
list = (process_kill_list_t *) lua_touserdata(L, -1);
|
||||
kill_list_wait_all(list);
|
||||
kill_list_free(list);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct luaL_Reg process_metatable[] = {
|
||||
{"__gc", f_gc},
|
||||
{"__tostring", f_tostring},
|
||||
{"start", process_start},
|
||||
{"strerror", process_strerror},
|
||||
{"pid", f_pid},
|
||||
{"returncode", f_returncode},
|
||||
{"read", f_read},
|
||||
|
@ -521,12 +809,32 @@ static const struct luaL_Reg lib[] = {
|
|||
{NULL, NULL}
|
||||
};
|
||||
|
||||
static const struct luaL_Reg lib[] = {
|
||||
{ "start", process_start },
|
||||
{ "strerror", process_strerror },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
int luaopen_process(lua_State *L) {
|
||||
process_kill_list_t *list = lua_newuserdata(L, sizeof(process_kill_list_t));
|
||||
if (kill_list_init(list))
|
||||
lua_setfield(L, LUA_REGISTRYINDEX, PROCESS_KILL_LIST_NAME);
|
||||
else
|
||||
lua_pop(L, 1); // discard the list
|
||||
|
||||
// create the process metatable
|
||||
luaL_newmetatable(L, API_TYPE_PROCESS);
|
||||
luaL_setfuncs(L, lib, 0);
|
||||
luaL_setfuncs(L, process_metatable, 0);
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setfield(L, -2, "__index");
|
||||
|
||||
// create the process library
|
||||
luaL_newlib(L, lib);
|
||||
lua_newtable(L);
|
||||
lua_pushcfunction(L, process_gc);
|
||||
lua_setfield(L, -2, "__gc");
|
||||
lua_setmetatable(L, -2);
|
||||
|
||||
API_CONSTANT_DEFINE(L, -1, "WAIT_INFINITE", WAIT_INFINITE);
|
||||
API_CONSTANT_DEFINE(L, -1, "WAIT_DEADLINE", WAIT_DEADLINE);
|
||||
|
||||
|
|
|
@ -91,6 +91,9 @@ static int regex_gmatch_iterator(lua_State *L) {
|
|||
int total_results = ovector_count * 2;
|
||||
size_t last_offset = 0;
|
||||
for (int i = index; i < total_results; i+=2) {
|
||||
if (ovector[i] == ovector[i+1])
|
||||
lua_pushinteger(L, ovector[i] + 1);
|
||||
else
|
||||
lua_pushlstring(L, state->subject+ovector[i], ovector[i+1] - ovector[i]);
|
||||
last_offset = ovector[i+1];
|
||||
total++;
|
||||
|
|
|
@ -90,7 +90,7 @@ static int f_font_load(lua_State *L) {
|
|||
return ret_code;
|
||||
|
||||
RenFont** font = lua_newuserdata(L, sizeof(RenFont*));
|
||||
*font = ren_font_load(filename, size, antialiasing, hinting, style);
|
||||
*font = ren_font_load(&window_renderer, filename, size, antialiasing, hinting, style);
|
||||
if (!*font)
|
||||
return luaL_error(L, "failed to load font");
|
||||
luaL_setmetatable(L, API_TYPE_FONT);
|
||||
|
@ -130,7 +130,7 @@ static int f_font_copy(lua_State *L) {
|
|||
}
|
||||
for (int i = 0; i < FONT_FALLBACK_MAX && fonts[i]; ++i) {
|
||||
RenFont** font = lua_newuserdata(L, sizeof(RenFont*));
|
||||
*font = ren_font_copy(fonts[i], size, antialiasing, hinting, style);
|
||||
*font = ren_font_copy(&window_renderer, fonts[i], size, antialiasing, hinting, style);
|
||||
if (!*font)
|
||||
return luaL_error(L, "failed to copy font");
|
||||
luaL_setmetatable(L, API_TYPE_FONT);
|
||||
|
@ -198,7 +198,7 @@ static int f_font_get_width(lua_State *L) {
|
|||
size_t len;
|
||||
const char *text = luaL_checklstring(L, 2, &len);
|
||||
|
||||
lua_pushnumber(L, ren_font_group_get_width(fonts, text, len));
|
||||
lua_pushnumber(L, ren_font_group_get_width(&window_renderer, fonts, text, len));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -217,7 +217,7 @@ static int f_font_get_size(lua_State *L) {
|
|||
static int f_font_set_size(lua_State *L) {
|
||||
RenFont* fonts[FONT_FALLBACK_MAX]; font_retrieve(L, fonts, 1);
|
||||
float size = luaL_checknumber(L, 2);
|
||||
ren_font_group_set_size(fonts, size);
|
||||
ren_font_group_set_size(&window_renderer, fonts, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -276,7 +276,7 @@ static int f_show_debug(lua_State *L) {
|
|||
|
||||
static int f_get_size(lua_State *L) {
|
||||
int w, h;
|
||||
ren_get_size(&w, &h);
|
||||
ren_get_size(&window_renderer, &w, &h);
|
||||
lua_pushnumber(L, w);
|
||||
lua_pushnumber(L, h);
|
||||
return 2;
|
||||
|
@ -284,13 +284,13 @@ static int f_get_size(lua_State *L) {
|
|||
|
||||
|
||||
static int f_begin_frame(UNUSED lua_State *L) {
|
||||
rencache_begin_frame();
|
||||
rencache_begin_frame(&window_renderer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int f_end_frame(UNUSED lua_State *L) {
|
||||
rencache_end_frame();
|
||||
rencache_end_frame(&window_renderer);
|
||||
// clear the font reference table
|
||||
lua_newtable(L);
|
||||
lua_rawseti(L, LUA_REGISTRYINDEX, RENDERER_FONT_REF);
|
||||
|
@ -311,7 +311,7 @@ static int f_set_clip_rect(lua_State *L) {
|
|||
lua_Number w = luaL_checknumber(L, 3);
|
||||
lua_Number h = luaL_checknumber(L, 4);
|
||||
RenRect rect = rect_to_grid(x, y, w, h);
|
||||
rencache_set_clip_rect(rect);
|
||||
rencache_set_clip_rect(&window_renderer, rect);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -323,7 +323,7 @@ static int f_draw_rect(lua_State *L) {
|
|||
lua_Number h = luaL_checknumber(L, 4);
|
||||
RenRect rect = rect_to_grid(x, y, w, h);
|
||||
RenColor color = checkcolor(L, 5, 255);
|
||||
rencache_draw_rect(rect, color);
|
||||
rencache_draw_rect(&window_renderer, rect, color);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -345,10 +345,10 @@ static int f_draw_text(lua_State *L) {
|
|||
|
||||
size_t len;
|
||||
const char *text = luaL_checklstring(L, 2, &len);
|
||||
float x = luaL_checknumber(L, 3);
|
||||
double x = luaL_checknumber(L, 3);
|
||||
int y = luaL_checknumber(L, 4);
|
||||
RenColor color = checkcolor(L, 5, 255);
|
||||
x = rencache_draw_text(fonts, text, len, x, y, color);
|
||||
x = rencache_draw_text(&window_renderer, fonts, text, len, x, y, color);
|
||||
lua_pushnumber(L, x);
|
||||
return 1;
|
||||
}
|
||||
|
|
228
src/api/system.c
228
src/api/system.c
|
@ -7,6 +7,7 @@
|
|||
#include <sys/stat.h>
|
||||
#include "api.h"
|
||||
#include "../rencache.h"
|
||||
#include "../renwindow.h"
|
||||
#ifdef _WIN32
|
||||
#include <direct.h>
|
||||
#include <windows.h>
|
||||
|
@ -36,9 +37,6 @@
|
|||
#endif
|
||||
#endif
|
||||
|
||||
extern SDL_Window *window;
|
||||
|
||||
|
||||
static const char* button_name(int button) {
|
||||
switch (button) {
|
||||
case SDL_BUTTON_LEFT : return "left";
|
||||
|
@ -76,7 +74,7 @@ static SDL_HitTestResult SDLCALL hit_test(SDL_Window *window, const SDL_Point *p
|
|||
const int controls_width = hit_info->controls_width;
|
||||
int w, h;
|
||||
|
||||
SDL_GetWindowSize(window, &w, &h);
|
||||
SDL_GetWindowSize(window_renderer.window, &w, &h);
|
||||
|
||||
if (pt->y < hit_info->title_height &&
|
||||
#if RESIZE_FROM_TOP
|
||||
|
@ -188,7 +186,7 @@ top:
|
|||
|
||||
case SDL_WINDOWEVENT:
|
||||
if (e.window.event == SDL_WINDOWEVENT_RESIZED) {
|
||||
ren_resize_window();
|
||||
ren_resize_window(&window_renderer);
|
||||
lua_pushstring(L, "resized");
|
||||
/* The size below will be in points. */
|
||||
lua_pushinteger(L, e.window.data1);
|
||||
|
@ -326,7 +324,7 @@ top:
|
|||
return 3;
|
||||
|
||||
case SDL_FINGERDOWN:
|
||||
SDL_GetWindowSize(window, &w, &h);
|
||||
SDL_GetWindowSize(window_renderer.window, &w, &h);
|
||||
|
||||
lua_pushstring(L, "touchpressed");
|
||||
lua_pushinteger(L, (lua_Integer)(e.tfinger.x * w));
|
||||
|
@ -335,7 +333,7 @@ top:
|
|||
return 4;
|
||||
|
||||
case SDL_FINGERUP:
|
||||
SDL_GetWindowSize(window, &w, &h);
|
||||
SDL_GetWindowSize(window_renderer.window, &w, &h);
|
||||
|
||||
lua_pushstring(L, "touchreleased");
|
||||
lua_pushinteger(L, (lua_Integer)(e.tfinger.x * w));
|
||||
|
@ -351,7 +349,7 @@ top:
|
|||
e.tfinger.dx += event_plus.tfinger.dx;
|
||||
e.tfinger.dy += event_plus.tfinger.dy;
|
||||
}
|
||||
SDL_GetWindowSize(window, &w, &h);
|
||||
SDL_GetWindowSize(window_renderer.window, &w, &h);
|
||||
|
||||
lua_pushstring(L, "touchmoved");
|
||||
lua_pushinteger(L, (lua_Integer)(e.tfinger.x * w));
|
||||
|
@ -360,6 +358,21 @@ top:
|
|||
lua_pushinteger(L, (lua_Integer)(e.tfinger.dy * h));
|
||||
lua_pushinteger(L, e.tfinger.fingerId);
|
||||
return 6;
|
||||
case SDL_APP_WILLENTERFOREGROUND:
|
||||
case SDL_APP_DIDENTERFOREGROUND:
|
||||
#ifdef LITE_USE_SDL_RENDERER
|
||||
rencache_invalidate();
|
||||
#else
|
||||
SDL_UpdateWindowSurface(window_renderer.window);
|
||||
#endif
|
||||
lua_pushstring(L, e.type == SDL_APP_WILLENTERFOREGROUND ? "enteringforeground" : "enteredforeground");
|
||||
return 1;
|
||||
case SDL_APP_WILLENTERBACKGROUND:
|
||||
lua_pushstring(L, "enteringbackground");
|
||||
return 1;
|
||||
case SDL_APP_DIDENTERBACKGROUND:
|
||||
lua_pushstring(L, "enteredbackground");
|
||||
return 1;
|
||||
|
||||
default:
|
||||
goto top;
|
||||
|
@ -415,7 +428,7 @@ static int f_set_cursor(lua_State *L) {
|
|||
|
||||
static int f_set_window_title(lua_State *L) {
|
||||
const char *title = luaL_checkstring(L, 1);
|
||||
SDL_SetWindowTitle(window, title);
|
||||
SDL_SetWindowTitle(window_renderer.window, title);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -425,39 +438,39 @@ enum { WIN_NORMAL, WIN_MINIMIZED, WIN_MAXIMIZED, WIN_FULLSCREEN };
|
|||
|
||||
static int f_set_window_mode(lua_State *L) {
|
||||
int n = luaL_checkoption(L, 1, "normal", window_opts);
|
||||
SDL_SetWindowFullscreen(window,
|
||||
SDL_SetWindowFullscreen(window_renderer.window,
|
||||
n == WIN_FULLSCREEN ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
|
||||
if (n == WIN_NORMAL) { SDL_RestoreWindow(window); }
|
||||
if (n == WIN_MAXIMIZED) { SDL_MaximizeWindow(window); }
|
||||
if (n == WIN_MINIMIZED) { SDL_MinimizeWindow(window); }
|
||||
if (n == WIN_NORMAL) { SDL_RestoreWindow(window_renderer.window); }
|
||||
if (n == WIN_MAXIMIZED) { SDL_MaximizeWindow(window_renderer.window); }
|
||||
if (n == WIN_MINIMIZED) { SDL_MinimizeWindow(window_renderer.window); }
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int f_set_window_bordered(lua_State *L) {
|
||||
int bordered = lua_toboolean(L, 1);
|
||||
SDL_SetWindowBordered(window, bordered);
|
||||
SDL_SetWindowBordered(window_renderer.window, bordered);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int f_set_window_hit_test(lua_State *L) {
|
||||
if (lua_gettop(L) == 0) {
|
||||
SDL_SetWindowHitTest(window, NULL, NULL);
|
||||
SDL_SetWindowHitTest(window_renderer.window, NULL, NULL);
|
||||
return 0;
|
||||
}
|
||||
window_hit_info->title_height = luaL_checknumber(L, 1);
|
||||
window_hit_info->controls_width = luaL_checknumber(L, 2);
|
||||
window_hit_info->resize_border = luaL_checknumber(L, 3);
|
||||
SDL_SetWindowHitTest(window, hit_test, window_hit_info);
|
||||
SDL_SetWindowHitTest(window_renderer.window, hit_test, window_hit_info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int f_get_window_size(lua_State *L) {
|
||||
int x, y, w, h;
|
||||
SDL_GetWindowSize(window, &w, &h);
|
||||
SDL_GetWindowPosition(window, &x, &y);
|
||||
SDL_GetWindowSize(window_renderer.window, &w, &h);
|
||||
SDL_GetWindowPosition(window_renderer.window, &x, &y);
|
||||
lua_pushinteger(L, w);
|
||||
lua_pushinteger(L, h);
|
||||
lua_pushinteger(L, x);
|
||||
|
@ -471,22 +484,22 @@ static int f_set_window_size(lua_State *L) {
|
|||
double h = luaL_checknumber(L, 2);
|
||||
double x = luaL_checknumber(L, 3);
|
||||
double y = luaL_checknumber(L, 4);
|
||||
SDL_SetWindowSize(window, w, h);
|
||||
SDL_SetWindowPosition(window, x, y);
|
||||
ren_resize_window();
|
||||
SDL_SetWindowSize(window_renderer.window, w, h);
|
||||
SDL_SetWindowPosition(window_renderer.window, x, y);
|
||||
ren_resize_window(&window_renderer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int f_window_has_focus(lua_State *L) {
|
||||
unsigned flags = SDL_GetWindowFlags(window);
|
||||
unsigned flags = SDL_GetWindowFlags(window_renderer.window);
|
||||
lua_pushboolean(L, flags & SDL_WINDOW_INPUT_FOCUS);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int f_get_window_mode(lua_State *L) {
|
||||
unsigned flags = SDL_GetWindowFlags(window);
|
||||
unsigned flags = SDL_GetWindowFlags(window_renderer.window);
|
||||
if (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) {
|
||||
lua_pushstring(L, "fullscreen");
|
||||
} else if (flags & SDL_WINDOW_MINIMIZED) {
|
||||
|
@ -524,8 +537,8 @@ static int f_raise_window(lua_State *L) {
|
|||
to allow the window to be focused. Also on wayland the raise window event
|
||||
may not always be obeyed.
|
||||
*/
|
||||
SDL_SetWindowInputFocus(window);
|
||||
SDL_RaiseWindow(window);
|
||||
SDL_SetWindowInputFocus(window_renderer.window);
|
||||
SDL_RaiseWindow(window_renderer.window);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -901,7 +914,7 @@ static int f_fuzzy_match(lua_State *L) {
|
|||
|
||||
static int f_set_window_opacity(lua_State *L) {
|
||||
double n = luaL_checknumber(L, 1);
|
||||
int r = SDL_SetWindowOpacity(window, n);
|
||||
int r = SDL_SetWindowOpacity(window_renderer.window, n);
|
||||
lua_pushboolean(L, r > -1);
|
||||
return 1;
|
||||
}
|
||||
|
@ -915,47 +928,70 @@ typedef struct lua_function_node {
|
|||
|
||||
#define P(FUNC) { "lua_" #FUNC, (fptr)(lua_##FUNC) }
|
||||
#define U(FUNC) { "luaL_" #FUNC, (fptr)(luaL_##FUNC) }
|
||||
#define S(FUNC) { #FUNC, (fptr)(FUNC) }
|
||||
static void* api_require(const char* symbol) {
|
||||
static const lua_function_node nodes[] = {
|
||||
P(atpanic), P(checkstack),
|
||||
P(close), P(concat), P(copy), P(createtable), P(dump),
|
||||
P(error), P(gc), P(getallocf), P(getfield),
|
||||
P(gethook), P(gethookcount), P(gethookmask), P(getinfo), P(getlocal),
|
||||
P(getmetatable), P(getstack), P(gettable), P(gettop), P(getupvalue),
|
||||
P(isnumber), P(isstring), P(isuserdata),
|
||||
P(load), P(newstate), P(newthread), P(next),
|
||||
#if LUA_VERSION_NUM == 501 || LUA_VERSION_NUM == 502 || LUA_VERSION_NUM == 503 || LUA_VERSION_NUM == 504
|
||||
U(addlstring), U(addstring), U(addvalue), U(argerror), U(buffinit),
|
||||
U(callmeta), U(checkany), U(checkinteger), U(checklstring),
|
||||
U(checknumber), U(checkoption), U(checkstack), U(checktype),
|
||||
U(checkudata), U(error), U(getmetafield), U(gsub), U(loadstring),
|
||||
U(newmetatable), U(newstate), U(openlibs), U(optinteger), U(optlstring),
|
||||
U(optnumber), U(pushresult), U(ref), U(unref), U(where), P(atpanic),
|
||||
P(checkstack), P(close), P(concat), P(createtable), P(dump), P(error),
|
||||
P(gc), P(getallocf), P(getfield), P(gethook), P(gethookcount),
|
||||
P(gethookmask), P(getinfo), P(getlocal), P(getmetatable), P(getstack),
|
||||
P(gettable), P(gettop), P(getupvalue), P(iscfunction), P(isnumber),
|
||||
P(isstring), P(isuserdata), P(load), P(newstate), P(newthread), P(next),
|
||||
P(pushboolean), P(pushcclosure), P(pushfstring), P(pushinteger),
|
||||
P(pushlightuserdata), P(pushlstring), P(pushnil), P(pushnumber),
|
||||
P(pushstring), P(pushthread), P(pushvalue),
|
||||
P(pushvfstring), P(rawequal), P(rawget), P(rawgeti),
|
||||
P(rawset), P(rawseti), P(resume),
|
||||
P(setallocf), P(setfield), P(sethook), P(setlocal),
|
||||
P(setmetatable), P(settable), P(settop), P(setupvalue),
|
||||
P(status), P(tocfunction), P(tointegerx), P(tolstring), P(toboolean),
|
||||
P(tonumberx), P(topointer), P(tothread), P(touserdata),
|
||||
P(type), P(typename), P(upvalueid), P(upvaluejoin), P(version), P(xmove),
|
||||
U(getmetafield), U(callmeta), U(argerror), U(checknumber), U(optnumber),
|
||||
U(checkinteger), U(checkstack), U(checktype), U(checkany),
|
||||
U(newmetatable), U(setmetatable), U(testudata), U(checkudata), U(where),
|
||||
U(error), U(fileresult), U(execresult), U(ref), U(unref), U(loadstring),
|
||||
U(newstate), U(setfuncs), U(buffinit), U(addlstring), U(addstring),
|
||||
U(addvalue), U(pushresult), U(openlibs), {"api_load_libs", (void*)(api_load_libs)},
|
||||
#if LUA_VERSION_NUM >= 502
|
||||
P(absindex), P(arith), P(callk), P(compare), P(getglobal),
|
||||
P(len), P(pcallk), P(rawgetp), P(rawlen), P(rawsetp), P(setglobal),
|
||||
P(iscfunction), P(yieldk),
|
||||
U(checkversion_), U(tolstring), U(len), U(getsubtable), U(prepbuffsize),
|
||||
U(pushresultsize), U(buffinitsize), U(checklstring), U(checkoption), U(gsub), U(loadbufferx),
|
||||
U(loadfilex), U(optinteger), U(optlstring), U(requiref), U(traceback),
|
||||
#else
|
||||
P(objlen),
|
||||
P(pushstring), P(pushthread), P(pushvalue), P(pushvfstring), P(rawequal),
|
||||
P(rawget), P(rawgeti), P(rawset), P(rawseti), P(resume), P(setallocf),
|
||||
P(setfield), P(sethook), P(setlocal), P(setmetatable), P(settable),
|
||||
P(settop), P(setupvalue), P(status), P(toboolean), P(tocfunction),
|
||||
P(tolstring), P(topointer), P(tothread), P(touserdata), P(type),
|
||||
P(typename), P(xmove), S(luaopen_base), S(luaopen_debug), S(luaopen_io),
|
||||
S(luaopen_math), S(luaopen_os), S(luaopen_package), S(luaopen_string),
|
||||
S(luaopen_table), S(api_load_libs),
|
||||
#endif
|
||||
#if LUA_VERSION_NUM >= 504
|
||||
P(newuserdatauv), P(setiuservalue), P(getiuservalue)
|
||||
#else
|
||||
P(newuserdata), P(setuservalue), P(getuservalue)
|
||||
#if LUA_VERSION_NUM == 502 || LUA_VERSION_NUM == 503 || LUA_VERSION_NUM == 504
|
||||
U(buffinitsize), U(checkversion_), U(execresult), U(fileresult),
|
||||
U(getsubtable), U(len), U(loadbufferx), U(loadfilex), U(prepbuffsize),
|
||||
U(pushresultsize), U(requiref), U(setfuncs), U(setmetatable),
|
||||
U(testudata), U(tolstring), U(traceback), P(absindex), P(arith),
|
||||
P(callk), P(compare), P(copy), P(getglobal), P(len), P(pcallk),
|
||||
P(rawgetp), P(rawlen), P(rawsetp), P(setglobal), P(tointegerx),
|
||||
P(tonumberx), P(upvalueid), P(upvaluejoin), P(version), P(yieldk),
|
||||
S(luaopen_coroutine),
|
||||
#endif
|
||||
#if LUA_VERSION_NUM == 501 || LUA_VERSION_NUM == 502 || LUA_VERSION_NUM == 503
|
||||
P(newuserdata),
|
||||
#endif
|
||||
#if LUA_VERSION_NUM == 503 || LUA_VERSION_NUM == 504
|
||||
P(geti), P(isinteger), P(isyieldable), P(rotate), P(seti),
|
||||
P(stringtonumber), S(luaopen_utf8),
|
||||
#endif
|
||||
#if LUA_VERSION_NUM == 502 || LUA_VERSION_NUM == 503
|
||||
P(getuservalue), P(setuservalue), S(luaopen_bit32),
|
||||
#endif
|
||||
#if LUA_VERSION_NUM == 501 || LUA_VERSION_NUM == 502
|
||||
P(insert), P(remove), P(replace),
|
||||
#endif
|
||||
#if LUA_VERSION_NUM == 504
|
||||
U(addgsub), U(typeerror), P(closeslot), P(getiuservalue),
|
||||
P(newuserdatauv), P(resetthread), P(setcstacklimit), P(setiuservalue),
|
||||
P(setwarnf), P(toclose), P(warning),
|
||||
#endif
|
||||
#if LUA_VERSION_NUM == 502
|
||||
U(checkunsigned), U(optunsigned), P(getctx), P(pushunsigned),
|
||||
P(tounsignedx),
|
||||
#endif
|
||||
#if LUA_VERSION_NUM == 501
|
||||
U(findtable), U(loadbuffer), U(loadfile), U(openlib), U(prepbuffer),
|
||||
U(register), U(typerror), P(call), P(cpcall), P(equal), P(getfenv),
|
||||
P(lessthan), P(objlen), P(pcall), P(setfenv), P(setlevel), P(tointeger),
|
||||
P(tonumber), P(yield),
|
||||
#endif
|
||||
|
||||
};
|
||||
for (size_t i = 0; i < sizeof(nodes) / sizeof(lua_function_node); ++i) {
|
||||
if (strcmp(nodes[i].symbol, symbol) == 0)
|
||||
|
@ -1023,17 +1059,18 @@ static int f_load_native_plugin(lua_State *L) {
|
|||
order used in the TreeView view of the project's files. Returns true iff
|
||||
path1 < path2 in the TreeView order. */
|
||||
static int f_path_compare(lua_State *L) {
|
||||
const char *path1 = luaL_checkstring(L, 1);
|
||||
size_t len1, len2;
|
||||
const char *path1 = luaL_checklstring(L, 1, &len1);
|
||||
const char *type1_s = luaL_checkstring(L, 2);
|
||||
const char *path2 = luaL_checkstring(L, 3);
|
||||
const char *path2 = luaL_checklstring(L, 3, &len2);
|
||||
const char *type2_s = luaL_checkstring(L, 4);
|
||||
const int len1 = strlen(path1), len2 = strlen(path2);
|
||||
int type1 = strcmp(type1_s, "dir") != 0;
|
||||
int type2 = strcmp(type2_s, "dir") != 0;
|
||||
/* Find the index of the common part of the path. */
|
||||
int offset = 0, i;
|
||||
size_t offset = 0, i, j;
|
||||
for (i = 0; i < len1 && i < len2; i++) {
|
||||
if (path1[i] != path2[i]) break;
|
||||
if (isdigit(path1[i])) break;
|
||||
if (path1[i] == PATHSEP) {
|
||||
offset = i + 1;
|
||||
}
|
||||
|
@ -1052,17 +1089,50 @@ static int f_path_compare(lua_State *L) {
|
|||
return 1;
|
||||
}
|
||||
/* If types are the same compare the files' path alphabetically. */
|
||||
int cfr = 0;
|
||||
int len_min = (len1 < len2 ? len1 : len2);
|
||||
for (int j = offset; j <= len_min; j++) {
|
||||
if (path1[j] == path2[j]) continue;
|
||||
if (path1[j] == 0 || path2[j] == 0) {
|
||||
cfr = (path1[j] == 0);
|
||||
} else if (path1[j] == PATHSEP || path2[j] == PATHSEP) {
|
||||
int cfr = -1;
|
||||
bool same_len = len1 == len2;
|
||||
for (i = offset, j = offset; i <= len1 && j <= len2; i++, j++) {
|
||||
if (path1[i] == 0 || path2[j] == 0) {
|
||||
if (cfr < 0) cfr = 0; // The strings are equal
|
||||
if (!same_len) {
|
||||
cfr = (path1[i] == 0);
|
||||
}
|
||||
} else if (isdigit(path1[i]) && isdigit(path2[j])) {
|
||||
size_t ii = 0, ij = 0;
|
||||
while (isdigit(path1[i+ii])) { ii++; }
|
||||
while (isdigit(path2[j+ij])) { ij++; }
|
||||
|
||||
size_t di = 0, dj = 0;
|
||||
for (size_t ai = 0; ai < ii; ++ai) {
|
||||
di = di * 10 + (path1[i+ai] - '0');
|
||||
}
|
||||
for (size_t aj = 0; aj < ij; ++aj) {
|
||||
dj = dj * 10 + (path2[j+aj] - '0');
|
||||
}
|
||||
|
||||
if (di == dj) {
|
||||
continue;
|
||||
}
|
||||
cfr = (di < dj);
|
||||
} else if (path1[i] == path2[j]) {
|
||||
continue;
|
||||
} else if (path1[i] == PATHSEP || path2[j] == PATHSEP) {
|
||||
/* For comparison we treat PATHSEP as if it was the string terminator. */
|
||||
cfr = (path1[j] == PATHSEP);
|
||||
cfr = (path1[i] == PATHSEP);
|
||||
} else {
|
||||
cfr = (path1[j] < path2[j]);
|
||||
char a = path1[i], b = path2[j];
|
||||
if (a >= 'A' && a <= 'Z') a += 32;
|
||||
if (b >= 'A' && b <= 'Z') b += 32;
|
||||
if (a == b) {
|
||||
/* If the strings have the same length, we need
|
||||
to keep the first case sensitive difference. */
|
||||
if (same_len && cfr < 0) {
|
||||
/* Give priority to lower-case characters */
|
||||
cfr = (path1[i] > path2[j]);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
cfr = (a < b);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -1071,6 +1141,15 @@ static int f_path_compare(lua_State *L) {
|
|||
}
|
||||
|
||||
|
||||
static int f_text_input(lua_State* L) {
|
||||
if (lua_toboolean(L, 1))
|
||||
SDL_StartTextInput();
|
||||
else
|
||||
SDL_StopTextInput();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const luaL_Reg lib[] = {
|
||||
{ "poll_event", f_poll_event },
|
||||
{ "wait_event", f_wait_event },
|
||||
|
@ -1104,6 +1183,7 @@ static const luaL_Reg lib[] = {
|
|||
{ "load_native_plugin", f_load_native_plugin },
|
||||
{ "path_compare", f_path_compare },
|
||||
{ "get_fs_type", f_get_fs_type },
|
||||
{ "text_input", f_text_input },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
|
|
52
src/main.c
52
src/main.c
|
@ -18,7 +18,7 @@
|
|||
#endif
|
||||
|
||||
|
||||
SDL_Window *window;
|
||||
static SDL_Window *window;
|
||||
|
||||
static double get_scale(void) {
|
||||
#ifndef __APPLE__
|
||||
|
@ -32,8 +32,18 @@ static double get_scale(void) {
|
|||
|
||||
static void get_exe_filename(char *buf, int sz) {
|
||||
#if _WIN32
|
||||
int len = GetModuleFileName(NULL, buf, sz - 1);
|
||||
buf[len] = '\0';
|
||||
int len;
|
||||
wchar_t *buf_w = malloc(sizeof(wchar_t) * sz);
|
||||
if (buf_w) {
|
||||
len = GetModuleFileNameW(NULL, buf_w, sz - 1);
|
||||
buf_w[len] = L'\0';
|
||||
// if the conversion failed we'll empty the string
|
||||
if (!WideCharToMultiByte(CP_UTF8, 0, buf_w, -1, buf, sz, NULL, NULL))
|
||||
buf[0] = '\0';
|
||||
free(buf_w);
|
||||
} else {
|
||||
buf[0] = '\0';
|
||||
}
|
||||
#elif __linux__
|
||||
char path[] = "/proc/self/exe";
|
||||
ssize_t len = readlink(path, buf, sz - 1);
|
||||
|
@ -120,11 +130,7 @@ void set_macos_bundle_resources(lua_State *L);
|
|||
#endif
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
#ifdef _WIN32
|
||||
HINSTANCE lib = LoadLibrary("user32.dll");
|
||||
int (*SetProcessDPIAware)() = (void*) GetProcAddress(lib, "SetProcessDPIAware");
|
||||
SetProcessDPIAware();
|
||||
#else
|
||||
#ifndef _WIN32
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
|
||||
|
@ -216,34 +222,42 @@ init_lua:
|
|||
set_macos_bundle_resources(L);
|
||||
#endif
|
||||
#endif
|
||||
SDL_EventState(SDL_TEXTINPUT, SDL_ENABLE);
|
||||
SDL_EventState(SDL_TEXTEDITING, SDL_ENABLE);
|
||||
|
||||
const char *init_lite_code = \
|
||||
"local core\n"
|
||||
"local os_exit = os.exit\n"
|
||||
"os.exit = function(code, close)\n"
|
||||
" os_exit(code, close == nil and true or close)\n"
|
||||
"end\n"
|
||||
"xpcall(function()\n"
|
||||
" local match = require('utf8extra').match\n"
|
||||
" HOME = os.getenv('" LITE_OS_HOME "')\n"
|
||||
" local exedir = EXEFILE:match('^(.*)" LITE_PATHSEP_PATTERN LITE_NONPATHSEP_PATTERN "$')\n"
|
||||
" local prefix = exedir:match('^(.*)" LITE_PATHSEP_PATTERN "bin$')\n"
|
||||
" local exedir = match(EXEFILE, '^(.*)" LITE_PATHSEP_PATTERN LITE_NONPATHSEP_PATTERN "$')\n"
|
||||
" local prefix = os.getenv('LITE_PREFIX') or match(exedir, '^(.*)" LITE_PATHSEP_PATTERN "bin$')\n"
|
||||
" dofile((MACOS_RESOURCES or (prefix and prefix .. '/share/lite-xl' or exedir .. '/data')) .. '/core/start.lua')\n"
|
||||
" core = require(os.getenv('LITE_XL_RUNTIME') or 'core')\n"
|
||||
" core.init()\n"
|
||||
" core.run()\n"
|
||||
"end, function(err)\n"
|
||||
" local error_dir\n"
|
||||
" local error_path = 'error.txt'\n"
|
||||
" io.stdout:write('Error: '..tostring(err)..'\\n')\n"
|
||||
" io.stdout:write(debug.traceback(nil, 4)..'\\n')\n"
|
||||
" io.stdout:write(debug.traceback(nil, 2)..'\\n')\n"
|
||||
" if core and core.on_error then\n"
|
||||
" error_dir=USERDIR\n"
|
||||
" error_path = USERDIR .. PATHSEP .. error_path\n"
|
||||
" pcall(core.on_error, err)\n"
|
||||
" else\n"
|
||||
" error_dir=system.absolute_path('.')\n"
|
||||
" local fp = io.open('error.txt', 'wb')\n"
|
||||
" local fp = io.open(error_path, 'wb')\n"
|
||||
" fp:write('Error: ' .. tostring(err) .. '\\n')\n"
|
||||
" fp:write(debug.traceback(nil, 4)..'\\n')\n"
|
||||
" fp:write(debug.traceback(nil, 2)..'\\n')\n"
|
||||
" fp:close()\n"
|
||||
" error_path = system.absolute_path(error_path)\n"
|
||||
" end\n"
|
||||
" system.show_fatal_error('Lite XL internal error',\n"
|
||||
" 'An internal error occurred in a critical part of the application.\\n\\n'..\n"
|
||||
" 'Please verify the file \\\"error.txt\\\" in the directory '..error_dir)\n"
|
||||
" 'Error: '..tostring(err)..'\\n\\n'..\n"
|
||||
" 'Details can be found in \\\"'..error_path..'\\\"')\n"
|
||||
" os.exit(1)\n"
|
||||
"end)\n"
|
||||
"return core and core.restart_request\n";
|
||||
|
@ -259,8 +273,10 @@ init_lua:
|
|||
goto init_lua;
|
||||
}
|
||||
|
||||
// This allows the window to be destroyed before lite-xl is done with
|
||||
// reaping child processes
|
||||
ren_free_window_resources(&window_renderer);
|
||||
lua_close(L);
|
||||
ren_free_window_resources();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -50,6 +50,7 @@ lite_rc = []
|
|||
if host_machine.system() == 'windows'
|
||||
windows = import('windows')
|
||||
lite_rc += windows.compile_resources('../resources/icons/icon.rc')
|
||||
lite_rc += windows.compile_resources('../resources/windows/manifest.rc')
|
||||
elif host_machine.system() == 'darwin'
|
||||
lite_sources += 'bundle_open.m'
|
||||
endif
|
||||
|
|
126
src/rencache.c
126
src/rencache.c
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include <lauxlib.h>
|
||||
#include "rencache.h"
|
||||
#include "renwindow.h"
|
||||
|
||||
/* a cache over the software renderer -- all drawing operations are stored as
|
||||
** commands when issued. At the end of the frame we write the commands to a grid
|
||||
|
@ -28,32 +29,45 @@
|
|||
#define CELL_SIZE 96
|
||||
#define CMD_BUF_RESIZE_RATE 1.2
|
||||
#define CMD_BUF_INIT_SIZE (1024 * 512)
|
||||
#define COMMAND_BARE_SIZE offsetof(Command, text)
|
||||
#define COMMAND_BARE_SIZE offsetof(Command, command)
|
||||
|
||||
enum { SET_CLIP, DRAW_TEXT, DRAW_RECT };
|
||||
enum CommandType { SET_CLIP, DRAW_TEXT, DRAW_RECT };
|
||||
|
||||
typedef struct {
|
||||
enum CommandType type;
|
||||
uint32_t size;
|
||||
/* Commands *must* always begin with a RenRect
|
||||
** This is done to ensure alignment */
|
||||
RenRect command[];
|
||||
} Command;
|
||||
|
||||
typedef struct {
|
||||
RenRect rect;
|
||||
} SetClipCommand;
|
||||
|
||||
typedef struct {
|
||||
int8_t type;
|
||||
int8_t tab_size;
|
||||
int32_t size;
|
||||
RenRect rect;
|
||||
RenColor color;
|
||||
RenFont *fonts[FONT_FALLBACK_MAX];
|
||||
float text_x;
|
||||
size_t len;
|
||||
int8_t tab_size;
|
||||
char text[];
|
||||
} Command;
|
||||
} DrawTextCommand;
|
||||
|
||||
typedef struct {
|
||||
RenRect rect;
|
||||
RenColor color;
|
||||
} DrawRectCommand;
|
||||
|
||||
static unsigned cells_buf1[CELLS_X * CELLS_Y];
|
||||
static unsigned cells_buf2[CELLS_X * CELLS_Y];
|
||||
static unsigned *cells_prev = cells_buf1;
|
||||
static unsigned *cells = cells_buf2;
|
||||
static RenRect rect_buf[CELLS_X * CELLS_Y / 2];
|
||||
size_t command_buf_size = 0;
|
||||
uint8_t *command_buf = NULL;
|
||||
static bool resize_issue;
|
||||
static int command_buf_idx;
|
||||
static RenRect screen_rect;
|
||||
static RenRect last_clip_rect;
|
||||
static bool show_debug;
|
||||
|
||||
static inline int rencache_min(int a, int b) { return a < b ? a : b; }
|
||||
|
@ -99,53 +113,54 @@ static RenRect merge_rects(RenRect a, RenRect b) {
|
|||
return (RenRect) { x1, y1, x2 - x1, y2 - y1 };
|
||||
}
|
||||
|
||||
static bool expand_command_buffer() {
|
||||
size_t new_size = command_buf_size * CMD_BUF_RESIZE_RATE;
|
||||
static bool expand_command_buffer(RenWindow *window_renderer) {
|
||||
size_t new_size = window_renderer->command_buf_size * CMD_BUF_RESIZE_RATE;
|
||||
if (new_size == 0) {
|
||||
new_size = CMD_BUF_INIT_SIZE;
|
||||
}
|
||||
uint8_t *new_command_buf = realloc(command_buf, new_size);
|
||||
uint8_t *new_command_buf = realloc(window_renderer->command_buf, new_size);
|
||||
if (!new_command_buf) {
|
||||
return false;
|
||||
}
|
||||
command_buf_size = new_size;
|
||||
command_buf = new_command_buf;
|
||||
window_renderer->command_buf_size = new_size;
|
||||
window_renderer->command_buf = new_command_buf;
|
||||
return true;
|
||||
}
|
||||
|
||||
static Command* push_command(int type, int size) {
|
||||
static void* push_command(RenWindow *window_renderer, enum CommandType type, int size) {
|
||||
if (resize_issue) {
|
||||
// Don't push new commands as we had problems resizing the command buffer.
|
||||
// Let's wait for the next frame.
|
||||
return NULL;
|
||||
}
|
||||
size_t alignment = alignof(max_align_t) - 1;
|
||||
size += COMMAND_BARE_SIZE;
|
||||
size = (size + alignment) & ~alignment;
|
||||
int n = command_buf_idx + size;
|
||||
while (n > command_buf_size) {
|
||||
if (!expand_command_buffer()) {
|
||||
fprintf(stderr, "Warning: (" __FILE__ "): unable to resize command buffer (%ld)\n",
|
||||
(size_t)(command_buf_size * CMD_BUF_RESIZE_RATE));
|
||||
int n = window_renderer->command_buf_idx + size;
|
||||
while (n > window_renderer->command_buf_size) {
|
||||
if (!expand_command_buffer(window_renderer)) {
|
||||
fprintf(stderr, "Warning: (" __FILE__ "): unable to resize command buffer (%zu)\n",
|
||||
(size_t)(window_renderer->command_buf_size * CMD_BUF_RESIZE_RATE));
|
||||
resize_issue = true;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
Command *cmd = (Command*) (command_buf + command_buf_idx);
|
||||
command_buf_idx = n;
|
||||
Command *cmd = (Command*) (window_renderer->command_buf + window_renderer->command_buf_idx);
|
||||
window_renderer->command_buf_idx = n;
|
||||
memset(cmd, 0, size);
|
||||
cmd->type = type;
|
||||
cmd->size = size;
|
||||
return cmd;
|
||||
return cmd->command;
|
||||
}
|
||||
|
||||
|
||||
static bool next_command(Command **prev) {
|
||||
static bool next_command(RenWindow *window_renderer, Command **prev) {
|
||||
if (*prev == NULL) {
|
||||
*prev = (Command*) command_buf;
|
||||
*prev = (Command*) window_renderer->command_buf;
|
||||
} else {
|
||||
*prev = (Command*) (((char*) *prev) + (*prev)->size);
|
||||
}
|
||||
return *prev != ((Command*) (command_buf + command_buf_idx));
|
||||
return *prev != ((Command*) (window_renderer->command_buf + window_renderer->command_buf_idx));
|
||||
}
|
||||
|
||||
|
||||
|
@ -154,30 +169,33 @@ void rencache_show_debug(bool enable) {
|
|||
}
|
||||
|
||||
|
||||
void rencache_set_clip_rect(RenRect rect) {
|
||||
Command *cmd = push_command(SET_CLIP, COMMAND_BARE_SIZE);
|
||||
if (cmd) { cmd->rect = intersect_rects(rect, screen_rect); }
|
||||
void rencache_set_clip_rect(RenWindow *window_renderer, RenRect rect) {
|
||||
SetClipCommand *cmd = push_command(window_renderer, SET_CLIP, sizeof(SetClipCommand));
|
||||
if (cmd) {
|
||||
cmd->rect = intersect_rects(rect, screen_rect);
|
||||
last_clip_rect = cmd->rect;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void rencache_draw_rect(RenRect rect, RenColor color) {
|
||||
if (!rects_overlap(screen_rect, rect) || rect.width == 0 || rect.height == 0) {
|
||||
void rencache_draw_rect(RenWindow *window_renderer, RenRect rect, RenColor color) {
|
||||
if (rect.width == 0 || rect.height == 0 || !rects_overlap(last_clip_rect, rect)) {
|
||||
return;
|
||||
}
|
||||
Command *cmd = push_command(DRAW_RECT, COMMAND_BARE_SIZE);
|
||||
DrawRectCommand *cmd = push_command(window_renderer, DRAW_RECT, sizeof(DrawRectCommand));
|
||||
if (cmd) {
|
||||
cmd->rect = rect;
|
||||
cmd->color = color;
|
||||
}
|
||||
}
|
||||
|
||||
float rencache_draw_text(RenFont **fonts, const char *text, size_t len, float x, int y, RenColor color)
|
||||
double rencache_draw_text(RenWindow *window_renderer, RenFont **fonts, const char *text, size_t len, double x, int y, RenColor color)
|
||||
{
|
||||
float width = ren_font_group_get_width(fonts, text, len);
|
||||
double width = ren_font_group_get_width(window_renderer, fonts, text, len);
|
||||
RenRect rect = { x, y, (int)width, ren_font_group_get_height(fonts) };
|
||||
if (rects_overlap(screen_rect, rect)) {
|
||||
if (rects_overlap(last_clip_rect, rect)) {
|
||||
int sz = len + 1;
|
||||
Command *cmd = push_command(DRAW_TEXT, COMMAND_BARE_SIZE + sz);
|
||||
DrawTextCommand *cmd = push_command(window_renderer, DRAW_TEXT, sizeof(DrawTextCommand) + sz);
|
||||
if (cmd) {
|
||||
memcpy(cmd->text, text, sz);
|
||||
cmd->color = color;
|
||||
|
@ -197,16 +215,17 @@ void rencache_invalidate(void) {
|
|||
}
|
||||
|
||||
|
||||
void rencache_begin_frame() {
|
||||
void rencache_begin_frame(RenWindow *window_renderer) {
|
||||
/* reset all cells if the screen width/height has changed */
|
||||
int w, h;
|
||||
resize_issue = false;
|
||||
ren_get_size(&w, &h);
|
||||
ren_get_size(window_renderer, &w, &h);
|
||||
if (screen_rect.width != w || h != screen_rect.height) {
|
||||
screen_rect.width = w;
|
||||
screen_rect.height = h;
|
||||
rencache_invalidate();
|
||||
}
|
||||
last_clip_rect = screen_rect;
|
||||
}
|
||||
|
||||
|
||||
|
@ -239,13 +258,14 @@ static void push_rect(RenRect r, int *count) {
|
|||
}
|
||||
|
||||
|
||||
void rencache_end_frame() {
|
||||
void rencache_end_frame(RenWindow *window_renderer) {
|
||||
/* update cells from commands */
|
||||
Command *cmd = NULL;
|
||||
RenRect cr = screen_rect;
|
||||
while (next_command(&cmd)) {
|
||||
if (cmd->type == SET_CLIP) { cr = cmd->rect; }
|
||||
RenRect r = intersect_rects(cmd->rect, cr);
|
||||
while (next_command(window_renderer, &cmd)) {
|
||||
/* cmd->command[0] should always be the Command rect */
|
||||
if (cmd->type == SET_CLIP) { cr = cmd->command[0]; }
|
||||
RenRect r = intersect_rects(cmd->command[0], cr);
|
||||
if (r.width == 0 || r.height == 0) { continue; }
|
||||
unsigned h = HASH_INITIAL;
|
||||
hash(&h, cmd, cmd->size);
|
||||
|
@ -277,43 +297,47 @@ void rencache_end_frame() {
|
|||
*r = intersect_rects(*r, screen_rect);
|
||||
}
|
||||
|
||||
RenSurface rs = renwin_get_surface(window_renderer);
|
||||
/* redraw updated regions */
|
||||
for (int i = 0; i < rect_count; i++) {
|
||||
/* draw */
|
||||
RenRect r = rect_buf[i];
|
||||
ren_set_clip_rect(r);
|
||||
ren_set_clip_rect(window_renderer, r);
|
||||
|
||||
cmd = NULL;
|
||||
while (next_command(&cmd)) {
|
||||
while (next_command(window_renderer, &cmd)) {
|
||||
SetClipCommand *ccmd = (SetClipCommand*)&cmd->command;
|
||||
DrawRectCommand *rcmd = (DrawRectCommand*)&cmd->command;
|
||||
DrawTextCommand *tcmd = (DrawTextCommand*)&cmd->command;
|
||||
switch (cmd->type) {
|
||||
case SET_CLIP:
|
||||
ren_set_clip_rect(intersect_rects(cmd->rect, r));
|
||||
ren_set_clip_rect(window_renderer, intersect_rects(ccmd->rect, r));
|
||||
break;
|
||||
case DRAW_RECT:
|
||||
ren_draw_rect(cmd->rect, cmd->color);
|
||||
ren_draw_rect(&rs, rcmd->rect, rcmd->color);
|
||||
break;
|
||||
case DRAW_TEXT:
|
||||
ren_font_group_set_tab_size(cmd->fonts, cmd->tab_size);
|
||||
ren_draw_text(cmd->fonts, cmd->text, cmd->len, cmd->text_x, cmd->rect.y, cmd->color);
|
||||
ren_font_group_set_tab_size(tcmd->fonts, tcmd->tab_size);
|
||||
ren_draw_text(&rs, tcmd->fonts, tcmd->text, tcmd->len, tcmd->text_x, tcmd->rect.y, tcmd->color);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (show_debug) {
|
||||
RenColor color = { rand(), rand(), rand(), 50 };
|
||||
ren_draw_rect(r, color);
|
||||
ren_draw_rect(&rs, r, color);
|
||||
}
|
||||
}
|
||||
|
||||
/* update dirty rects */
|
||||
if (rect_count > 0) {
|
||||
ren_update_rects(rect_buf, rect_count);
|
||||
ren_update_rects(window_renderer, rect_buf, rect_count);
|
||||
}
|
||||
|
||||
/* swap cell buffer and reset */
|
||||
unsigned *tmp = cells;
|
||||
cells = cells_prev;
|
||||
cells_prev = tmp;
|
||||
command_buf_idx = 0;
|
||||
window_renderer->command_buf_idx = 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,11 +6,11 @@
|
|||
#include "renderer.h"
|
||||
|
||||
void rencache_show_debug(bool enable);
|
||||
void rencache_set_clip_rect(RenRect rect);
|
||||
void rencache_draw_rect(RenRect rect, RenColor color);
|
||||
float rencache_draw_text(RenFont **font, const char *text, size_t len, float x, int y, RenColor color);
|
||||
void rencache_set_clip_rect(RenWindow *window_renderer, RenRect rect);
|
||||
void rencache_draw_rect(RenWindow *window_renderer, RenRect rect, RenColor color);
|
||||
double rencache_draw_text(RenWindow *window_renderer, RenFont **font, const char *text, size_t len, double x, int y, RenColor color);
|
||||
void rencache_invalidate(void);
|
||||
void rencache_begin_frame();
|
||||
void rencache_end_frame();
|
||||
void rencache_begin_frame(RenWindow *window_renderer);
|
||||
void rencache_end_frame(RenWindow *window_renderer);
|
||||
|
||||
#endif
|
||||
|
|
259
src/renderer.c
259
src/renderer.c
|
@ -4,9 +4,10 @@
|
|||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <ft2build.h>
|
||||
#include <freetype/ftlcdfil.h>
|
||||
#include <freetype/ftoutln.h>
|
||||
#include FT_FREETYPE_H
|
||||
#include FT_LCD_FILTER_H
|
||||
#include FT_OUTLINE_H
|
||||
#include FT_SYSTEM_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
|
@ -16,11 +17,12 @@
|
|||
#include "renderer.h"
|
||||
#include "renwindow.h"
|
||||
|
||||
#define MAX_GLYPHSET 256
|
||||
#define MAX_LOADABLE_GLYPHSETS 1024
|
||||
#define MAX_UNICODE 0x100000
|
||||
#define GLYPHSET_SIZE 256
|
||||
#define MAX_LOADABLE_GLYPHSETS (MAX_UNICODE / GLYPHSET_SIZE)
|
||||
#define SUBPIXEL_BITMAPS_CACHED 3
|
||||
|
||||
static RenWindow window_renderer = {0};
|
||||
RenWindow window_renderer = {0};
|
||||
static FT_Library library;
|
||||
|
||||
// draw_rect_surface is used as a 1x1 surface to simplify ren_draw_rect with blending
|
||||
|
@ -37,18 +39,19 @@ static void* check_alloc(void *ptr) {
|
|||
/************************* Fonts *************************/
|
||||
|
||||
typedef struct {
|
||||
unsigned short x0, x1, y0, y1, loaded;
|
||||
short bitmap_left, bitmap_top;
|
||||
unsigned int x0, x1, y0, y1, loaded;
|
||||
int bitmap_left, bitmap_top;
|
||||
float xadvance;
|
||||
} GlyphMetric;
|
||||
|
||||
typedef struct {
|
||||
SDL_Surface* surface;
|
||||
GlyphMetric metrics[MAX_GLYPHSET];
|
||||
GlyphMetric metrics[GLYPHSET_SIZE];
|
||||
} GlyphSet;
|
||||
|
||||
typedef struct RenFont {
|
||||
FT_Face face;
|
||||
FT_StreamRec stream;
|
||||
GlyphSet* sets[SUBPIXEL_BITMAPS_CACHED][MAX_LOADABLE_GLYPHSETS];
|
||||
float size, space_advance, tab_advance;
|
||||
unsigned short max_height, baseline, height;
|
||||
|
@ -56,10 +59,6 @@ typedef struct RenFont {
|
|||
ERenFontHinting hinting;
|
||||
unsigned char style;
|
||||
unsigned short underline_thickness;
|
||||
#ifdef _WIN32
|
||||
unsigned char *file;
|
||||
HANDLE file_handle;
|
||||
#endif
|
||||
char path[];
|
||||
} RenFont;
|
||||
|
||||
|
@ -128,14 +127,14 @@ static void font_load_glyphset(RenFont* font, int idx) {
|
|||
for (int j = 0, pen_x = 0; j < bitmaps_cached; ++j) {
|
||||
GlyphSet* set = check_alloc(calloc(1, sizeof(GlyphSet)));
|
||||
font->sets[j][idx] = set;
|
||||
for (int i = 0; i < MAX_GLYPHSET; ++i) {
|
||||
int glyph_index = FT_Get_Char_Index(font->face, i + idx * MAX_GLYPHSET);
|
||||
for (int i = 0; i < GLYPHSET_SIZE; ++i) {
|
||||
int glyph_index = FT_Get_Char_Index(font->face, i + idx * GLYPHSET_SIZE);
|
||||
if (!glyph_index || FT_Load_Glyph(font->face, glyph_index, load_option | FT_LOAD_BITMAP_METRICS_ONLY)
|
||||
|| font_set_style(&font->face->glyph->outline, j * (64 / SUBPIXEL_BITMAPS_CACHED), font->style) || FT_Render_Glyph(font->face->glyph, render_option)) {
|
||||
continue;
|
||||
}
|
||||
FT_GlyphSlot slot = font->face->glyph;
|
||||
int glyph_width = slot->bitmap.width / byte_width;
|
||||
unsigned int glyph_width = slot->bitmap.width / byte_width;
|
||||
if (font->antialiasing == FONT_ANTIALIASING_NONE)
|
||||
glyph_width *= 8;
|
||||
set->metrics[i] = (GlyphMetric){ pen_x, pen_x + glyph_width, 0, slot->bitmap.rows, true, slot->bitmap_left, slot->bitmap_top, (slot->advance.x + slot->lsb_delta - slot->rsb_delta) / 64.0f};
|
||||
|
@ -153,8 +152,8 @@ static void font_load_glyphset(RenFont* font, int idx) {
|
|||
continue;
|
||||
set->surface = check_alloc(SDL_CreateRGBSurface(0, pen_x, font->max_height, font->antialiasing == FONT_ANTIALIASING_SUBPIXEL ? 24 : 8, 0, 0, 0, 0));
|
||||
uint8_t* pixels = set->surface->pixels;
|
||||
for (int i = 0; i < MAX_GLYPHSET; ++i) {
|
||||
int glyph_index = FT_Get_Char_Index(font->face, i + idx * MAX_GLYPHSET);
|
||||
for (int i = 0; i < GLYPHSET_SIZE; ++i) {
|
||||
int glyph_index = FT_Get_Char_Index(font->face, i + idx * GLYPHSET_SIZE);
|
||||
if (!glyph_index || FT_Load_Glyph(font->face, glyph_index, load_option))
|
||||
continue;
|
||||
FT_GlyphSlot slot = font->face->glyph;
|
||||
|
@ -178,7 +177,7 @@ static void font_load_glyphset(RenFont* font, int idx) {
|
|||
}
|
||||
|
||||
static GlyphSet* font_get_glyphset(RenFont* font, unsigned int codepoint, int subpixel_idx) {
|
||||
int idx = (codepoint >> 8) % MAX_LOADABLE_GLYPHSETS;
|
||||
int idx = (codepoint / GLYPHSET_SIZE) % MAX_LOADABLE_GLYPHSETS;
|
||||
if (!font->sets[font->antialiasing == FONT_ANTIALIASING_SUBPIXEL ? subpixel_idx : 0][idx])
|
||||
font_load_glyphset(font, idx);
|
||||
return font->sets[font->antialiasing == FONT_ANTIALIASING_SUBPIXEL ? subpixel_idx : 0][idx];
|
||||
|
@ -192,7 +191,7 @@ static RenFont* font_group_get_glyph(GlyphSet** set, GlyphMetric** metric, RenFo
|
|||
bitmap_index += SUBPIXEL_BITMAPS_CACHED;
|
||||
for (int i = 0; i < FONT_FALLBACK_MAX && fonts[i]; ++i) {
|
||||
*set = font_get_glyphset(fonts[i], codepoint, bitmap_index);
|
||||
*metric = &(*set)->metrics[codepoint % 256];
|
||||
*metric = &(*set)->metrics[codepoint % GLYPHSET_SIZE];
|
||||
if ((*metric)->loaded || codepoint < 0xFF)
|
||||
return fonts[i];
|
||||
}
|
||||
|
@ -203,7 +202,7 @@ static RenFont* font_group_get_glyph(GlyphSet** set, GlyphMetric** metric, RenFo
|
|||
|
||||
static void font_clear_glyph_cache(RenFont* font) {
|
||||
for (int i = 0; i < SUBPIXEL_BITMAPS_CACHED; ++i) {
|
||||
for (int j = 0; j < MAX_GLYPHSET; ++j) {
|
||||
for (int j = 0; j < MAX_LOADABLE_GLYPHSETS; ++j) {
|
||||
if (font->sets[i][j]) {
|
||||
if (font->sets[i][j]->surface)
|
||||
SDL_FreeSurface(font->sets[i][j]->surface);
|
||||
|
@ -214,54 +213,49 @@ static void font_clear_glyph_cache(RenFont* font) {
|
|||
}
|
||||
}
|
||||
|
||||
RenFont* ren_font_load(const char* path, float size, ERenFontAntialiasing antialiasing, ERenFontHinting hinting, unsigned char style) {
|
||||
// based on https://github.com/libsdl-org/SDL_ttf/blob/2a094959055fba09f7deed6e1ffeb986188982ae/SDL_ttf.c#L1735
|
||||
static unsigned long font_file_read(FT_Stream stream, unsigned long offset, unsigned char *buffer, unsigned long count) {
|
||||
uint64_t amount;
|
||||
SDL_RWops *file = (SDL_RWops *) stream->descriptor.pointer;
|
||||
SDL_RWseek(file, (int) offset, RW_SEEK_SET);
|
||||
if (count == 0)
|
||||
return 0;
|
||||
amount = SDL_RWread(file, buffer, sizeof(char), count);
|
||||
if (amount <= 0)
|
||||
return 0;
|
||||
return (unsigned long) amount;
|
||||
}
|
||||
|
||||
static void font_file_close(FT_Stream stream) {
|
||||
if (stream && stream->descriptor.pointer) {
|
||||
SDL_RWclose((SDL_RWops *) stream->descriptor.pointer);
|
||||
stream->descriptor.pointer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
RenFont* ren_font_load(RenWindow *window_renderer, const char* path, float size, ERenFontAntialiasing antialiasing, ERenFontHinting hinting, unsigned char style) {
|
||||
RenFont *font = NULL;
|
||||
FT_Face face = NULL;
|
||||
|
||||
#ifdef _WIN32
|
||||
SDL_RWops *file = SDL_RWFromFile(path, "rb");
|
||||
if (!file)
|
||||
goto rwops_failure;
|
||||
|
||||
HANDLE file = INVALID_HANDLE_VALUE;
|
||||
DWORD read;
|
||||
int font_file_len = 0;
|
||||
unsigned char *font_file = NULL;
|
||||
wchar_t *wpath = NULL;
|
||||
int len = strlen(path);
|
||||
font = check_alloc(calloc(1, sizeof(RenFont) + len + 1));
|
||||
font->stream.read = font_file_read;
|
||||
font->stream.close = font_file_close;
|
||||
font->stream.descriptor.pointer = file;
|
||||
font->stream.pos = 0;
|
||||
font->stream.size = (unsigned long) SDL_RWsize(file);
|
||||
|
||||
if ((wpath = utfconv_utf8towc(path)) == NULL)
|
||||
return NULL;
|
||||
|
||||
if ((file = CreateFileW(wpath,
|
||||
GENERIC_READ,
|
||||
FILE_SHARE_READ, // or else we can't copy fonts
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NULL)) == INVALID_HANDLE_VALUE)
|
||||
if (FT_Open_Face(library, &(FT_Open_Args){ .flags = FT_OPEN_STREAM, .stream = &font->stream }, 0, &face))
|
||||
goto failure;
|
||||
|
||||
if ((font_file_len = GetFileSize(file, NULL)) == INVALID_FILE_SIZE)
|
||||
goto failure;
|
||||
|
||||
font_file = check_alloc(malloc(font_file_len * sizeof(unsigned char)));
|
||||
if (!ReadFile(file, font_file, font_file_len, &read, NULL) || read != font_file_len)
|
||||
goto failure;
|
||||
|
||||
free(wpath);
|
||||
wpath = NULL;
|
||||
|
||||
if (FT_New_Memory_Face(library, font_file, read, 0, &face))
|
||||
goto failure;
|
||||
|
||||
#else
|
||||
|
||||
if (FT_New_Face(library, path, 0, &face))
|
||||
return NULL;
|
||||
|
||||
#endif
|
||||
|
||||
const int surface_scale = renwin_surface_scale(&window_renderer);
|
||||
const int surface_scale = renwin_get_surface(window_renderer).scale;
|
||||
if (FT_Set_Pixel_Sizes(face, 0, (int)(size*surface_scale)))
|
||||
goto failure;
|
||||
int len = strlen(path);
|
||||
RenFont* font = check_alloc(calloc(1, sizeof(RenFont) + len + 1));
|
||||
|
||||
strcpy(font->path, path);
|
||||
font->face = face;
|
||||
font->size = size;
|
||||
|
@ -271,41 +265,37 @@ RenFont* ren_font_load(const char* path, float size, ERenFontAntialiasing antial
|
|||
font->hinting = hinting;
|
||||
font->style = style;
|
||||
|
||||
#ifdef _WIN32
|
||||
// we need to keep this for freetype
|
||||
font->file = font_file;
|
||||
font->file_handle = file;
|
||||
#endif
|
||||
|
||||
if(FT_IS_SCALABLE(face))
|
||||
font->underline_thickness = (unsigned short)((face->underline_thickness / (float)face->units_per_EM) * font->size);
|
||||
if(!font->underline_thickness) font->underline_thickness = ceil((double) font->height / 14.0);
|
||||
if(!font->underline_thickness)
|
||||
font->underline_thickness = ceil((double) font->height / 14.0);
|
||||
|
||||
if (FT_Load_Char(face, ' ', font_set_load_options(font))) {
|
||||
free(font);
|
||||
if (FT_Load_Char(face, ' ', font_set_load_options(font)))
|
||||
goto failure;
|
||||
}
|
||||
|
||||
font->space_advance = face->glyph->advance.x / 64.0f;
|
||||
font->tab_advance = font->space_advance * 2;
|
||||
return font;
|
||||
|
||||
failure:
|
||||
#ifdef _WIN32
|
||||
free(wpath);
|
||||
free(font_file);
|
||||
if (file != INVALID_HANDLE_VALUE) CloseHandle(file);
|
||||
#endif
|
||||
if (face != NULL)
|
||||
if (face)
|
||||
FT_Done_Face(face);
|
||||
if (font)
|
||||
free(font);
|
||||
return NULL;
|
||||
|
||||
rwops_failure:
|
||||
if (file)
|
||||
SDL_RWclose(file);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
RenFont* ren_font_copy(RenFont* font, float size, ERenFontAntialiasing antialiasing, ERenFontHinting hinting, int style) {
|
||||
RenFont* ren_font_copy(RenWindow *window_renderer, RenFont* font, float size, ERenFontAntialiasing antialiasing, ERenFontHinting hinting, int style) {
|
||||
antialiasing = antialiasing == -1 ? font->antialiasing : antialiasing;
|
||||
hinting = hinting == -1 ? font->hinting : hinting;
|
||||
style = style == -1 ? font->style : style;
|
||||
|
||||
return ren_font_load(font->path, size, antialiasing, hinting, style);
|
||||
return ren_font_load(window_renderer, font->path, size, antialiasing, hinting, style);
|
||||
}
|
||||
|
||||
const char* ren_font_get_path(RenFont *font) {
|
||||
|
@ -315,22 +305,20 @@ const char* ren_font_get_path(RenFont *font) {
|
|||
void ren_font_free(RenFont* font) {
|
||||
font_clear_glyph_cache(font);
|
||||
FT_Done_Face(font->face);
|
||||
#ifdef _WIN32
|
||||
free(font->file);
|
||||
CloseHandle(font->file_handle);
|
||||
#endif
|
||||
free(font);
|
||||
}
|
||||
|
||||
void ren_font_group_set_tab_size(RenFont **fonts, int n) {
|
||||
unsigned int tab_index = '\t' % GLYPHSET_SIZE;
|
||||
for (int j = 0; j < FONT_FALLBACK_MAX && fonts[j]; ++j) {
|
||||
for (int i = 0; i < (fonts[j]->antialiasing == FONT_ANTIALIASING_SUBPIXEL ? SUBPIXEL_BITMAPS_CACHED : 1); ++i)
|
||||
font_get_glyphset(fonts[j], '\t', i)->metrics['\t'].xadvance = fonts[j]->space_advance * n;
|
||||
font_get_glyphset(fonts[j], '\t', i)->metrics[tab_index].xadvance = fonts[j]->space_advance * n;
|
||||
}
|
||||
}
|
||||
|
||||
int ren_font_group_get_tab_size(RenFont **fonts) {
|
||||
float advance = font_get_glyphset(fonts[0], '\t', 0)->metrics['\t'].xadvance;
|
||||
unsigned int tab_index = '\t' % GLYPHSET_SIZE;
|
||||
float advance = font_get_glyphset(fonts[0], '\t', 0)->metrics[tab_index].xadvance;
|
||||
if (fonts[0]->space_advance) {
|
||||
advance /= fonts[0]->space_advance;
|
||||
}
|
||||
|
@ -341,8 +329,8 @@ float ren_font_group_get_size(RenFont **fonts) {
|
|||
return fonts[0]->size;
|
||||
}
|
||||
|
||||
void ren_font_group_set_size(RenFont **fonts, float size) {
|
||||
const int surface_scale = renwin_surface_scale(&window_renderer);
|
||||
void ren_font_group_set_size(RenWindow *window_renderer, RenFont **fonts, float size) {
|
||||
const int surface_scale = renwin_get_surface(window_renderer).scale;
|
||||
for (int i = 0; i < FONT_FALLBACK_MAX && fonts[i]; ++i) {
|
||||
font_clear_glyph_cache(fonts[i]);
|
||||
FT_Face face = fonts[i]->face;
|
||||
|
@ -360,8 +348,8 @@ int ren_font_group_get_height(RenFont **fonts) {
|
|||
return fonts[0]->height;
|
||||
}
|
||||
|
||||
float ren_font_group_get_width(RenFont **fonts, const char *text, size_t len) {
|
||||
float width = 0;
|
||||
double ren_font_group_get_width(RenWindow *window_renderer, RenFont **fonts, const char *text, size_t len) {
|
||||
double width = 0;
|
||||
const char* end = text + len;
|
||||
GlyphMetric* metric = NULL; GlyphSet* set = NULL;
|
||||
while (text < end) {
|
||||
|
@ -372,24 +360,25 @@ float ren_font_group_get_width(RenFont **fonts, const char *text, size_t len) {
|
|||
break;
|
||||
width += (!font || metric->xadvance) ? metric->xadvance : fonts[0]->space_advance;
|
||||
}
|
||||
const int surface_scale = renwin_surface_scale(&window_renderer);
|
||||
const int surface_scale = renwin_get_surface(window_renderer).scale;
|
||||
return width / surface_scale;
|
||||
}
|
||||
|
||||
float ren_draw_text(RenFont **fonts, const char *text, size_t len, float x, int y, RenColor color) {
|
||||
SDL_Surface *surface = renwin_get_surface(&window_renderer);
|
||||
const RenRect clip = window_renderer.clip;
|
||||
double ren_draw_text(RenSurface *rs, RenFont **fonts, const char *text, size_t len, float x, int y, RenColor color) {
|
||||
SDL_Surface *surface = rs->surface;
|
||||
SDL_Rect clip;
|
||||
SDL_GetClipRect(surface, &clip);
|
||||
|
||||
const int surface_scale = renwin_surface_scale(&window_renderer);
|
||||
float pen_x = x * surface_scale;
|
||||
const int surface_scale = rs->scale;
|
||||
double pen_x = x * surface_scale;
|
||||
y *= surface_scale;
|
||||
int bytes_per_pixel = surface->format->BytesPerPixel;
|
||||
const char* end = text + len;
|
||||
uint8_t* destination_pixels = surface->pixels;
|
||||
int clip_end_x = clip.x + clip.width, clip_end_y = clip.y + clip.height;
|
||||
int clip_end_x = clip.x + clip.w, clip_end_y = clip.y + clip.h;
|
||||
|
||||
RenFont* last = NULL;
|
||||
float last_pen_x = x;
|
||||
double last_pen_x = x;
|
||||
bool underline = fonts[0]->style & FONT_STYLE_UNDERLINE;
|
||||
bool strikethrough = fonts[0]->style & FONT_STYLE_STRIKETHROUGH;
|
||||
|
||||
|
@ -404,11 +393,11 @@ float ren_draw_text(RenFont **fonts, const char *text, size_t len, float x, int
|
|||
int end_x = (metric->x1 - metric->x0) + start_x;
|
||||
int glyph_end = metric->x1, glyph_start = metric->x0;
|
||||
if (!metric->loaded && codepoint > 0xFF)
|
||||
ren_draw_rect((RenRect){ start_x + 1, y, font->space_advance - 1, ren_font_group_get_height(fonts) }, color);
|
||||
ren_draw_rect(rs, (RenRect){ start_x + 1, y, font->space_advance - 1, ren_font_group_get_height(fonts) }, color);
|
||||
if (set->surface && color.a > 0 && end_x >= clip.x && start_x < clip_end_x) {
|
||||
uint8_t* source_pixels = set->surface->pixels;
|
||||
for (int line = metric->y0; line < metric->y1; ++line) {
|
||||
int target_y = line + y - metric->bitmap_top + font->baseline * surface_scale;
|
||||
int target_y = line + y - metric->bitmap_top + fonts[0]->baseline * surface_scale;
|
||||
if (target_y < clip.y)
|
||||
continue;
|
||||
if (target_y >= clip_end_y)
|
||||
|
@ -453,11 +442,11 @@ float ren_draw_text(RenFont **fonts, const char *text, size_t len, float x, int
|
|||
|
||||
if(!last) last = font;
|
||||
else if(font != last || text == end) {
|
||||
float local_pen_x = text == end ? pen_x + adv : pen_x;
|
||||
double local_pen_x = text == end ? pen_x + adv : pen_x;
|
||||
if (underline)
|
||||
ren_draw_rect((RenRect){last_pen_x, y / surface_scale + last->height - 1, (local_pen_x - last_pen_x) / surface_scale, last->underline_thickness * surface_scale}, color);
|
||||
ren_draw_rect(rs, (RenRect){last_pen_x, y / surface_scale + last->height - 1, (local_pen_x - last_pen_x) / surface_scale, last->underline_thickness * surface_scale}, color);
|
||||
if (strikethrough)
|
||||
ren_draw_rect((RenRect){last_pen_x, y / surface_scale + last->height / 2, (local_pen_x - last_pen_x) / surface_scale, last->underline_thickness * surface_scale}, color);
|
||||
ren_draw_rect(rs, (RenRect){last_pen_x, y / surface_scale + last->height / 2, (local_pen_x - last_pen_x) / surface_scale, last->underline_thickness * surface_scale}, color);
|
||||
last = font;
|
||||
last_pen_x = pen_x;
|
||||
}
|
||||
|
@ -476,31 +465,27 @@ static inline RenColor blend_pixel(RenColor dst, RenColor src) {
|
|||
return dst;
|
||||
}
|
||||
|
||||
void ren_draw_rect(RenRect rect, RenColor color) {
|
||||
void ren_draw_rect(RenSurface *rs, RenRect rect, RenColor color) {
|
||||
if (color.a == 0) { return; }
|
||||
|
||||
const int surface_scale = renwin_surface_scale(&window_renderer);
|
||||
SDL_Surface *surface = rs->surface;
|
||||
const int surface_scale = rs->scale;
|
||||
|
||||
/* transforms coordinates in pixels. */
|
||||
rect.x *= surface_scale;
|
||||
rect.y *= surface_scale;
|
||||
rect.width *= surface_scale;
|
||||
rect.height *= surface_scale;
|
||||
SDL_Rect dest_rect = { rect.x * surface_scale,
|
||||
rect.y * surface_scale,
|
||||
rect.width * surface_scale,
|
||||
rect.height * surface_scale };
|
||||
|
||||
const RenRect clip = window_renderer.clip;
|
||||
int x1 = rect.x < clip.x ? clip.x : rect.x;
|
||||
int y1 = rect.y < clip.y ? clip.y : rect.y;
|
||||
int x2 = rect.x + rect.width;
|
||||
int y2 = rect.y + rect.height;
|
||||
x2 = x2 > clip.x + clip.width ? clip.x + clip.width : x2;
|
||||
y2 = y2 > clip.y + clip.height ? clip.y + clip.height : y2;
|
||||
|
||||
SDL_Surface *surface = renwin_get_surface(&window_renderer);
|
||||
SDL_Rect dest_rect = { x1, y1, x2 - x1, y2 - y1 };
|
||||
if (color.a == 0xff) {
|
||||
uint32_t translated = SDL_MapRGB(surface->format, color.r, color.g, color.b);
|
||||
SDL_FillRect(surface, &dest_rect, translated);
|
||||
} else {
|
||||
// Seems like SDL doesn't handle clipping as we expect when using
|
||||
// scaled blitting, so we "clip" manually.
|
||||
SDL_Rect clip;
|
||||
SDL_GetClipRect(surface, &clip);
|
||||
if (!SDL_IntersectRect(&clip, &dest_rect, &dest_rect)) return;
|
||||
|
||||
uint32_t *pixel = (uint32_t *)draw_rect_surface->pixels;
|
||||
*pixel = SDL_MapRGBA(draw_rect_surface->format, color.r, color.g, color.b, color.a);
|
||||
SDL_BlitScaled(draw_rect_surface, NULL, surface, &dest_rect);
|
||||
|
@ -508,16 +493,15 @@ void ren_draw_rect(RenRect rect, RenColor color) {
|
|||
}
|
||||
|
||||
/*************** Window Management ****************/
|
||||
void ren_free_window_resources() {
|
||||
extern uint8_t *command_buf;
|
||||
extern size_t command_buf_size;
|
||||
renwin_free(&window_renderer);
|
||||
void ren_free_window_resources(RenWindow *window_renderer) {
|
||||
renwin_free(window_renderer);
|
||||
SDL_FreeSurface(draw_rect_surface);
|
||||
free(command_buf);
|
||||
command_buf = NULL;
|
||||
command_buf_size = 0;
|
||||
free(window_renderer->command_buf);
|
||||
window_renderer->command_buf = NULL;
|
||||
window_renderer->command_buf_size = 0;
|
||||
}
|
||||
|
||||
// TODO remove global and return RenWindow*
|
||||
void ren_init(SDL_Window *win) {
|
||||
assert(win);
|
||||
int error = FT_Init_FreeType( &library );
|
||||
|
@ -527,36 +511,35 @@ void ren_init(SDL_Window *win) {
|
|||
}
|
||||
window_renderer.window = win;
|
||||
renwin_init_surface(&window_renderer);
|
||||
renwin_init_command_buf(&window_renderer);
|
||||
renwin_clip_to_surface(&window_renderer);
|
||||
draw_rect_surface = SDL_CreateRGBSurface(0, 1, 1, 32,
|
||||
0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF);
|
||||
}
|
||||
|
||||
|
||||
void ren_resize_window() {
|
||||
renwin_resize_surface(&window_renderer);
|
||||
void ren_resize_window(RenWindow *window_renderer) {
|
||||
renwin_resize_surface(window_renderer);
|
||||
}
|
||||
|
||||
|
||||
void ren_update_rects(RenRect *rects, int count) {
|
||||
void ren_update_rects(RenWindow *window_renderer, RenRect *rects, int count) {
|
||||
static bool initial_frame = true;
|
||||
if (initial_frame) {
|
||||
renwin_show_window(&window_renderer);
|
||||
renwin_show_window(window_renderer);
|
||||
initial_frame = false;
|
||||
}
|
||||
renwin_update_rects(&window_renderer, rects, count);
|
||||
renwin_update_rects(window_renderer, rects, count);
|
||||
}
|
||||
|
||||
|
||||
void ren_set_clip_rect(RenRect rect) {
|
||||
renwin_set_clip_rect(&window_renderer, rect);
|
||||
void ren_set_clip_rect(RenWindow *window_renderer, RenRect rect) {
|
||||
renwin_set_clip_rect(window_renderer, rect);
|
||||
}
|
||||
|
||||
|
||||
void ren_get_size(int *x, int *y) {
|
||||
RenWindow *ren = &window_renderer;
|
||||
const int scale = renwin_surface_scale(ren);
|
||||
SDL_Surface *surface = renwin_get_surface(ren);
|
||||
*x = surface->w / scale;
|
||||
*y = surface->h / scale;
|
||||
void ren_get_size(RenWindow *window_renderer, int *x, int *y) {
|
||||
RenSurface rs = renwin_get_surface(window_renderer);
|
||||
*x = rs.surface->w / rs.scale;
|
||||
*y = rs.surface->h / rs.scale;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#define UNUSED
|
||||
#endif
|
||||
|
||||
|
||||
#define FONT_FALLBACK_MAX 10
|
||||
typedef struct RenFont RenFont;
|
||||
typedef enum { FONT_HINTING_NONE, FONT_HINTING_SLIGHT, FONT_HINTING_FULL } ERenFontHinting;
|
||||
|
@ -18,27 +19,32 @@ typedef enum { FONT_ANTIALIASING_NONE, FONT_ANTIALIASING_GRAYSCALE, FONT_ANTIALI
|
|||
typedef enum { FONT_STYLE_BOLD = 1, FONT_STYLE_ITALIC = 2, FONT_STYLE_UNDERLINE = 4, FONT_STYLE_SMOOTH = 8, FONT_STYLE_STRIKETHROUGH = 16 } ERenFontStyle;
|
||||
typedef struct { uint8_t b, g, r, a; } RenColor;
|
||||
typedef struct { int x, y, width, height; } RenRect;
|
||||
typedef struct { SDL_Surface *surface; int scale; } RenSurface;
|
||||
|
||||
RenFont* ren_font_load(const char *filename, float size, ERenFontAntialiasing antialiasing, ERenFontHinting hinting, unsigned char style);
|
||||
RenFont* ren_font_copy(RenFont* font, float size, ERenFontAntialiasing antialiasing, ERenFontHinting hinting, int style);
|
||||
struct RenWindow;
|
||||
typedef struct RenWindow RenWindow;
|
||||
extern RenWindow window_renderer;
|
||||
|
||||
RenFont* ren_font_load(RenWindow *window_renderer, const char *filename, float size, ERenFontAntialiasing antialiasing, ERenFontHinting hinting, unsigned char style);
|
||||
RenFont* ren_font_copy(RenWindow *window_renderer, RenFont* font, float size, ERenFontAntialiasing antialiasing, ERenFontHinting hinting, int style);
|
||||
const char* ren_font_get_path(RenFont *font);
|
||||
void ren_font_free(RenFont *font);
|
||||
int ren_font_group_get_tab_size(RenFont **font);
|
||||
int ren_font_group_get_height(RenFont **font);
|
||||
float ren_font_group_get_size(RenFont **font);
|
||||
void ren_font_group_set_size(RenFont **font, float size);
|
||||
void ren_font_group_set_size(RenWindow *window_renderer, RenFont **font, float size);
|
||||
void ren_font_group_set_tab_size(RenFont **font, int n);
|
||||
float ren_font_group_get_width(RenFont **font, const char *text, size_t len);
|
||||
float ren_draw_text(RenFont **font, const char *text, size_t len, float x, int y, RenColor color);
|
||||
double ren_font_group_get_width(RenWindow *window_renderer, RenFont **font, const char *text, size_t len);
|
||||
double ren_draw_text(RenSurface *rs, RenFont **font, const char *text, size_t len, float x, int y, RenColor color);
|
||||
|
||||
void ren_draw_rect(RenRect rect, RenColor color);
|
||||
void ren_draw_rect(RenSurface *rs, RenRect rect, RenColor color);
|
||||
|
||||
void ren_init(SDL_Window *win);
|
||||
void ren_resize_window();
|
||||
void ren_update_rects(RenRect *rects, int count);
|
||||
void ren_set_clip_rect(RenRect rect);
|
||||
void ren_get_size(int *x, int *y); /* Reports the size in points. */
|
||||
void ren_free_window_resources();
|
||||
void ren_resize_window(RenWindow *window_renderer);
|
||||
void ren_update_rects(RenWindow *window_renderer, RenRect *rects, int count);
|
||||
void ren_set_clip_rect(RenWindow *window_renderer, RenRect rect);
|
||||
void ren_get_size(RenWindow *window_renderer, int *x, int *y); /* Reports the size in points. */
|
||||
void ren_free_window_resources(RenWindow *window_renderer);
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
@ -17,26 +17,27 @@ static int query_surface_scale(RenWindow *ren) {
|
|||
static void setup_renderer(RenWindow *ren, int w, int h) {
|
||||
/* Note that w and h here should always be in pixels and obtained from
|
||||
a call to SDL_GL_GetDrawableSize(). */
|
||||
if (ren->renderer) {
|
||||
SDL_DestroyTexture(ren->texture);
|
||||
SDL_DestroyRenderer(ren->renderer);
|
||||
}
|
||||
if (!ren->renderer) {
|
||||
ren->renderer = SDL_CreateRenderer(ren->window, -1, 0);
|
||||
}
|
||||
if (ren->texture) {
|
||||
SDL_DestroyTexture(ren->texture);
|
||||
}
|
||||
ren->texture = SDL_CreateTexture(ren->renderer, SDL_PIXELFORMAT_BGRA32, SDL_TEXTUREACCESS_STREAMING, w, h);
|
||||
ren->surface_scale = query_surface_scale(ren);
|
||||
ren->rensurface.scale = query_surface_scale(ren);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void renwin_init_surface(UNUSED RenWindow *ren) {
|
||||
#ifdef LITE_USE_SDL_RENDERER
|
||||
if (ren->surface) {
|
||||
SDL_FreeSurface(ren->surface);
|
||||
if (ren->rensurface.surface) {
|
||||
SDL_FreeSurface(ren->rensurface.surface);
|
||||
}
|
||||
int w, h;
|
||||
SDL_GL_GetDrawableSize(ren->window, &w, &h);
|
||||
ren->surface = SDL_CreateRGBSurfaceWithFormat(0, w, h, 32, SDL_PIXELFORMAT_BGRA32);
|
||||
if (!ren->surface) {
|
||||
ren->rensurface.surface = SDL_CreateRGBSurfaceWithFormat(0, w, h, 32, SDL_PIXELFORMAT_BGRA32);
|
||||
if (!ren->rensurface.surface) {
|
||||
fprintf(stderr, "Error creating surface: %s", SDL_GetError());
|
||||
exit(1);
|
||||
}
|
||||
|
@ -44,12 +45,10 @@ void renwin_init_surface(UNUSED RenWindow *ren) {
|
|||
#endif
|
||||
}
|
||||
|
||||
int renwin_surface_scale(UNUSED RenWindow *ren) {
|
||||
#ifdef LITE_USE_SDL_RENDERER
|
||||
return ren->surface_scale;
|
||||
#else
|
||||
return 1;
|
||||
#endif
|
||||
void renwin_init_command_buf(RenWindow *ren) {
|
||||
ren->command_buf = NULL;
|
||||
ren->command_buf_idx = 0;
|
||||
ren->command_buf_size = 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -59,26 +58,27 @@ static RenRect scaled_rect(const RenRect rect, const int scale) {
|
|||
|
||||
|
||||
void renwin_clip_to_surface(RenWindow *ren) {
|
||||
SDL_Surface *surface = renwin_get_surface(ren);
|
||||
ren->clip = (RenRect) {0, 0, surface->w, surface->h};
|
||||
SDL_SetClipRect(renwin_get_surface(ren).surface, NULL);
|
||||
}
|
||||
|
||||
|
||||
void renwin_set_clip_rect(RenWindow *ren, RenRect rect) {
|
||||
ren->clip = scaled_rect(rect, renwin_surface_scale(ren));
|
||||
RenSurface rs = renwin_get_surface(ren);
|
||||
RenRect sr = scaled_rect(rect, rs.scale);
|
||||
SDL_SetClipRect(rs.surface, &(SDL_Rect){.x = sr.x, .y = sr.y, .w = sr.width, .h = sr.height});
|
||||
}
|
||||
|
||||
|
||||
SDL_Surface *renwin_get_surface(RenWindow *ren) {
|
||||
RenSurface renwin_get_surface(RenWindow *ren) {
|
||||
#ifdef LITE_USE_SDL_RENDERER
|
||||
return ren->surface;
|
||||
return ren->rensurface;
|
||||
#else
|
||||
SDL_Surface *surface = SDL_GetWindowSurface(ren->window);
|
||||
if (!surface) {
|
||||
fprintf(stderr, "Error getting window surface: %s", SDL_GetError());
|
||||
exit(1);
|
||||
}
|
||||
return surface;
|
||||
return (RenSurface){.surface = surface, .scale = 1};
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -87,7 +87,7 @@ void renwin_resize_surface(UNUSED RenWindow *ren) {
|
|||
int new_w, new_h;
|
||||
SDL_GL_GetDrawableSize(ren->window, &new_w, &new_h);
|
||||
/* Note that (w, h) may differ from (new_w, new_h) on retina displays. */
|
||||
if (new_w != ren->surface->w || new_h != ren->surface->h) {
|
||||
if (new_w != ren->rensurface.surface->w || new_h != ren->rensurface.surface->h) {
|
||||
renwin_init_surface(ren);
|
||||
renwin_clip_to_surface(ren);
|
||||
setup_renderer(ren, new_w, new_h);
|
||||
|
@ -101,14 +101,14 @@ void renwin_show_window(RenWindow *ren) {
|
|||
|
||||
void renwin_update_rects(RenWindow *ren, RenRect *rects, int count) {
|
||||
#ifdef LITE_USE_SDL_RENDERER
|
||||
const int scale = ren->surface_scale;
|
||||
const int scale = ren->rensurface.scale;
|
||||
for (int i = 0; i < count; i++) {
|
||||
const RenRect *r = &rects[i];
|
||||
const int x = scale * r->x, y = scale * r->y;
|
||||
const int w = scale * r->width, h = scale * r->height;
|
||||
const SDL_Rect sr = {.x = x, .y = y, .w = w, .h = h};
|
||||
int32_t *pixels = ((int32_t *) ren->surface->pixels) + x + ren->surface->w * y;
|
||||
SDL_UpdateTexture(ren->texture, &sr, pixels, ren->surface->w * 4);
|
||||
int32_t *pixels = ((int32_t *) ren->rensurface.surface->pixels) + x + ren->rensurface.surface->w * y;
|
||||
SDL_UpdateTexture(ren->texture, &sr, pixels, ren->rensurface.surface->w * 4);
|
||||
}
|
||||
SDL_RenderCopy(ren->renderer, ren->texture, NULL, NULL);
|
||||
SDL_RenderPresent(ren->renderer);
|
||||
|
@ -123,6 +123,6 @@ void renwin_free(RenWindow *ren) {
|
|||
#ifdef LITE_USE_SDL_RENDERER
|
||||
SDL_DestroyTexture(ren->texture);
|
||||
SDL_DestroyRenderer(ren->renderer);
|
||||
SDL_FreeSurface(ren->surface);
|
||||
SDL_FreeSurface(ren->rensurface.surface);
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -3,23 +3,24 @@
|
|||
|
||||
struct RenWindow {
|
||||
SDL_Window *window;
|
||||
RenRect clip; /* Clipping rect in pixel coordinates. */
|
||||
uint8_t *command_buf;
|
||||
size_t command_buf_idx;
|
||||
size_t command_buf_size;
|
||||
#ifdef LITE_USE_SDL_RENDERER
|
||||
SDL_Renderer *renderer;
|
||||
SDL_Texture *texture;
|
||||
SDL_Surface *surface;
|
||||
int surface_scale;
|
||||
RenSurface rensurface;
|
||||
#endif
|
||||
};
|
||||
typedef struct RenWindow RenWindow;
|
||||
|
||||
void renwin_init_surface(RenWindow *ren);
|
||||
int renwin_surface_scale(RenWindow *ren);
|
||||
void renwin_init_command_buf(RenWindow *ren);
|
||||
void renwin_clip_to_surface(RenWindow *ren);
|
||||
void renwin_set_clip_rect(RenWindow *ren, RenRect rect);
|
||||
void renwin_resize_surface(RenWindow *ren);
|
||||
void renwin_show_window(RenWindow *ren);
|
||||
void renwin_update_rects(RenWindow *ren, RenRect *rects, int count);
|
||||
void renwin_free(RenWindow *ren);
|
||||
SDL_Surface *renwin_get_surface(RenWindow *ren);
|
||||
RenSurface renwin_get_surface(RenWindow *ren);
|
||||
|
||||
|
|
Loading…
Reference in New Issue