diff --git a/.gitattributes b/.gitattributes index eef19e09b9..e6f3c2789b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,6 @@ +build.zig.zon.nix linguist-generated=true +build.zig.zon.txt linguist-generated=true +build.zig.zon2json-lock linguist-generated=true vendor/** linguist-vendored website/** linguist-documentation pkg/breakpad/vendor/** linguist-vendored diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index d5ee328e50..ec55f2dffd 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -1,6 +1,31 @@ on: [push, pull_request] name: Nix jobs: + required: + name: "Required Checks: Nix" + runs-on: namespace-profile-ghostty-sm + needs: + - check-zig-cache-hash + steps: + - id: status + name: Determine status + run: | + results=$(tr -d '\n' <<< '${{ toJSON(needs.*.result) }}') + if ! grep -q -v -E '(failure|cancelled)' <<< "$results"; then + result="failed" + else + result="success" + fi + { + echo "result=${result}" + echo "results=${results}" + } | tee -a "$GITHUB_OUTPUT" + - if: always() && steps.status.outputs.result != 'success' + name: Check for failed status + run: | + echo "One or more required build workflows failed: ${{ steps.status.outputs.results }}" + exit 1 + check-zig-cache-hash: if: github.repository == 'ghostty-org/ghostty' runs-on: namespace-profile-ghostty-sm @@ -25,5 +50,5 @@ jobs: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" useDaemon: false # sometimes fails on short jobs - - name: Check Zig cache hash - run: nix develop -c ./nix/build-support/check-zig-cache-hash.sh + - name: Check Zig cache + run: nix develop -c ./nix/build-support/check-zig-cache.sh diff --git a/.github/workflows/publish-tag.yml b/.github/workflows/publish-tag.yml new file mode 100644 index 0000000000..4589821400 --- /dev/null +++ b/.github/workflows/publish-tag.yml @@ -0,0 +1,74 @@ +on: + workflow_dispatch: + inputs: + version: + description: "Version to deploy (format: vX.Y.Z)" + required: true + +name: Publish Tagged Release + +# We must only run one release workflow at a time to prevent corrupting +# our release artifacts. +concurrency: + group: ${{ github.workflow }} + cancel-in-progress: false + +jobs: + setup: + runs-on: namespace-profile-ghostty-sm + outputs: + version: ${{ steps.extract_version.outputs.version }} + steps: + - name: Validate Version Input + run: | + if [[ ! "${{ github.event.inputs.version }}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Error: Version must follow the format vX.Y.Z (e.g., v1.0.0)." + exit 1 + fi + + echo "Version is valid: ${{ github.event.inputs.version }}" + + - name: Exract the Version + id: extract_version + run: | + VERSION=${{ github.event.inputs.version }} + VERSION=${VERSION#v} + echo "version=$VERSION" >> $GITHUB_OUTPUT + + upload: + needs: [setup] + runs-on: namespace-profile-ghostty-sm + env: + GHOSTTY_VERSION: ${{ needs.setup.outputs.version }} + steps: + - name: Validate Release Files + run: | + BASE="https://release.files.ghostty.org/${GHOSTTY_VERSION}" + curl -I -s -o /dev/null -w "%{http_code}" "${BASE}/ghostty-${GHOSTTY_VERSION}.tar.gz" | grep -q "^200$" || exit 1 + curl -I -s -o /dev/null -w "%{http_code}" "${BASE}/ghostty-${GHOSTTY_VERSION}.tar.gz.minisig" | grep -q "^200$" || exit 1 + curl -I -s -o /dev/null -w "%{http_code}" "${BASE}/ghostty-source.tar.gz" | grep -q "^200$" || exit 1 + curl -I -s -o /dev/null -w "%{http_code}" "${BASE}/ghostty-source.tar.gz.minisig" | grep -q "^200$" || exit 1 + curl -I -s -o /dev/null -w "%{http_code}" "${BASE}/ghostty-macos-universal.zip" | grep -q "^200$" || exit 1 + curl -I -s -o /dev/null -w "%{http_code}" "${BASE}/ghostty-macos-universal-dsym.zip" | grep -q "^200$" || exit 1 + curl -I -s -o /dev/null -w "%{http_code}" "${BASE}/Ghostty.dmg" | grep -q "^200$" || exit 1 + curl -I -s -o /dev/null -w "%{http_code}" "${BASE}/appcast-staged.xml" | grep -q "^200$" || exit 1 + + - name: Download Staged Appcast + run: | + curl -L https://release.files.ghostty.org/${GHOSTTY_VERSION}/appcast-staged.xml > appcast-staged.xml + mv appcast-staged.xml appcast.xml + + - name: Upload Appcast + run: | + rm -rf blob + mkdir blob + mv appcast.xml blob/appcast.xml + - name: Upload Appcast to R2 + uses: ryand56/r2-upload-action@latest + with: + r2-account-id: ${{ secrets.CF_R2_RELEASE_ACCOUNT_ID }} + r2-access-key-id: ${{ secrets.CF_R2_RELEASE_AWS_KEY }} + r2-secret-access-key: ${{ secrets.CF_R2_RELEASE_SECRET_KEY }} + r2-bucket: ghostty-release + source-dir: blob + destination-dir: ./ diff --git a/.github/workflows/release-pr.yml b/.github/workflows/release-pr.yml index e647c97203..ced4979977 100644 --- a/.github/workflows/release-pr.yml +++ b/.github/workflows/release-pr.yml @@ -68,7 +68,7 @@ jobs: # Setup Sparkle - name: Setup Sparkle env: - SPARKLE_VERSION: 2.6.3 + SPARKLE_VERSION: 2.6.4 run: | mkdir -p .action/sparkle cd .action/sparkle diff --git a/.github/workflows/release-tag.yml b/.github/workflows/release-tag.yml index cf94bf23ef..1789576a8c 100644 --- a/.github/workflows/release-tag.yml +++ b/.github/workflows/release-tag.yml @@ -7,6 +7,7 @@ on: upload: description: "Upload final artifacts to R2" default: false + push: tags: - "v[0-9]+.[0-9]+.[0-9]+" @@ -135,7 +136,7 @@ jobs: - name: Setup Sparkle env: - SPARKLE_VERSION: 2.6.3 + SPARKLE_VERSION: 2.6.4 run: | mkdir -p .action/sparkle cd .action/sparkle @@ -297,7 +298,7 @@ jobs: - name: Setup Sparkle env: - SPARKLE_VERSION: 2.6.3 + SPARKLE_VERSION: 2.6.4 run: | mkdir -p .action/sparkle cd .action/sparkle @@ -367,6 +368,7 @@ jobs: mv ghostty-macos-universal.zip blob/${GHOSTTY_VERSION}/ghostty-macos-universal.zip mv ghostty-macos-universal-dsym.zip blob/${GHOSTTY_VERSION}/ghostty-macos-universal-dsym.zip mv Ghostty.dmg blob/${GHOSTTY_VERSION}/Ghostty.dmg + mv appcast.xml blob/${GHOSTTY_VERSION}/appcast-staged.xml - name: Upload to R2 uses: ryand56/r2-upload-action@latest with: @@ -376,18 +378,3 @@ jobs: r2-bucket: ghostty-release source-dir: blob destination-dir: ./ - - - name: Prep Appcast - run: | - rm -rf blob - mkdir blob - mv appcast.xml blob/appcast.xml - - name: Upload Appcast to R2 - uses: ryand56/r2-upload-action@latest - with: - r2-account-id: ${{ secrets.CF_R2_RELEASE_ACCOUNT_ID }} - r2-access-key-id: ${{ secrets.CF_R2_RELEASE_AWS_KEY }} - r2-secret-access-key: ${{ secrets.CF_R2_RELEASE_SECRET_KEY }} - r2-bucket: ghostty-release - source-dir: blob - destination-dir: ./ diff --git a/.github/workflows/release-tip.yml b/.github/workflows/release-tip.yml index e239dda4f3..a031f3ad14 100644 --- a/.github/workflows/release-tip.yml +++ b/.github/workflows/release-tip.yml @@ -164,7 +164,7 @@ jobs: # Setup Sparkle - name: Setup Sparkle env: - SPARKLE_VERSION: 2.6.3 + SPARKLE_VERSION: 2.6.4 run: | mkdir -p .action/sparkle cd .action/sparkle diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 81d58a1efe..e64b2c4d85 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,6 +6,47 @@ on: name: Test jobs: + required: + name: "Required Checks: Test" + runs-on: namespace-profile-ghostty-sm + needs: + - build + - build-bench + - build-linux-libghostty + - build-nix + - build-snap + - build-macos + - build-macos-matrix + - build-windows + - test + - test-gtk + - test-sentry-linux + - test-macos + - prettier + - alejandra + - typos + - test-pkg-linux + - test-debian-12 + steps: + - id: status + name: Determine status + run: | + results=$(tr -d '\n' <<< '${{ toJSON(needs.*.result) }}') + if ! grep -q -v -E '(failure|cancelled)' <<< "$results"; then + result="failed" + else + result="success" + fi + { + echo "result=${result}" + echo "results=${results}" + } | tee -a "$GITHUB_OUTPUT" + - if: always() && steps.status.outputs.result != 'success' + name: Check for failed status + run: | + echo "One or more required build workflows failed: ${{ steps.status.outputs.results }}" + exit 1 + build: strategy: fail-fast: false @@ -163,10 +204,14 @@ jobs: - name: XCode Select run: sudo xcode-select -s /Applications/Xcode_16.0.app + - name: get the Zig deps + id: deps + run: nix build -L .#deps && echo "deps=$(readlink ./result)" >> $GITHUB_OUTPUT + # GhosttyKit is the framework that is built from Zig for our native # Mac app to access. - name: Build GhosttyKit - run: nix develop -c zig build + run: nix develop -c zig build --system ${{ steps.deps.outputs.deps }} # The native app is built with native XCode tooling. This also does # codesigning. IMPORTANT: this must NOT run in a Nix environment. @@ -199,35 +244,65 @@ jobs: - name: XCode Select run: sudo xcode-select -s /Applications/Xcode_16.0.app + - name: get the Zig deps + id: deps + run: nix build -L .#deps && echo "deps=$(readlink ./result)" >> $GITHUB_OUTPUT + - name: Test All run: | # OpenGL - nix develop -c zig build test -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=freetype - nix develop -c zig build test -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext - nix develop -c zig build test -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_freetype - nix develop -c zig build test -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_harfbuzz - nix develop -c zig build test -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_noshape + nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=freetype + nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext + nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_freetype + nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_harfbuzz + nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_noshape # Metal - nix develop -c zig build test -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=freetype - nix develop -c zig build test -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext - nix develop -c zig build test -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_freetype - nix develop -c zig build test -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_harfbuzz - nix develop -c zig build test -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_noshape + nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=freetype + nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext + nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_freetype + nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_harfbuzz + nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_noshape - name: Build All run: | - nix develop -c zig build -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=freetype - nix develop -c zig build -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext - nix develop -c zig build -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_freetype - nix develop -c zig build -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_harfbuzz - nix develop -c zig build -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_noshape - - nix develop -c zig build -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=freetype - nix develop -c zig build -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext - nix develop -c zig build -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_freetype - nix develop -c zig build -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_harfbuzz - nix develop -c zig build -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_noshape + nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=freetype + nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext + nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_freetype + nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_harfbuzz + nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_noshape + + nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=freetype + nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext + nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_freetype + nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_harfbuzz + nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_noshape + + build-snap: + strategy: + fail-fast: false + matrix: + os: + [namespace-profile-ghostty-snap, namespace-profile-ghostty-snap-arm64] + runs-on: ${{ matrix.os }} + needs: test + env: + ZIG_LOCAL_CACHE_DIR: /zig/local-cache + ZIG_GLOBAL_CACHE_DIR: /zig/global-cache + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + fetch-tags: true + - name: Setup Cache + uses: namespacelabs/nscloud-cache-action@v1.2.0 + with: + path: | + /nix + /zig + - run: sudo apt install -y udev + - run: sudo systemctl start systemd-udevd + - uses: snapcore/action-build@v1 build-windows: runs-on: windows-2022 @@ -247,10 +322,10 @@ jobs: run: | # Get the zig version from build.zig so that it only needs to be updated $fileContent = Get-Content -Path "build.zig" -Raw - $pattern = 'const required_zig = "(.*?)";' + $pattern = 'buildpkg\.requireZig\("(.*?)"\);' $zigVersion = [regex]::Match($fileContent, $pattern).Groups[1].Value - Write-Output $version $version = "zig-windows-x86_64-$zigVersion" + Write-Output $version $uri = "https://ziglang.org/download/$zigVersion/$version.zip" Invoke-WebRequest -Uri "$uri" -OutFile ".\zig-windows.zip" Expand-Archive -Path ".\zig-windows.zip" -DestinationPath ".\" -Force @@ -327,7 +402,7 @@ jobs: run: nix develop -c zig build -Dapp-runtime=none test - name: Test GTK Build - run: nix develop -c zig build -Dapp-runtime=gtk -Dgtk-adwaita=true -Demit-docs + run: nix develop -c zig build -Dapp-runtime=gtk -Demit-docs - name: Test GLFW Build run: nix develop -c zig build -Dapp-runtime=glfw @@ -340,9 +415,9 @@ jobs: strategy: fail-fast: false matrix: - adwaita: ["true", "false"] x11: ["true", "false"] - name: GTK adwaita=${{ matrix.adwaita }} x11=${{ matrix.x11 }} + wayland: ["true", "false"] + name: GTK x11=${{ matrix.x11 }} wayland=${{ matrix.wayland }} runs-on: namespace-profile-ghostty-sm needs: test env: @@ -373,8 +448,8 @@ jobs: nix develop -c \ zig build \ -Dapp-runtime=gtk \ - -Dgtk-adwaita=${{ matrix.adwaita }} \ - -Dgtk-x11=${{ matrix.x11 }} + -Dgtk-x11=${{ matrix.x11 }} \ + -Dgtk-wayland=${{ matrix.wayland }} test-sentry-linux: strategy: @@ -430,8 +505,12 @@ jobs: - name: XCode Select run: sudo xcode-select -s /Applications/Xcode_16.0.app + - name: get the Zig deps + id: deps + run: nix build -L .#deps && echo "deps=$(readlink ./result)" >> $GITHUB_OUTPUT + - name: test - run: nix develop -c zig build test + run: nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} prettier: if: github.repository == 'ghostty-org/ghostty' @@ -548,3 +627,26 @@ jobs: - name: Test ${{ matrix.pkg }} Build run: | nix develop -c sh -c "cd pkg/${{ matrix.pkg }} ; zig build test" + + test-debian-12: + name: Test build on Debian 12 + runs-on: namespace-profile-ghostty-sm + needs: test + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install and configure Namespace CLI + uses: namespacelabs/nscloud-setup@v0 + + - name: Configure Namespace powered Buildx + uses: namespacelabs/nscloud-setup-buildx-action@v0 + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + file: src/build/docker/debian/Dockerfile + build-args: | + DISTRO_VERSION=12 + ZIG_VERSION=0.13.0 diff --git a/.github/workflows/update-colorschemes.yml b/.github/workflows/update-colorschemes.yml index 569ef6765e..8a6c77ea5f 100644 --- a/.github/workflows/update-colorschemes.yml +++ b/.github/workflows/update-colorschemes.yml @@ -48,14 +48,14 @@ jobs: run: | # Only proceed if build.zig.zon has changed if ! git diff --exit-code build.zig.zon; then - nix develop -c ./nix/build-support/check-zig-cache-hash.sh --update - nix develop -c ./nix/build-support/check-zig-cache-hash.sh + nix develop -c ./nix/build-support/check-zig-cache.sh --update + nix develop -c ./nix/build-support/check-zig-cache.sh fi # Verify the build still works. We choose an arbitrary build type # as a canary instead of testing all build types. - name: Test Build - run: nix develop -c zig build -Dapp-runtime=gtk -Dgtk-adwaita=true + run: nix build .#ghostty - name: Create pull request uses: peter-evans/create-pull-request@v7 @@ -66,7 +66,7 @@ jobs: commit-message: "deps: Update iTerm2 color schemes" add-paths: | build.zig.zon - nix/zigCacheHash.nix + build.zig.zon.nix body: | Upstream revision: https://github.com/mbadolato/iTerm2-Color-Schemes/tree/${{ steps.zig_fetch.outputs.upstream_rev }} labels: dependencies diff --git a/.gitignore b/.gitignore index 0e301f8c41..db8457e1f8 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ test/cases/**/*.actual.png glad.zip /Box_test.ppm /Box_test_diff.ppm +/ghostty.qcow2 diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000000..835244ebc5 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,149 @@ +# This file documents the subsystem maintainers of the Ghostty project +# along with the responsibilities of a maintainer and how one can become +# a maintainer. +# +# Ghostty follows a subsystem maintainer model where distinguished +# contributors (with mutual agreement) are designated as maintainers of a +# specific subset of the project. A subsystem maintainer has more privileges +# and authority over a specific part of the project than a regular +# contributor and deference is given to them when making decisions about +# their subsystem. +# +# Ultimately Ghostty has a BDFL (Benevolent Dictator For Life) model +# currently with @mitchellh as the BDFL. The BDFL has the final say in all +# decisions and may override a maintainer's decision if necessary. I like to +# say its a BDFLFN (Benevolent Dictator For Life "For Now") model because +# long term I'd like to see the project be more community driven. But for +# now, early in its life, we're going with this model. +# +# ## Privileges +# +# - Authority to approve or reject pull requests in their subsystem. +# - Authority to moderate issues and discussions in their subsystem. +# - Authority to make roadmap and design decisions about their subsystem +# with input only from other subsystem maintainers. +# +# In all scenarios, the BDFL doesn't need to be consulted for decisions +# but may revert or override decisions if necessary. The expectation is +# that maintainers will be trusted to make the right decisions for their +# subsystem and this will be rare. +# +# ## Responsibilities +# +# Subsystem maintainership is a voluntary role and maintainers are not +# expected to dedicate any amount of time to the project. However, if a +# maintainer is inactive for a long period of time, they may be removed from +# the maintainers list to avoid bitrot or outdated information. +# +# Maintainers are expected to be exemplary members of the community and +# should be respectful, helpful, and professional in all interactions. +# This is both in regards to the community at large as well as other +# subsystem maintainers as well as @mitchellh. +# +# As technical leaders, maintainers are expected to be mindful about +# breaking changes, performance, user impact, and other technical +# considerations in their subsystem. They should be considerate of large +# changes and should be able to justify their decisions. +# +# Notably, maintainers have NO OBLIGATION to review pull requests or issues +# in their subsystem. They have full discretion to review or not review +# anything they want. This isn't a job! It is a role of trust and authority +# and the expectation is that maintainers will use their best judgement. +# +# ## Becoming a Maintainer +# +# Maintainer candidates are noticed and proposed by the community. Anyone +# may propose themselves or someone else as a maintainer. The BDFL along +# with existing maintainers will discuss and decide. +# +# Generally, we want to see consistent high quality contributions to a +# specific subsystem before considering someone as a maintainer. There isn't +# an exact number of contributions or time period required but generally +# we're looking for an order of a dozen or more contributions over a period of +# months, at least. +# +# # Subsystem List +# +# The subsystems don't fully cover the entirety of the Ghostty project but +# are created organically as experts in certain areas emerge. If you feel +# you are an expert in a certain area and would like to be a maintainer, +# please reach out to @mitchellh on Discord. +# +# (Alphabetical order) +# +# - @ghostty-org/font - All things font related including discovery, +# rasterization, shaping, coloring, etc. +# +# - @ghostty-org/gtk - Anything GTK-related in the project, primarily +# the GTK apprt. Also includes X11/Wayland integrations and general +# Linux support. +# +# - @ghostty-org/macos - The Ghostty macOS app and any macOS-specific +# features, configurations, etc. +# +# - @ghostty-org/renderer - Ghostty rendering subsystem, including the +# rendering abstractions as well as specific renderers like OpenGL +# and Metal. +# +# - @ghostty-org/shell - Ghostty shell integration, including shell +# completions, shell detection, and any other shell interactions. +# +# - @ghostty-org/terminal - The terminal emulator subsystem, including +# subprocess management and pty handling, escape sequence parsing, +# key encoding, etc. +# +# ## Outside of Ghostty +# +# Other "subsystems" exist outside of Ghostty and will not be represented +# in this CODEOWNERS file: +# +# - @ghostty-org/discord-bot - Maintainers of the Ghostty Discord bot. +# +# - @ghostty-org/website - Maintainers of the Ghostty website. + +# Font +/src/font/ @ghostty-org/font +/pkg/fontconfig/ @ghostty-org/font +/pkg/freetype/ @ghostty-org/font +/pkg/harfbuzz/ @ghostty-org/font + +# GTK +/src/apprt/gtk/ @ghostty-org/gtk +/src/os/cgroup.zig @ghostty-org/gtk +/src/os/flatpak.zig @ghostty-org/gtk +/dist/linux/ @ghostty-org/gtk + +# macOS +# +# This includes libghostty because the macOS apprt is built on top of +# libghostty and often requires or is impacted by changes to libghostty. +# macOS subsystem maintainers are expected to only work on libghostty +# insofar as it impacts the macOS apprt. +/include/ghostty.h @ghostty-org/macos +/src/apprt/embedded.zig @ghostty-org/macos +/src/os/cf_release_thread.zig @ghostty-org/macos +/src/os/macos.zig @ghostty-org/macos +/macos/ @ghostty-org/macos +/dist/macos/ @ghostty-org/macos +/pkg/apple-sdk/ @ghostty-org/macos +/pkg/macos/ @ghostty-org/macos + +# Renderer +/src/renderer.zig @ghostty-org/renderer +/src/renderer/ @ghostty-org/renderer +/pkg/glslang/ @ghostty-org/renderer +/pkg/opengl/ @ghostty-org/renderer +/pkg/spirv-cross/ @ghostty-org/renderer +/pkg/wuffs/ @ghostty-org/renderer + +# Shell +/src/shell-integration/ @ghostty-org/shell +/src/termio/shell-integration.zig @ghostty-org/shell + +# Terminal +/src/simd/ @ghostty-org/terminal +/src/terminal/ @ghostty-org/terminal +/src/terminfo/ @ghostty-org/terminal +/src/unicode/ @ghostty-org/terminal +/src/Surface.zig @ghostty-org/terminal +/src/surface_mouse.zig @ghostty-org/terminal diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index af3c30be7d..e4d148df87 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -77,3 +77,183 @@ pull request will be accepted with a high degree of certainty. > **Pull requests are NOT a place to discuss feature design.** Please do > not open a WIP pull request to discuss a feature. Instead, use a discussion > and link to your branch. + +# Developer Guide + +> [!NOTE] +> +> **The remainder of this file is dedicated to developers actively +> working on Ghostty.** If you're a user reporting an issue, you can +> ignore the rest of this document. + +## Input Stack Testing + +The input stack is the part of the codebase that starts with a +key event and ends with text encoding being sent to the pty (it +does not include _rendering_ the text, which is part of the +font or rendering stack). + +If you modify any part of the input stack, you must manually verify +all the following input cases work properly. We unfortunately do +not automate this in any way, but if we can do that one day that'd +save a LOT of grief and time. + +Note: this list may not be exhaustive, I'm still working on it. + +### Linux IME + +IME (Input Method Editors) are a common source of bugs in the input stack, +especially on Linux since there are multiple different IME systems +interacting with different windowing systems and application frameworks +all written by different organizations. + +The following matrix should be tested to ensure that all IME input works +properly: + +1. Wayland, X11 +2. ibus, fcitx, none +3. Dead key input (e.g. Spanish), CJK (e.g. Japanese), Emoji, Unicode Hex +4. ibus versions: 1.5.29, 1.5.30, 1.5.31 (each exhibit slightly different behaviors) + +> [!NOTE] +> +> This is a **work in progress**. I'm still working on this list and it +> is not complete. As I find more test cases, I will add them here. + +#### Dead Key Input + +Set your keyboard layout to "Spanish" (or another layout that uses dead keys). + +1. Launch Ghostty +2. Press `'` +3. Press `a` +4. Verify that `á` is displayed + +Note that the dead key may or may not show a preedit state visually. +For ibus and fcitx it does but for the "none" case it does not. Importantly, +the text should be correct when it is sent to the pty. + +We should also test canceling dead key input: + +1. Launch Ghostty +2. Press `'` +3. Press escape +4. Press `a` +5. Verify that `a` is displayed (no diacritic) + +#### CJK Input + +Configure fcitx or ibus with a keyboard layout like Japanese or Mozc. The +exact layout doesn't matter. + +1. Launch Ghostty +2. Press `Ctrl+Shift` to switch to "Hiragana" +3. On a US physical layout, type: `konn`, you should see `こん` in preedit. +4. Press `Enter` +5. Verify that `こん` is displayed in the terminal. + +We should also test switching input methods while preedit is active, which +should commit the text: + +1. Launch Ghostty +2. Press `Ctrl+Shift` to switch to "Hiragana" +3. On a US physical layout, type: `konn`, you should see `こん` in preedit. +4. Press `Ctrl+Shift` to switch to another layout (any) +5. Verify that `こん` is displayed in the terminal as committed text. + +## Nix Virtual Machines + +Several Nix virtual machine definitions are provided by the project for testing +and developing Ghostty against multiple different Linux desktop environments. + +Running these requires a working Nix installation, either Nix on your +favorite Linux distribution, NixOS, or macOS with nix-darwin installed. Further +requirements for macOS are detailed below. + +VMs should only be run on your local desktop and then powered off when not in +use, which will discard any changes to the VM. + +The VM definitions provide minimal software "out of the box" but additional +software can be installed by using standard Nix mechanisms like `nix run nixpkgs#`. + +### Linux + +1. Check out the Ghostty source and change to the directory. +2. Run `nix run .#`. `` can be any of the VMs defined in the + `nix/vm` directory (without the `.nix` suffix) excluding any file prefixed + with `common` or `create`. +3. The VM will build and then launch. Depending on the speed of your system, this + can take a while, but eventually you should get a new VM window. +4. The Ghostty source directory should be mounted to `/tmp/shared` in the VM. Depending + on what UID and GID of the user that you launched the VM as, `/tmp/shared` _may_ be + writable by the VM user, so be careful! + +### macOS + +1. To run the VMs on macOS you will need to enable the Linux builder in your `nix-darwin` + config. This _should_ be as simple as adding `nix.linux-builder.enable=true` to your + configuration and then rebuilding. See [this](https://nixcademy.com/posts/macos-linux-builder/) + blog post for more information about the Linux builder and how to tune the performance. +2. Once the Linux builder has been enabled, you should be able to follow the Linux instructions + above to launch a VM. + +### Custom VMs + +To easily create a custom VM without modifying the Ghostty source, create a new +directory, then create a file called `flake.nix` with the following text in the +new directory. + +``` +{ + inputs = { + nixpkgs.url = "nixpkgs/nixpkgs-unstable"; + ghostty.url = "github:ghostty-org/ghostty"; + }; + outputs = { + nixpkgs, + ghostty, + ... + }: { + nixosConfigurations.custom-vm = ghostty.create-gnome-vm { + nixpkgs = nixpkgs; + system = "x86_64-linux"; + overlay = ghostty.overlays.releasefast; + # module = ./configuration.nix # also works + module = {pkgs, ...}: { + environment.systemPackages = [ + pkgs.btop + ]; + }; + }; + }; +} +``` + +The custom VM can then be run with a command like this: + +``` +nix run .#nixosConfigurations.custom-vm.config.system.build.vm +``` + +A file named `ghostty.qcow2` will be created that is used to persist any changes +made in the VM. To "reset" the VM to default delete the file and it will be +recreated the next time you run the VM. + +### Contributing new VM definitions + +#### VM Acceptance Criteria + +We welcome the contribution of new VM definitions, as long as they meet the following criteria: + +1. The should be different enough from existing VM definitions that they represent a distinct + user (and developer) experience. +2. There's a significant Ghostty user population that uses a similar environment. +3. The VMs can be built using only packages from the current stable NixOS release. + +#### VM Definition Criteria + +1. VMs should be as minimal as possible so that they build and launch quickly. + Additional software can be added at runtime with a command like `nix run nixpkgs#`. +2. VMs should not expose any services to the network, or run any remote access + software like SSH daemons, VNC or RDP. +3. VMs should auto-login using the "ghostty" user. diff --git a/PACKAGING.md b/PACKAGING.md index 82c7c56739..6a4c01b6f5 100644 --- a/PACKAGING.md +++ b/PACKAGING.md @@ -23,13 +23,6 @@ https://release.files.ghostty.org/VERSION/ghostty-VERSION.tar.gz https://release.files.ghostty.org/VERSION/ghostty-VERSION.tar.gz.minisig ``` -> [!NOTE] -> -> **Version 1.0.0 the filename is `ghostty-source.tar.gz`.** Future -> versions will use the `ghostty-VERSION.tar.gz` format since it is more -> typical for source tarballs. But for version 1.0.0, the filename is -> `ghostty-source.tar.gz`. - Signature files are signed with [minisign](https://jedisct1.github.io/minisign/) using the following public key: @@ -88,6 +81,13 @@ for system packages which separate a build and install step, since the install step can then be done with a `mv` or `cp` command (from `/tmp/ghostty` to wherever the package manager expects it). +> [!NOTE] +> +> **Version 1.1.1 and 1.1.2 are missing `fetch-zig-cache.sh`.** This was +> an oversight on the release process. You can use the script from version +> 1.1.0 to fetch the Zig cache for these versions. Future versions will +> restore the script. + ### Build Options Ghostty uses the Zig build system. You can see all available build options by diff --git a/build.zig b/build.zig index 1364745cec..38d2bca6dd 100644 --- a/build.zig +++ b/build.zig @@ -3,21 +3,7 @@ const builtin = @import("builtin"); const buildpkg = @import("src/build/main.zig"); comptime { - // This is the required Zig version for building this project. We allow - // any patch version but the major and minor must match exactly. - const required_zig = "0.13.0"; - - // Fail compilation if the current Zig version doesn't meet requirements. - const current_vsn = builtin.zig_version; - const required_vsn = std.SemanticVersion.parse(required_zig) catch unreachable; - if (current_vsn.major != required_vsn.major or - current_vsn.minor != required_vsn.minor) - { - @compileError(std.fmt.comptimePrint( - "Your Zig version v{} does not meet the required build version of v{}", - .{ current_vsn, required_vsn }, - )); - } + buildpkg.requireZig("0.13.0"); } pub fn build(b: *std.Build) !void { diff --git a/build.zig.zon b/build.zig.zon index 518022486f..cc617cf513 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,32 +1,39 @@ .{ .name = "ghostty", - .version = "1.0.2", + .version = "1.1.3", .paths = .{""}, .dependencies = .{ // Zig libs + .libxev = .{ - .url = "https://github.com/mitchellh/libxev/archive/db6a52bafadf00360e675fefa7926e8e6c0e9931.tar.gz", - .hash = "12206029de146b685739f69b10a6f08baee86b3d0a5f9a659fa2b2b66c9602078bbf", + // mitchellh/libxev + .url = "https://deps.files.ghostty.org/libxev-1220ebf88622c4d502dc59e71347e4d28c47e033f11b59aff774ae5787565c40999c.tar.gz", + .hash = "1220ebf88622c4d502dc59e71347e4d28c47e033f11b59aff774ae5787565c40999c", }, .mach_glfw = .{ - .url = "https://github.com/mitchellh/mach-glfw/archive/37c2995f31abcf7e8378fba68ddcf4a3faa02de0.tar.gz", + // mitchellh/mach-glfw + .url = "https://deps.files.ghostty.org/mach_glfw-12206ed982e709e565d536ce930701a8c07edfd2cfdce428683f3f2a601d37696a62.tar.gz", .hash = "12206ed982e709e565d536ce930701a8c07edfd2cfdce428683f3f2a601d37696a62", .lazy = true, }, .vaxis = .{ - .url = "git+https://github.com/rockorager/libvaxis/?ref=main#6d729a2dc3b934818dffe06d2ba3ce02841ed74b", - .hash = "12200df4ebeaed45de26cb2c9f3b6f3746d8013b604e035dae658f86f586c8c91d2f", + // rockorager/libvaxis + .url = "git+https://github.com/rockorager/libvaxis#2237a7059eae99e9f132dd5acd1555e49d6c7d93", + .hash = "1220f5aec880d4f430cc1597ede88f1530da69e39a4986080e976b0c7b919c2ebfeb", }, .z2d = .{ - .url = "git+https://github.com/vancluever/z2d?ref=v0.4.0#4638bb02a9dc41cc2fb811f092811f6a951c752a", + // vancluever/z2d + .url = "https://deps.files.ghostty.org/z2d-12201f0d542e7541cf492a001d4d0d0155c92f58212fbcb0d224e95edeba06b5416a.tar.gz", .hash = "12201f0d542e7541cf492a001d4d0d0155c92f58212fbcb0d224e95edeba06b5416a", }, .zig_objc = .{ - .url = "https://github.com/mitchellh/zig-objc/archive/9b8ba849b0f58fe207ecd6ab7c147af55b17556e.tar.gz", + // mitchellh/zig-objc + .url = "https://deps.files.ghostty.org/zig_objc-1220e17e64ef0ef561b3e4b9f3a96a2494285f2ec31c097721bf8c8677ec4415c634.tar.gz", .hash = "1220e17e64ef0ef561b3e4b9f3a96a2494285f2ec31c097721bf8c8677ec4415c634", }, .zig_js = .{ - .url = "https://github.com/mitchellh/zig-js/archive/d0b8b0a57c52fbc89f9d9fecba75ca29da7dd7d1.tar.gz", + // mitchellh/zig-js + .url = "https://deps.files.ghostty.org/zig_js-12205a66d423259567764fa0fc60c82be35365c21aeb76c5a7dc99698401f4f6fefc.tar.gz", .hash = "12205a66d423259567764fa0fc60c82be35365c21aeb76c5a7dc99698401f4f6fefc", }, .ziglyph = .{ @@ -34,13 +41,20 @@ .hash = "12207831bce7d4abce57b5a98e8f3635811cfefd160bca022eb91fe905d36a02cf25", }, .zig_wayland = .{ - .url = "https://codeberg.org/ifreund/zig-wayland/archive/a5e2e9b6a6d7fba638ace4d4b24a3b576a02685b.tar.gz", - .hash = "1220d41b23ae70e93355bb29dac1c07aa6aeb92427a2dffc4375e94b4de18111248c", + // codeberg ifreund/zig-wayland + .url = "https://deps.files.ghostty.org/zig-wayland-fbfe3b4ac0b472a27b1f1a67405436c58cbee12d.tar.gz", + .hash = "12209ca054cb1919fa276e328967f10b253f7537c4136eb48f3332b0f7cf661cad38", }, .zf = .{ - .url = "git+https://github.com/natecraddock/zf/?ref=main#ed99ca18b02dda052e20ba467e90b623c04690dd", + // natecraddock/zf + .url = "https://deps.files.ghostty.org/zf-1220edc3b8d8bedbb50555947987e5e8e2f93871ca3c8e8d4cc8f1377c15b5dd35e8.tar.gz", .hash = "1220edc3b8d8bedbb50555947987e5e8e2f93871ca3c8e8d4cc8f1377c15b5dd35e8", }, + .gobject = .{ + // ianprime0509/zig-gobject + .url = "https://deps.files.ghostty.org/gobject-12208d70ee791d7ef7e16e1c3c9c1127b57f1ed066a24f87d57fc9f730c5dc394b9d.tar.zst", + .hash = "12208d70ee791d7ef7e16e1c3c9c1127b57f1ed066a24f87d57fc9f730c5dc394b9d", + }, // C libs .cimgui = .{ .path = "./pkg/cimgui" }, @@ -72,15 +86,15 @@ .hash = "12201a57c6ce0001aa034fa80fba3e1cd2253c560a45748f4f4dd21ff23b491cddef", }, .plasma_wayland_protocols = .{ - .url = "git+https://github.com/KDE/plasma-wayland-protocols?ref=main#db525e8f9da548cffa2ac77618dd0fbe7f511b86", + .url = "https://deps.files.ghostty.org/plasma_wayland_protocols-12207e0851c12acdeee0991e893e0132fc87bb763969a585dc16ecca33e88334c566.tar.gz", .hash = "12207e0851c12acdeee0991e893e0132fc87bb763969a585dc16ecca33e88334c566", }, // Other .apple_sdk = .{ .path = "./pkg/apple-sdk" }, .iterm2_themes = .{ - .url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/4762ad5bd6d3906e28babdc2bda8a967d63a63be.tar.gz", - .hash = "1220a263b22113273d01bd33e3c06b8119cb2f63b4e5d414a85d88e3aa95bb68a2de", + .url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/efb1bb1843500a751eb30afa58fe48a6bec8952c.tar.gz", + .hash = "1220a1dbe41bc69aacf75026a7158812198ea265fb9cac64dcb91cd31f3b1b8c1f92", }, }, } diff --git a/build.zig.zon.nix b/build.zig.zon.nix new file mode 100644 index 0000000000..e1eecdd3ea --- /dev/null +++ b/build.zig.zon.nix @@ -0,0 +1,390 @@ +# generated by zon2nix (https://github.com/Cloudef/zig2nix) +{ + lib, + linkFarm, + fetchurl, + fetchgit, + runCommandLocal, + zig, + name ? "zig-packages", +}: +with builtins; +with lib; let + unpackZigArtifact = { + name, + artifact, + }: + runCommandLocal name + { + nativeBuildInputs = [zig]; + } + '' + hash="$(zig fetch --global-cache-dir "$TMPDIR" ${artifact})" + mv "$TMPDIR/p/$hash" "$out" + chmod 755 "$out" + ''; + + fetchZig = { + name, + url, + hash, + }: let + artifact = fetchurl {inherit url hash;}; + in + unpackZigArtifact {inherit name artifact;}; + + fetchGitZig = { + name, + url, + hash, + }: let + parts = splitString "#" url; + url_base = elemAt parts 0; + url_without_query = elemAt (splitString "?" url_base) 0; + rev_base = elemAt parts 1; + rev = + if match "^[a-fA-F0-9]{40}$" rev_base != null + then rev_base + else "refs/heads/${rev_base}"; + in + fetchgit { + inherit name rev hash; + url = url_without_query; + deepClone = false; + }; + + fetchZigArtifact = { + name, + url, + hash, + }: let + parts = splitString "://" url; + proto = elemAt parts 0; + path = elemAt parts 1; + fetcher = { + "git+http" = fetchGitZig { + inherit name hash; + url = "http://${path}"; + }; + "git+https" = fetchGitZig { + inherit name hash; + url = "https://${path}"; + }; + http = fetchZig { + inherit name hash; + url = "http://${path}"; + }; + https = fetchZig { + inherit name hash; + url = "https://${path}"; + }; + }; + in + fetcher.${proto}; +in + linkFarm name [ + { + name = "1220ebf88622c4d502dc59e71347e4d28c47e033f11b59aff774ae5787565c40999c"; + path = fetchZigArtifact { + name = "libxev"; + url = "https://deps.files.ghostty.org/libxev-1220ebf88622c4d502dc59e71347e4d28c47e033f11b59aff774ae5787565c40999c.tar.gz"; + hash = "sha256-VHP90NTytIZ8UZsYRKOOxN490/I6yv6ec40sP8y5MJ8="; + }; + } + { + name = "12206ed982e709e565d536ce930701a8c07edfd2cfdce428683f3f2a601d37696a62"; + path = fetchZigArtifact { + name = "mach_glfw"; + url = "https://deps.files.ghostty.org/mach_glfw-12206ed982e709e565d536ce930701a8c07edfd2cfdce428683f3f2a601d37696a62.tar.gz"; + hash = "sha256-HhXIvWUS8/CHWY4VXPG2ZEo+we8XOn3o5rYJCQ1n8Nk="; + }; + } + { + name = "1220736fa4ba211162c7a0e46cc8fe04d95921927688bff64ab5da7420d098a7272d"; + path = fetchZigArtifact { + name = "glfw"; + url = "https://github.com/mitchellh/glfw/archive/b552c6ec47326b94015feddb36058ea567b87159.tar.gz"; + hash = "sha256-IeBVAOQmtyFqVxzuXPek1onuPwIamcOyYtxqKpPEQjU="; + }; + } + { + name = "12202adbfecdad671d585c9a5bfcbd5cdf821726779430047742ce1bf94ad67d19cb"; + path = fetchZigArtifact { + name = "xcode_frameworks"; + url = "https://github.com/mitchellh/xcode-frameworks/archive/69801c154c39d7ae6129ea1ba8fe1afe00585fc8.tar.gz"; + hash = "sha256-mP/I2coL57UJm/3+4Q8sPAgQwk8V4zM+S4VBBTrX2To="; + }; + } + { + name = "122004bfd4c519dadfb8e6281a42fc34fd1aa15aea654ea8a492839046f9894fa2cf"; + path = fetchZigArtifact { + name = "vulkan_headers"; + url = "https://github.com/mitchellh/vulkan-headers/archive/04c8a0389d5a0236a96312988017cd4ce27d8041.tar.gz"; + hash = "sha256-K+zrRudgHFukOM6En1StRYRMNYkeRk+qHTXvrXaG+FU="; + }; + } + { + name = "1220b3164434d2ec9db146a40bf3a30f490590d68fa8529776a3138074f0da2c11ca"; + path = fetchZigArtifact { + name = "wayland_headers"; + url = "https://github.com/mitchellh/wayland-headers/archive/5f991515a29f994d87b908115a2ab0b899474bd1.tar.gz"; + hash = "sha256-uFilLZinKkZt6RdVTV3lUmJpzpswDdFva22FvwU/XQI="; + }; + } + { + name = "122089c326186c84aa2fd034b16abc38f3ebf4862d9ae106dc1847ac44f557b36465"; + path = fetchZigArtifact { + name = "x11_headers"; + url = "https://github.com/mitchellh/x11-headers/archive/2ffbd62d82ff73ec929dd8de802bc95effa0ef88.tar.gz"; + hash = "sha256-EhV2bmTY/OMYN1wEul35gD0hQgS/Al262jO3pVr0O+c="; + }; + } + { + name = "1220f5aec880d4f430cc1597ede88f1530da69e39a4986080e976b0c7b919c2ebfeb"; + path = fetchZigArtifact { + name = "vaxis"; + url = "git+https://github.com/rockorager/libvaxis#2237a7059eae99e9f132dd5acd1555e49d6c7d93"; + hash = "sha256-ZzLNJOsXzyBhkdUhbET30RoU2T9xKYsBUQz2NAjK/G8="; + }; + } + { + name = "1220dd654ef941fc76fd96f9ec6adadf83f69b9887a0d3f4ee5ac0a1a3e11be35cf5"; + path = fetchZigArtifact { + name = "zigimg"; + url = "git+https://github.com/zigimg/zigimg#3a667bdb3d7f0955a5a51c8468eac83210c1439e"; + hash = "sha256-oLf3YH3yeg4ikVO/GahMCDRMTU31AHkfSnF4rt7xTKo="; + }; + } + { + name = "122055beff332830a391e9895c044d33b15ea21063779557024b46169fb1984c6e40"; + path = fetchZigArtifact { + name = "zg"; + url = "https://codeberg.org/atman/zg/archive/v0.13.2.tar.gz"; + hash = "sha256-2x9hT7bYq9KJYWLVOf21a+QvTG/F7HWT+YK15IMRzNY="; + }; + } + { + name = "12201f0d542e7541cf492a001d4d0d0155c92f58212fbcb0d224e95edeba06b5416a"; + path = fetchZigArtifact { + name = "z2d"; + url = "https://deps.files.ghostty.org/z2d-12201f0d542e7541cf492a001d4d0d0155c92f58212fbcb0d224e95edeba06b5416a.tar.gz"; + hash = "sha256-P0UJ54RO/vVyDa+UkBl+QEOjzoMMEFSOTexQP/uBXfc="; + }; + } + { + name = "1220e17e64ef0ef561b3e4b9f3a96a2494285f2ec31c097721bf8c8677ec4415c634"; + path = fetchZigArtifact { + name = "zig_objc"; + url = "https://deps.files.ghostty.org/zig_objc-1220e17e64ef0ef561b3e4b9f3a96a2494285f2ec31c097721bf8c8677ec4415c634.tar.gz"; + hash = "sha256-H+HIbh2T23uzrsg9/1/vl9Ir1HCAa2pzeTx6zktJH9Q="; + }; + } + { + name = "12205a66d423259567764fa0fc60c82be35365c21aeb76c5a7dc99698401f4f6fefc"; + path = fetchZigArtifact { + name = "zig_js"; + url = "https://deps.files.ghostty.org/zig_js-12205a66d423259567764fa0fc60c82be35365c21aeb76c5a7dc99698401f4f6fefc.tar.gz"; + hash = "sha256-fyNeCVbC9UAaKJY6JhAZlT0A479M/AKYMPIWEZbDWD0="; + }; + } + { + name = "12207831bce7d4abce57b5a98e8f3635811cfefd160bca022eb91fe905d36a02cf25"; + path = fetchZigArtifact { + name = "ziglyph"; + url = "https://deps.files.ghostty.org/ziglyph-b89d43d1e3fb01b6074bc1f7fc980324b04d26a5.tar.gz"; + hash = "sha256-cse98+Ft8QUjX+P88yyYfaxJOJGQ9M7Ymw7jFxDz89k="; + }; + } + { + name = "12209ca054cb1919fa276e328967f10b253f7537c4136eb48f3332b0f7cf661cad38"; + path = fetchZigArtifact { + name = "zig_wayland"; + url = "https://deps.files.ghostty.org/zig-wayland-fbfe3b4ac0b472a27b1f1a67405436c58cbee12d.tar.gz"; + hash = "sha256-RtAystqK/GRYIquTK1KfD7rRSCrfuzAvCD1Z9DE1ldc="; + }; + } + { + name = "1220edc3b8d8bedbb50555947987e5e8e2f93871ca3c8e8d4cc8f1377c15b5dd35e8"; + path = fetchZigArtifact { + name = "zf"; + url = "https://deps.files.ghostty.org/zf-1220edc3b8d8bedbb50555947987e5e8e2f93871ca3c8e8d4cc8f1377c15b5dd35e8.tar.gz"; + hash = "sha256-/oLryY3VQfjbtQi+UP+n6FJTVA/YxIetjO+6Ovrh6/E="; + }; + } + { + name = "1220c72c1697dd9008461ead702997a15d8a1c5810247f02e7983b9f74c6c6e4c087"; + path = fetchZigArtifact { + name = "vaxis"; + url = "git+https://github.com/rockorager/libvaxis/?ref=main#dc0a228a5544988d4a920cfb40be9cd28db41423"; + hash = "sha256-QWN4jOrA91KlbqmeEHHJ4HTnCC9nmfxt8DHUXJpAzLI="; + }; + } + { + name = "12208d70ee791d7ef7e16e1c3c9c1127b57f1ed066a24f87d57fc9f730c5dc394b9d"; + path = fetchZigArtifact { + name = "gobject"; + url = "https://deps.files.ghostty.org/gobject-12208d70ee791d7ef7e16e1c3c9c1127b57f1ed066a24f87d57fc9f730c5dc394b9d.tar.zst"; + hash = "sha256-UU97kNv/bZzQPKz1djhEDLapLguvfBpFfWVb6FthtcI="; + }; + } + { + name = "12202cdac858abc52413a6c6711d5026d2d3c8e13f95ca2c327eade0736298bb021f"; + path = fetchZigArtifact { + name = "wayland"; + url = "https://deps.files.ghostty.org/wayland-9cb3d7aa9dc995ffafdbdef7ab86a949d0fb0e7d.tar.gz"; + hash = "sha256-6kGR1o5DdnflHzqs3ieCmBAUTpMdOXoyfcYDXiw5xQ0="; + }; + } + { + name = "12201a57c6ce0001aa034fa80fba3e1cd2253c560a45748f4f4dd21ff23b491cddef"; + path = fetchZigArtifact { + name = "wayland_protocols"; + url = "https://deps.files.ghostty.org/wayland-protocols-258d8f88f2c8c25a830c6316f87d23ce1a0f12d9.tar.gz"; + hash = "sha256-XO3K3egbdeYPI+XoO13SuOtO+5+Peb16NH0UiusFMPg="; + }; + } + { + name = "12207e0851c12acdeee0991e893e0132fc87bb763969a585dc16ecca33e88334c566"; + path = fetchZigArtifact { + name = "plasma_wayland_protocols"; + url = "https://deps.files.ghostty.org/plasma_wayland_protocols-12207e0851c12acdeee0991e893e0132fc87bb763969a585dc16ecca33e88334c566.tar.gz"; + hash = "sha256-XFi6IUrNjmvKNCbcCLAixGqN2Zeymhs+KLrfccIN9EE="; + }; + } + { + name = "1220a1dbe41bc69aacf75026a7158812198ea265fb9cac64dcb91cd31f3b1b8c1f92"; + path = fetchZigArtifact { + name = "iterm2_themes"; + url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/efb1bb1843500a751eb30afa58fe48a6bec8952c.tar.gz"; + hash = "sha256-ZdVc1mmLwF45PZiqL/j/l7MO2O6hZ11lqIToGFdHiEU="; + }; + } + { + name = "1220bc6b9daceaf7c8c60f3c3998058045ba0c5c5f48ae255ff97776d9cd8bfc6402"; + path = fetchZigArtifact { + name = "imgui"; + url = "https://deps.files.ghostty.org/imgui-1220bc6b9daceaf7c8c60f3c3998058045ba0c5c5f48ae255ff97776d9cd8bfc6402.tar.gz"; + hash = "sha256-oF/QHgTPEat4Hig4fGIdLkIPHmBEyOJ6JeYD6pnveGA="; + }; + } + { + name = "1220b81f6ecfb3fd222f76cf9106fecfa6554ab07ec7fdc4124b9bb063ae2adf969d"; + path = fetchZigArtifact { + name = "freetype"; + url = "https://deps.files.ghostty.org/freetype-1220b81f6ecfb3fd222f76cf9106fecfa6554ab07ec7fdc4124b9bb063ae2adf969d.tar.gz"; + hash = "sha256-QnIB9dUVFnDQXB9bRb713aHy592XHvVPD+qqf/0quQw="; + }; + } + { + name = "1220aa013f0c83da3fb64ea6d327f9173fa008d10e28bc9349eac3463457723b1c66"; + path = fetchZigArtifact { + name = "libpng"; + url = "https://deps.files.ghostty.org/libpng-1220aa013f0c83da3fb64ea6d327f9173fa008d10e28bc9349eac3463457723b1c66.tar.gz"; + hash = "sha256-/syVtGzwXo4/yKQUdQ4LparQDYnp/fF16U/wQcrxoDo="; + }; + } + { + name = "1220fed0c74e1019b3ee29edae2051788b080cd96e90d56836eea857b0b966742efb"; + path = fetchZigArtifact { + name = "zlib"; + url = "https://deps.files.ghostty.org/zlib-1220fed0c74e1019b3ee29edae2051788b080cd96e90d56836eea857b0b966742efb.tar.gz"; + hash = "sha256-F+iIY/NgBnKrSRgvIXKBtvxNPHYr3jYZNeQ2qVIU0Fw="; + }; + } + { + name = "12201149afb3326c56c05bb0a577f54f76ac20deece63aa2f5cd6ff31a4fa4fcb3b7"; + path = fetchZigArtifact { + name = "fontconfig"; + url = "https://deps.files.ghostty.org/fontconfig-2.14.2.tar.gz"; + hash = "sha256-O6LdkhWHGKzsXKrxpxYEO1qgVcJ7CB2RSvPMtA3OilU="; + }; + } + { + name = "122032442d95c3b428ae8e526017fad881e7dc78eab4d558e9a58a80bfbd65a64f7d"; + path = fetchZigArtifact { + name = "libxml2"; + url = "https://github.com/GNOME/libxml2/archive/refs/tags/v2.11.5.tar.gz"; + hash = "sha256-bCgFni4+60K1tLFkieORamNGwQladP7jvGXNxdiaYhU="; + }; + } + { + name = "1220b8588f106c996af10249bfa092c6fb2f35fbacb1505ef477a0b04a7dd1063122"; + path = fetchZigArtifact { + name = "harfbuzz"; + url = "https://deps.files.ghostty.org/harfbuzz-1220b8588f106c996af10249bfa092c6fb2f35fbacb1505ef477a0b04a7dd1063122.tar.gz"; + hash = "sha256-nxygiYE7BZRK0c6MfgGCEwJtNdybq0gKIeuHaDg5ZVY="; + }; + } + { + name = "12205c83b8311a24b1d5ae6d21640df04f4b0726e314337c043cde1432758cbe165b"; + path = fetchZigArtifact { + name = "highway"; + url = "https://deps.files.ghostty.org/highway-12205c83b8311a24b1d5ae6d21640df04f4b0726e314337c043cde1432758cbe165b.tar.gz"; + hash = "sha256-NUqLRTm1iOcLmOxwhEJz4/J0EwLEw3e8xOgbPRhm98k="; + }; + } + { + name = "1220c15e72eadd0d9085a8af134904d9a0f5dfcbed5f606ad60edc60ebeccd9706bb"; + path = fetchZigArtifact { + name = "oniguruma"; + url = "https://deps.files.ghostty.org/oniguruma-1220c15e72eadd0d9085a8af134904d9a0f5dfcbed5f606ad60edc60ebeccd9706bb.tar.gz"; + hash = "sha256-ABqhIC54RI9MC/GkjHblVodrNvFtks4yB+zP1h2Z8qA="; + }; + } + { + name = "1220446be831adcca918167647c06c7b825849fa3fba5f22da394667974537a9c77e"; + path = fetchZigArtifact { + name = "sentry"; + url = "https://deps.files.ghostty.org/sentry-1220446be831adcca918167647c06c7b825849fa3fba5f22da394667974537a9c77e.tar.gz"; + hash = "sha256-KsZJfMjWGo0xCT5HrduMmyxFsWsHBbszSoNbZCPDGN8="; + }; + } + { + name = "12207fd37bb8251919c112dcdd8f616a491857b34a451f7e4486490077206dc2a1ea"; + path = fetchZigArtifact { + name = "breakpad"; + url = "https://github.com/getsentry/breakpad/archive/b99f444ba5f6b98cac261cbb391d8766b34a5918.tar.gz"; + hash = "sha256-bMqYlD0amQdmzvYQd8Ca/1k4Bj/heh7+EijlQSttatk="; + }; + } + { + name = "1220d4d18426ca72fc2b7e56ce47273149815501d0d2395c2a98c726b31ba931e641"; + path = fetchZigArtifact { + name = "utfcpp"; + url = "https://deps.files.ghostty.org/utfcpp-1220d4d18426ca72fc2b7e56ce47273149815501d0d2395c2a98c726b31ba931e641.tar.gz"; + hash = "sha256-/8ZooxDndgfTk/PBizJxXyI9oerExNbgV5oR345rWc8="; + }; + } + { + name = "122037b39d577ec2db3fd7b2130e7b69ef6cc1807d68607a7c232c958315d381b5cd"; + path = fetchZigArtifact { + name = "wuffs"; + url = "https://deps.files.ghostty.org/wuffs-122037b39d577ec2db3fd7b2130e7b69ef6cc1807d68607a7c232c958315d381b5cd.tar.gz"; + hash = "sha256-nkzSCr6W5sTG7enDBXEIhgEm574uLD41UVR2wlC+HBM="; + }; + } + { + name = "12207ff340169c7d40c570b4b6a97db614fe47e0d83b5801a932dcd44917424c8806"; + path = fetchZigArtifact { + name = "pixels"; + url = "https://deps.files.ghostty.org/pixels-12207ff340169c7d40c570b4b6a97db614fe47e0d83b5801a932dcd44917424c8806.tar.gz"; + hash = "sha256-Veg7FtCRCCUCvxSb9FfzH0IJLFmCZQ4/+657SIcb8Ro="; + }; + } + { + name = "12201278a1a05c0ce0b6eb6026c65cd3e9247aa041b1c260324bf29cee559dd23ba1"; + path = fetchZigArtifact { + name = "glslang"; + url = "https://deps.files.ghostty.org/glslang-12201278a1a05c0ce0b6eb6026c65cd3e9247aa041b1c260324bf29cee559dd23ba1.tar.gz"; + hash = "sha256-FKLtu1Ccs+UamlPj9eQ12/WXFgS0uDPmPmB26MCpl7U="; + }; + } + { + name = "1220fb3b5586e8be67bc3feb34cbe749cf42a60d628d2953632c2f8141302748c8da"; + path = fetchZigArtifact { + name = "spirv_cross"; + url = "https://deps.files.ghostty.org/spirv_cross-1220fb3b5586e8be67bc3feb34cbe749cf42a60d628d2953632c2f8141302748c8da.tar.gz"; + hash = "sha256-tStvz8Ref6abHwahNiwVVHNETizAmZVVaxVsU7pmV+M="; + }; + } + ] diff --git a/build.zig.zon.txt b/build.zig.zon.txt new file mode 100644 index 0000000000..297226f2f1 --- /dev/null +++ b/build.zig.zon.txt @@ -0,0 +1,38 @@ +git+https://github.com/rockorager/libvaxis#2237a7059eae99e9f132dd5acd1555e49d6c7d93 +git+https://github.com/rockorager/libvaxis/?ref=main#dc0a228a5544988d4a920cfb40be9cd28db41423 +git+https://github.com/zigimg/zigimg#3a667bdb3d7f0955a5a51c8468eac83210c1439e +https://codeberg.org/atman/zg/archive/v0.13.2.tar.gz +https://deps.files.ghostty.org/fontconfig-2.14.2.tar.gz +https://deps.files.ghostty.org/freetype-1220b81f6ecfb3fd222f76cf9106fecfa6554ab07ec7fdc4124b9bb063ae2adf969d.tar.gz +https://deps.files.ghostty.org/glslang-12201278a1a05c0ce0b6eb6026c65cd3e9247aa041b1c260324bf29cee559dd23ba1.tar.gz +https://deps.files.ghostty.org/gobject-12208d70ee791d7ef7e16e1c3c9c1127b57f1ed066a24f87d57fc9f730c5dc394b9d.tar.zst +https://deps.files.ghostty.org/harfbuzz-1220b8588f106c996af10249bfa092c6fb2f35fbacb1505ef477a0b04a7dd1063122.tar.gz +https://deps.files.ghostty.org/highway-12205c83b8311a24b1d5ae6d21640df04f4b0726e314337c043cde1432758cbe165b.tar.gz +https://deps.files.ghostty.org/imgui-1220bc6b9daceaf7c8c60f3c3998058045ba0c5c5f48ae255ff97776d9cd8bfc6402.tar.gz +https://deps.files.ghostty.org/libpng-1220aa013f0c83da3fb64ea6d327f9173fa008d10e28bc9349eac3463457723b1c66.tar.gz +https://deps.files.ghostty.org/libxev-1220ebf88622c4d502dc59e71347e4d28c47e033f11b59aff774ae5787565c40999c.tar.gz +https://deps.files.ghostty.org/mach_glfw-12206ed982e709e565d536ce930701a8c07edfd2cfdce428683f3f2a601d37696a62.tar.gz +https://deps.files.ghostty.org/oniguruma-1220c15e72eadd0d9085a8af134904d9a0f5dfcbed5f606ad60edc60ebeccd9706bb.tar.gz +https://deps.files.ghostty.org/pixels-12207ff340169c7d40c570b4b6a97db614fe47e0d83b5801a932dcd44917424c8806.tar.gz +https://deps.files.ghostty.org/plasma_wayland_protocols-12207e0851c12acdeee0991e893e0132fc87bb763969a585dc16ecca33e88334c566.tar.gz +https://deps.files.ghostty.org/sentry-1220446be831adcca918167647c06c7b825849fa3fba5f22da394667974537a9c77e.tar.gz +https://deps.files.ghostty.org/spirv_cross-1220fb3b5586e8be67bc3feb34cbe749cf42a60d628d2953632c2f8141302748c8da.tar.gz +https://deps.files.ghostty.org/utfcpp-1220d4d18426ca72fc2b7e56ce47273149815501d0d2395c2a98c726b31ba931e641.tar.gz +https://deps.files.ghostty.org/wayland-9cb3d7aa9dc995ffafdbdef7ab86a949d0fb0e7d.tar.gz +https://deps.files.ghostty.org/wayland-protocols-258d8f88f2c8c25a830c6316f87d23ce1a0f12d9.tar.gz +https://deps.files.ghostty.org/wuffs-122037b39d577ec2db3fd7b2130e7b69ef6cc1807d68607a7c232c958315d381b5cd.tar.gz +https://deps.files.ghostty.org/z2d-12201f0d542e7541cf492a001d4d0d0155c92f58212fbcb0d224e95edeba06b5416a.tar.gz +https://deps.files.ghostty.org/zf-1220edc3b8d8bedbb50555947987e5e8e2f93871ca3c8e8d4cc8f1377c15b5dd35e8.tar.gz +https://deps.files.ghostty.org/zig-wayland-fbfe3b4ac0b472a27b1f1a67405436c58cbee12d.tar.gz +https://deps.files.ghostty.org/zig_js-12205a66d423259567764fa0fc60c82be35365c21aeb76c5a7dc99698401f4f6fefc.tar.gz +https://deps.files.ghostty.org/zig_objc-1220e17e64ef0ef561b3e4b9f3a96a2494285f2ec31c097721bf8c8677ec4415c634.tar.gz +https://deps.files.ghostty.org/ziglyph-b89d43d1e3fb01b6074bc1f7fc980324b04d26a5.tar.gz +https://deps.files.ghostty.org/zlib-1220fed0c74e1019b3ee29edae2051788b080cd96e90d56836eea857b0b966742efb.tar.gz +https://github.com/GNOME/libxml2/archive/refs/tags/v2.11.5.tar.gz +https://github.com/getsentry/breakpad/archive/b99f444ba5f6b98cac261cbb391d8766b34a5918.tar.gz +https://github.com/mbadolato/iTerm2-Color-Schemes/archive/db227d159adc265818f2e898da0f70ef8d7b580e.tar.gz +https://github.com/mitchellh/glfw/archive/b552c6ec47326b94015feddb36058ea567b87159.tar.gz +https://github.com/mitchellh/vulkan-headers/archive/04c8a0389d5a0236a96312988017cd4ce27d8041.tar.gz +https://github.com/mitchellh/wayland-headers/archive/5f991515a29f994d87b908115a2ab0b899474bd1.tar.gz +https://github.com/mitchellh/x11-headers/archive/2ffbd62d82ff73ec929dd8de802bc95effa0ef88.tar.gz +https://github.com/mitchellh/xcode-frameworks/archive/69801c154c39d7ae6129ea1ba8fe1afe00585fc8.tar.gz diff --git a/build.zig.zon2json-lock b/build.zig.zon2json-lock new file mode 100644 index 0000000000..26ccc690a3 --- /dev/null +++ b/build.zig.zon2json-lock @@ -0,0 +1,192 @@ +{ + "1220ebf88622c4d502dc59e71347e4d28c47e033f11b59aff774ae5787565c40999c": { + "name": "libxev", + "url": "https://deps.files.ghostty.org/libxev-1220ebf88622c4d502dc59e71347e4d28c47e033f11b59aff774ae5787565c40999c.tar.gz", + "hash": "sha256-VHP90NTytIZ8UZsYRKOOxN490/I6yv6ec40sP8y5MJ8=" + }, + "12206ed982e709e565d536ce930701a8c07edfd2cfdce428683f3f2a601d37696a62": { + "name": "mach_glfw", + "url": "https://deps.files.ghostty.org/mach_glfw-12206ed982e709e565d536ce930701a8c07edfd2cfdce428683f3f2a601d37696a62.tar.gz", + "hash": "sha256-HhXIvWUS8/CHWY4VXPG2ZEo+we8XOn3o5rYJCQ1n8Nk=" + }, + "1220736fa4ba211162c7a0e46cc8fe04d95921927688bff64ab5da7420d098a7272d": { + "name": "glfw", + "url": "https://github.com/mitchellh/glfw/archive/b552c6ec47326b94015feddb36058ea567b87159.tar.gz", + "hash": "sha256-IeBVAOQmtyFqVxzuXPek1onuPwIamcOyYtxqKpPEQjU=" + }, + "12202adbfecdad671d585c9a5bfcbd5cdf821726779430047742ce1bf94ad67d19cb": { + "name": "xcode_frameworks", + "url": "https://github.com/mitchellh/xcode-frameworks/archive/69801c154c39d7ae6129ea1ba8fe1afe00585fc8.tar.gz", + "hash": "sha256-mP/I2coL57UJm/3+4Q8sPAgQwk8V4zM+S4VBBTrX2To=" + }, + "122004bfd4c519dadfb8e6281a42fc34fd1aa15aea654ea8a492839046f9894fa2cf": { + "name": "vulkan_headers", + "url": "https://github.com/mitchellh/vulkan-headers/archive/04c8a0389d5a0236a96312988017cd4ce27d8041.tar.gz", + "hash": "sha256-K+zrRudgHFukOM6En1StRYRMNYkeRk+qHTXvrXaG+FU=" + }, + "1220b3164434d2ec9db146a40bf3a30f490590d68fa8529776a3138074f0da2c11ca": { + "name": "wayland_headers", + "url": "https://github.com/mitchellh/wayland-headers/archive/5f991515a29f994d87b908115a2ab0b899474bd1.tar.gz", + "hash": "sha256-uFilLZinKkZt6RdVTV3lUmJpzpswDdFva22FvwU/XQI=" + }, + "122089c326186c84aa2fd034b16abc38f3ebf4862d9ae106dc1847ac44f557b36465": { + "name": "x11_headers", + "url": "https://github.com/mitchellh/x11-headers/archive/2ffbd62d82ff73ec929dd8de802bc95effa0ef88.tar.gz", + "hash": "sha256-EhV2bmTY/OMYN1wEul35gD0hQgS/Al262jO3pVr0O+c=" + }, + "1220f5aec880d4f430cc1597ede88f1530da69e39a4986080e976b0c7b919c2ebfeb": { + "name": "vaxis", + "url": "git+https://github.com/rockorager/libvaxis#2237a7059eae99e9f132dd5acd1555e49d6c7d93", + "hash": "sha256-ZzLNJOsXzyBhkdUhbET30RoU2T9xKYsBUQz2NAjK/G8=" + }, + "1220dd654ef941fc76fd96f9ec6adadf83f69b9887a0d3f4ee5ac0a1a3e11be35cf5": { + "name": "zigimg", + "url": "git+https://github.com/zigimg/zigimg#3a667bdb3d7f0955a5a51c8468eac83210c1439e", + "hash": "sha256-oLf3YH3yeg4ikVO/GahMCDRMTU31AHkfSnF4rt7xTKo=" + }, + "122055beff332830a391e9895c044d33b15ea21063779557024b46169fb1984c6e40": { + "name": "zg", + "url": "https://codeberg.org/atman/zg/archive/v0.13.2.tar.gz", + "hash": "sha256-2x9hT7bYq9KJYWLVOf21a+QvTG/F7HWT+YK15IMRzNY=" + }, + "12201f0d542e7541cf492a001d4d0d0155c92f58212fbcb0d224e95edeba06b5416a": { + "name": "z2d", + "url": "https://deps.files.ghostty.org/z2d-12201f0d542e7541cf492a001d4d0d0155c92f58212fbcb0d224e95edeba06b5416a.tar.gz", + "hash": "sha256-P0UJ54RO/vVyDa+UkBl+QEOjzoMMEFSOTexQP/uBXfc=" + }, + "1220e17e64ef0ef561b3e4b9f3a96a2494285f2ec31c097721bf8c8677ec4415c634": { + "name": "zig_objc", + "url": "https://deps.files.ghostty.org/zig_objc-1220e17e64ef0ef561b3e4b9f3a96a2494285f2ec31c097721bf8c8677ec4415c634.tar.gz", + "hash": "sha256-H+HIbh2T23uzrsg9/1/vl9Ir1HCAa2pzeTx6zktJH9Q=" + }, + "12205a66d423259567764fa0fc60c82be35365c21aeb76c5a7dc99698401f4f6fefc": { + "name": "zig_js", + "url": "https://deps.files.ghostty.org/zig_js-12205a66d423259567764fa0fc60c82be35365c21aeb76c5a7dc99698401f4f6fefc.tar.gz", + "hash": "sha256-fyNeCVbC9UAaKJY6JhAZlT0A479M/AKYMPIWEZbDWD0=" + }, + "12207831bce7d4abce57b5a98e8f3635811cfefd160bca022eb91fe905d36a02cf25": { + "name": "ziglyph", + "url": "https://deps.files.ghostty.org/ziglyph-b89d43d1e3fb01b6074bc1f7fc980324b04d26a5.tar.gz", + "hash": "sha256-cse98+Ft8QUjX+P88yyYfaxJOJGQ9M7Ymw7jFxDz89k=" + }, + "12209ca054cb1919fa276e328967f10b253f7537c4136eb48f3332b0f7cf661cad38": { + "name": "zig_wayland", + "url": "https://deps.files.ghostty.org/zig-wayland-fbfe3b4ac0b472a27b1f1a67405436c58cbee12d.tar.gz", + "hash": "sha256-RtAystqK/GRYIquTK1KfD7rRSCrfuzAvCD1Z9DE1ldc=" + }, + "1220edc3b8d8bedbb50555947987e5e8e2f93871ca3c8e8d4cc8f1377c15b5dd35e8": { + "name": "zf", + "url": "https://deps.files.ghostty.org/zf-1220edc3b8d8bedbb50555947987e5e8e2f93871ca3c8e8d4cc8f1377c15b5dd35e8.tar.gz", + "hash": "sha256-/oLryY3VQfjbtQi+UP+n6FJTVA/YxIetjO+6Ovrh6/E=" + }, + "1220c72c1697dd9008461ead702997a15d8a1c5810247f02e7983b9f74c6c6e4c087": { + "name": "vaxis", + "url": "git+https://github.com/rockorager/libvaxis/?ref=main#dc0a228a5544988d4a920cfb40be9cd28db41423", + "hash": "sha256-QWN4jOrA91KlbqmeEHHJ4HTnCC9nmfxt8DHUXJpAzLI=" + }, + "12208d70ee791d7ef7e16e1c3c9c1127b57f1ed066a24f87d57fc9f730c5dc394b9d": { + "name": "gobject", + "url": "https://deps.files.ghostty.org/gobject-12208d70ee791d7ef7e16e1c3c9c1127b57f1ed066a24f87d57fc9f730c5dc394b9d.tar.zst", + "hash": "sha256-UU97kNv/bZzQPKz1djhEDLapLguvfBpFfWVb6FthtcI=" + }, + "12202cdac858abc52413a6c6711d5026d2d3c8e13f95ca2c327eade0736298bb021f": { + "name": "wayland", + "url": "https://deps.files.ghostty.org/wayland-9cb3d7aa9dc995ffafdbdef7ab86a949d0fb0e7d.tar.gz", + "hash": "sha256-6kGR1o5DdnflHzqs3ieCmBAUTpMdOXoyfcYDXiw5xQ0=" + }, + "12201a57c6ce0001aa034fa80fba3e1cd2253c560a45748f4f4dd21ff23b491cddef": { + "name": "wayland_protocols", + "url": "https://deps.files.ghostty.org/wayland-protocols-258d8f88f2c8c25a830c6316f87d23ce1a0f12d9.tar.gz", + "hash": "sha256-XO3K3egbdeYPI+XoO13SuOtO+5+Peb16NH0UiusFMPg=" + }, + "12207e0851c12acdeee0991e893e0132fc87bb763969a585dc16ecca33e88334c566": { + "name": "plasma_wayland_protocols", + "url": "https://deps.files.ghostty.org/plasma_wayland_protocols-12207e0851c12acdeee0991e893e0132fc87bb763969a585dc16ecca33e88334c566.tar.gz", + "hash": "sha256-XFi6IUrNjmvKNCbcCLAixGqN2Zeymhs+KLrfccIN9EE=" + }, + "12203d2647e5daf36a9c85b969e03f422540786ce9ea624eb4c26d204fe1f46218f3": { + "name": "iterm2_themes", + "url": "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/db227d159adc265818f2e898da0f70ef8d7b580e.tar.gz", + "hash": "sha256-Iyf7U4rpvNkPX4AOEbYSYGte5+SjRwsWD2luOn1Hz8U=" + }, + "1220bc6b9daceaf7c8c60f3c3998058045ba0c5c5f48ae255ff97776d9cd8bfc6402": { + "name": "imgui", + "url": "https://deps.files.ghostty.org/imgui-1220bc6b9daceaf7c8c60f3c3998058045ba0c5c5f48ae255ff97776d9cd8bfc6402.tar.gz", + "hash": "sha256-oF/QHgTPEat4Hig4fGIdLkIPHmBEyOJ6JeYD6pnveGA=" + }, + "1220b81f6ecfb3fd222f76cf9106fecfa6554ab07ec7fdc4124b9bb063ae2adf969d": { + "name": "freetype", + "url": "https://deps.files.ghostty.org/freetype-1220b81f6ecfb3fd222f76cf9106fecfa6554ab07ec7fdc4124b9bb063ae2adf969d.tar.gz", + "hash": "sha256-QnIB9dUVFnDQXB9bRb713aHy592XHvVPD+qqf/0quQw=" + }, + "1220aa013f0c83da3fb64ea6d327f9173fa008d10e28bc9349eac3463457723b1c66": { + "name": "libpng", + "url": "https://deps.files.ghostty.org/libpng-1220aa013f0c83da3fb64ea6d327f9173fa008d10e28bc9349eac3463457723b1c66.tar.gz", + "hash": "sha256-/syVtGzwXo4/yKQUdQ4LparQDYnp/fF16U/wQcrxoDo=" + }, + "1220fed0c74e1019b3ee29edae2051788b080cd96e90d56836eea857b0b966742efb": { + "name": "zlib", + "url": "https://deps.files.ghostty.org/zlib-1220fed0c74e1019b3ee29edae2051788b080cd96e90d56836eea857b0b966742efb.tar.gz", + "hash": "sha256-F+iIY/NgBnKrSRgvIXKBtvxNPHYr3jYZNeQ2qVIU0Fw=" + }, + "12201149afb3326c56c05bb0a577f54f76ac20deece63aa2f5cd6ff31a4fa4fcb3b7": { + "name": "fontconfig", + "url": "https://deps.files.ghostty.org/fontconfig-2.14.2.tar.gz", + "hash": "sha256-O6LdkhWHGKzsXKrxpxYEO1qgVcJ7CB2RSvPMtA3OilU=" + }, + "122032442d95c3b428ae8e526017fad881e7dc78eab4d558e9a58a80bfbd65a64f7d": { + "name": "libxml2", + "url": "https://github.com/GNOME/libxml2/archive/refs/tags/v2.11.5.tar.gz", + "hash": "sha256-bCgFni4+60K1tLFkieORamNGwQladP7jvGXNxdiaYhU=" + }, + "1220b8588f106c996af10249bfa092c6fb2f35fbacb1505ef477a0b04a7dd1063122": { + "name": "harfbuzz", + "url": "https://deps.files.ghostty.org/harfbuzz-1220b8588f106c996af10249bfa092c6fb2f35fbacb1505ef477a0b04a7dd1063122.tar.gz", + "hash": "sha256-nxygiYE7BZRK0c6MfgGCEwJtNdybq0gKIeuHaDg5ZVY=" + }, + "12205c83b8311a24b1d5ae6d21640df04f4b0726e314337c043cde1432758cbe165b": { + "name": "highway", + "url": "https://deps.files.ghostty.org/highway-12205c83b8311a24b1d5ae6d21640df04f4b0726e314337c043cde1432758cbe165b.tar.gz", + "hash": "sha256-NUqLRTm1iOcLmOxwhEJz4/J0EwLEw3e8xOgbPRhm98k=" + }, + "1220c15e72eadd0d9085a8af134904d9a0f5dfcbed5f606ad60edc60ebeccd9706bb": { + "name": "oniguruma", + "url": "https://deps.files.ghostty.org/oniguruma-1220c15e72eadd0d9085a8af134904d9a0f5dfcbed5f606ad60edc60ebeccd9706bb.tar.gz", + "hash": "sha256-ABqhIC54RI9MC/GkjHblVodrNvFtks4yB+zP1h2Z8qA=" + }, + "1220446be831adcca918167647c06c7b825849fa3fba5f22da394667974537a9c77e": { + "name": "sentry", + "url": "https://deps.files.ghostty.org/sentry-1220446be831adcca918167647c06c7b825849fa3fba5f22da394667974537a9c77e.tar.gz", + "hash": "sha256-KsZJfMjWGo0xCT5HrduMmyxFsWsHBbszSoNbZCPDGN8=" + }, + "12207fd37bb8251919c112dcdd8f616a491857b34a451f7e4486490077206dc2a1ea": { + "name": "breakpad", + "url": "https://github.com/getsentry/breakpad/archive/b99f444ba5f6b98cac261cbb391d8766b34a5918.tar.gz", + "hash": "sha256-bMqYlD0amQdmzvYQd8Ca/1k4Bj/heh7+EijlQSttatk=" + }, + "1220d4d18426ca72fc2b7e56ce47273149815501d0d2395c2a98c726b31ba931e641": { + "name": "utfcpp", + "url": "https://deps.files.ghostty.org/utfcpp-1220d4d18426ca72fc2b7e56ce47273149815501d0d2395c2a98c726b31ba931e641.tar.gz", + "hash": "sha256-/8ZooxDndgfTk/PBizJxXyI9oerExNbgV5oR345rWc8=" + }, + "122037b39d577ec2db3fd7b2130e7b69ef6cc1807d68607a7c232c958315d381b5cd": { + "name": "wuffs", + "url": "https://deps.files.ghostty.org/wuffs-122037b39d577ec2db3fd7b2130e7b69ef6cc1807d68607a7c232c958315d381b5cd.tar.gz", + "hash": "sha256-nkzSCr6W5sTG7enDBXEIhgEm574uLD41UVR2wlC+HBM=" + }, + "12207ff340169c7d40c570b4b6a97db614fe47e0d83b5801a932dcd44917424c8806": { + "name": "pixels", + "url": "https://deps.files.ghostty.org/pixels-12207ff340169c7d40c570b4b6a97db614fe47e0d83b5801a932dcd44917424c8806.tar.gz", + "hash": "sha256-Veg7FtCRCCUCvxSb9FfzH0IJLFmCZQ4/+657SIcb8Ro=" + }, + "12201278a1a05c0ce0b6eb6026c65cd3e9247aa041b1c260324bf29cee559dd23ba1": { + "name": "glslang", + "url": "https://deps.files.ghostty.org/glslang-12201278a1a05c0ce0b6eb6026c65cd3e9247aa041b1c260324bf29cee559dd23ba1.tar.gz", + "hash": "sha256-FKLtu1Ccs+UamlPj9eQ12/WXFgS0uDPmPmB26MCpl7U=" + }, + "1220fb3b5586e8be67bc3feb34cbe749cf42a60d628d2953632c2f8141302748c8da": { + "name": "spirv_cross", + "url": "https://deps.files.ghostty.org/spirv_cross-1220fb3b5586e8be67bc3feb34cbe749cf42a60d628d2953632c2f8141302748c8da.tar.gz", + "hash": "sha256-tStvz8Ref6abHwahNiwVVHNETizAmZVVaxVsU7pmV+M=" + } +} diff --git a/dist/linux/app.desktop b/dist/linux/app.desktop index 6fc43d4708..6e464ea87c 100644 --- a/dist/linux/app.desktop +++ b/dist/linux/app.desktop @@ -7,6 +7,7 @@ Icon=com.mitchellh.ghostty Categories=System;TerminalEmulator; Keywords=terminal;tty;pty; StartupNotify=true +StartupWMClass=com.mitchellh.ghostty Terminal=false Actions=new-window; X-GNOME-UsesNotifications=true diff --git a/dist/linux/ghostty_dolphin.desktop b/dist/linux/ghostty_dolphin.desktop old mode 100644 new mode 100755 diff --git a/dist/linux/ghostty_nautilus.py b/dist/linux/ghostty_nautilus.py new file mode 100644 index 0000000000..42c3976428 --- /dev/null +++ b/dist/linux/ghostty_nautilus.py @@ -0,0 +1,97 @@ +# Adapted from wezterm: https://github.com/wez/wezterm/blob/main/assets/wezterm-nautilus.py +# original copyright notice: +# +# Copyright (C) 2022 Sebastian Wiesner +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +from os.path import isdir +from gi import require_version +from gi.repository import Nautilus, GObject, Gio, GLib + + +class OpenInGhosttyAction(GObject.GObject, Nautilus.MenuProvider): + def __init__(self): + super().__init__() + session = Gio.bus_get_sync(Gio.BusType.SESSION, None) + self._systemd = None + # Check if the this system runs under systemd, per sd_booted(3) + if isdir('/run/systemd/system/'): + self._systemd = Gio.DBusProxy.new_sync(session, + Gio.DBusProxyFlags.NONE, + None, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", None) + + def _open_terminal(self, path): + cmd = ['ghostty', f'--working-directory={path}', '--gtk-single-instance=false'] + child = Gio.Subprocess.new(cmd, Gio.SubprocessFlags.NONE) + if self._systemd: + # Move new terminal into a dedicated systemd scope to make systemd + # track the terminal separately; in particular this makes systemd + # keep a separate CPU and memory account for the terminal which in turn + # ensures that oomd doesn't take nautilus down if a process in + # ghostty consumes a lot of memory. + pid = int(child.get_identifier()) + props = [("PIDs", GLib.Variant('au', [pid])), + ('CollectMode', GLib.Variant('s', 'inactive-or-failed'))] + name = 'app-nautilus-com.mitchellh.ghostty-{}.scope'.format(pid) + args = GLib.Variant('(ssa(sv)a(sa(sv)))', (name, 'fail', props, [])) + self._systemd.call_sync('StartTransientUnit', args, + Gio.DBusCallFlags.NO_AUTO_START, 500, None) + + def _menu_item_activated(self, _menu, paths): + for path in paths: + self._open_terminal(path) + + def _make_item(self, name, paths): + item = Nautilus.MenuItem(name=name, label='Open in Ghostty', + icon='com.mitchellh.ghostty') + item.connect('activate', self._menu_item_activated, paths) + return item + + def _paths_to_open(self, files): + paths = [] + for file in files: + location = file.get_location() if file.is_directory() else file.get_parent_location() + path = location.get_path() + if path and path not in paths: + paths.append(path) + if 10 < len(paths): + # Let's not open anything if the user selected a lot of directories, + # to avoid accidentally spamming their desktop with dozends of + # new windows or tabs. Ten is a totally arbitrary limit :) + return [] + else: + return paths + + def get_file_items(self, *args): + # Nautilus 3.0 API passes args (window, files), 4.0 API just passes files + files = args[0] if len(args) == 1 else args[1] + paths = self._paths_to_open(files) + if paths: + return [self._make_item(name='GhosttyNautilus::open_in_ghostty', paths=paths)] + else: + return [] + + def get_background_items(self, *args): + # Nautilus 3.0 API passes args (window, file), 4.0 API just passes file + file = args[0] if len(args) == 1 else args[1] + paths = self._paths_to_open([file]) + if paths: + return [self._make_item(name='GhosttyNautilus::open_folder_in_ghostty', paths=paths)] + else: + return [] diff --git a/dist/macos/update_appcast_tag.py b/dist/macos/update_appcast_tag.py index 4ef526019d..2cb20dd5d5 100644 --- a/dist/macos/update_appcast_tag.py +++ b/dist/macos/update_appcast_tag.py @@ -21,6 +21,7 @@ now = datetime.now(timezone.utc) version = os.environ["GHOSTTY_VERSION"] +version_dash = version.replace('.', '-') build = os.environ["GHOSTTY_BUILD"] commit = os.environ["GHOSTTY_COMMIT"] commit_long = os.environ["GHOSTTY_COMMIT_LONG"] @@ -82,6 +83,8 @@ elem.text = f"{version}" elem = ET.SubElement(item, "sparkle:minimumSystemVersion") elem.text = "13.0.0" +elem = ET.SubElement(item, "sparkle:fullReleaseNotesLink") +elem.text = f"https://ghostty.org/docs/install/release-notes/{version_dash}" elem = ET.SubElement(item, "description") elem.text = f"""

Ghostty v{version}

@@ -91,8 +94,8 @@

We don't currently generate release notes for auto-updates. -You can view the complete changelog and release notes on -the Ghostty website. +You can view the complete changelog and release notes +at ghostty.org/docs/install/release-notes/{version_dash}.

""" elem = ET.SubElement(item, "enclosure") diff --git a/flake.lock b/flake.lock index bf678156b7..7905635b3b 100644 --- a/flake.lock +++ b/flake.lock @@ -3,11 +3,11 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1696426674, - "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "lastModified": 1733328505, + "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", "owner": "edolstra", "repo": "flake-compat", - "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", "type": "github" }, "original": { @@ -21,11 +21,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1705309234, - "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", "owner": "numtide", "repo": "flake-utils", - "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", "type": "github" }, "original": { @@ -36,11 +36,11 @@ }, "nixpkgs-stable": { "locked": { - "lastModified": 1733423277, - "narHash": "sha256-TxabjxEgkNbCGFRHgM/b9yZWlBj60gUOUnRT/wbVQR8=", + "lastModified": 1738255539, + "narHash": "sha256-hP2eOqhIO/OILW+3moNWO4GtdJFYCqAe9yJZgvlCoDQ=", "owner": "nixos", "repo": "nixpkgs", - "rev": "e36963a147267afc055f7cf65225958633e536bf", + "rev": "c3511a3b53b482aa7547c9d1626fd7310c1de1c5", "type": "github" }, "original": { @@ -52,11 +52,11 @@ }, "nixpkgs-unstable": { "locked": { - "lastModified": 1733229606, - "narHash": "sha256-FLYY5M0rpa5C2QAE3CKLYAM6TwbKicdRK6qNrSHlNrE=", + "lastModified": 1738136902, + "narHash": "sha256-pUvLijVGARw4u793APze3j6mU1Zwdtz7hGkGGkD87qw=", "owner": "nixos", "repo": "nixpkgs", - "rev": "566e53c2ad750c84f6d31f9ccb9d00f823165550", + "rev": "9a5db3142ce450045840cc8d832b13b8a2018e0c", "type": "github" }, "original": { @@ -69,9 +69,11 @@ "root": { "inputs": { "flake-compat": "flake-compat", + "flake-utils": "flake-utils", "nixpkgs-stable": "nixpkgs-stable", "nixpkgs-unstable": "nixpkgs-unstable", - "zig": "zig" + "zig": "zig", + "zig2nix": "zig2nix" } }, "systems": { @@ -92,17 +94,19 @@ "zig": { "inputs": { "flake-compat": [], - "flake-utils": "flake-utils", + "flake-utils": [ + "flake-utils" + ], "nixpkgs": [ "nixpkgs-stable" ] }, "locked": { - "lastModified": 1717848532, - "narHash": "sha256-d+xIUvSTreHl8pAmU1fnmkfDTGQYCn2Rb/zOwByxS2M=", + "lastModified": 1738239110, + "narHash": "sha256-Y5i9mQ++dyIQr+zEPNy+KIbc5wjPmfllBrag3cHZgcE=", "owner": "mitchellh", "repo": "zig-overlay", - "rev": "02fc5cc555fc14fda40c42d7c3250efa43812b43", + "rev": "1a8fb6f3a04724519436355564b95fce5e272504", "type": "github" }, "original": { @@ -110,6 +114,30 @@ "repo": "zig-overlay", "type": "github" } + }, + "zig2nix": { + "inputs": { + "flake-utils": [ + "flake-utils" + ], + "nixpkgs": [ + "nixpkgs-stable" + ] + }, + "locked": { + "lastModified": 1738263917, + "narHash": "sha256-j/3fwe2pEOquHabP/puljOKwAZFjIE9gXZqA91sC48M=", + "owner": "jcollie", + "repo": "zig2nix", + "rev": "c311d8e77a6ee0d995f40a6e10a89a3a4ab04f9a", + "type": "github" + }, + "original": { + "owner": "jcollie", + "ref": "c311d8e77a6ee0d995f40a6e10a89a3a4ab04f9a", + "repo": "zig2nix", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index 83d4af4144..df0eeb759e 100644 --- a/flake.nix +++ b/flake.nix @@ -8,6 +8,7 @@ # glibc versions used by our dependencies from Nix are compatible with the # system glibc that the user is building for. nixpkgs-stable.url = "github:nixos/nixpkgs/release-24.11"; + flake-utils.url = "github:numtide/flake-utils"; # Used for shell.nix flake-compat = { @@ -19,9 +20,18 @@ url = "github:mitchellh/zig-overlay"; inputs = { nixpkgs.follows = "nixpkgs-stable"; + flake-utils.follows = "flake-utils"; flake-compat.follows = ""; }; }; + + zig2nix = { + url = "github:jcollie/zig2nix?ref=c311d8e77a6ee0d995f40a6e10a89a3a4ab04f9a"; + inputs = { + nixpkgs.follows = "nixpkgs-stable"; + flake-utils.follows = "flake-utils"; + }; + }; }; outputs = { @@ -29,40 +39,86 @@ nixpkgs-unstable, nixpkgs-stable, zig, + zig2nix, ... }: - builtins.foldl' nixpkgs-stable.lib.recursiveUpdate {} (builtins.map (system: let - pkgs-stable = nixpkgs-stable.legacyPackages.${system}; - pkgs-unstable = nixpkgs-unstable.legacyPackages.${system}; - in { - devShell.${system} = pkgs-stable.callPackage ./nix/devShell.nix { - zig = zig.packages.${system}."0.13.0"; - wraptest = pkgs-stable.callPackage ./nix/wraptest.nix {}; - }; + builtins.foldl' nixpkgs-stable.lib.recursiveUpdate {} ( + builtins.map ( + system: let + pkgs-stable = nixpkgs-stable.legacyPackages.${system}; + pkgs-unstable = nixpkgs-unstable.legacyPackages.${system}; + in { + devShell.${system} = pkgs-stable.callPackage ./nix/devShell.nix { + zig = zig.packages.${system}."0.13.0"; + wraptest = pkgs-stable.callPackage ./nix/wraptest.nix {}; + zig2nix = zig2nix; + }; - packages.${system} = let - mkArgs = optimize: { - inherit optimize; + packages.${system} = let + mkArgs = optimize: { + inherit optimize; - revision = self.shortRev or self.dirtyShortRev or "dirty"; - }; - in rec { - ghostty-debug = pkgs-stable.callPackage ./nix/package.nix (mkArgs "Debug"); - ghostty-releasesafe = pkgs-stable.callPackage ./nix/package.nix (mkArgs "ReleaseSafe"); - ghostty-releasefast = pkgs-stable.callPackage ./nix/package.nix (mkArgs "ReleaseFast"); + revision = self.shortRev or self.dirtyShortRev or "dirty"; + }; + in rec { + deps = pkgs-stable.callPackage ./build.zig.zon.nix {}; + ghostty-debug = pkgs-stable.callPackage ./nix/package.nix (mkArgs "Debug"); + ghostty-releasesafe = pkgs-stable.callPackage ./nix/package.nix (mkArgs "ReleaseSafe"); + ghostty-releasefast = pkgs-stable.callPackage ./nix/package.nix (mkArgs "ReleaseFast"); - ghostty = ghostty-releasefast; - default = ghostty; - }; + ghostty = ghostty-releasefast; + default = ghostty; + }; + + formatter.${system} = pkgs-stable.alejandra; - formatter.${system} = pkgs-stable.alejandra; + apps.${system} = let + runVM = ( + module: let + vm = import ./nix/vm/create.nix { + inherit system module; + nixpkgs = nixpkgs-stable; + overlay = self.overlays.debug; + }; + program = pkgs-stable.writeShellScript "run-ghostty-vm" '' + SHARED_DIR=$(pwd) + export SHARED_DIR - # Our supported systems are the same supported systems as the Zig binaries. - }) (builtins.attrNames zig.packages)) + ${pkgs-stable.lib.getExe vm.config.system.build.vm} "$@" + ''; + in { + type = "app"; + program = "${program}"; + } + ); + in { + wayland-cinnamon = runVM ./nix/vm/wayland-cinnamon.nix; + wayland-gnome = runVM ./nix/vm/wayland-gnome.nix; + wayland-plasma6 = runVM ./nix/vm/wayland-plasma6.nix; + x11-cinnamon = runVM ./nix/vm/x11-cinnamon.nix; + x11-gnome = runVM ./nix/vm/x11-gnome.nix; + x11-plasma6 = runVM ./nix/vm/x11-plasma6.nix; + x11-xfce = runVM ./nix/vm/x11-xfce.nix; + }; + } + # Our supported systems are the same supported systems as the Zig binaries. + ) (builtins.attrNames zig.packages) + ) // { - overlays.default = final: prev: { - ghostty = self.packages.${prev.system}.default; + overlays = { + default = self.overlays.releasefast; + releasefast = final: prev: { + ghostty = self.packages.${prev.system}.ghostty-releasefast; + }; + debug = final: prev: { + ghostty = self.packages.${prev.system}.ghostty-debug; + }; }; + create-vm = import ./nix/vm/create.nix; + create-cinnamon-vm = import ./nix/vm/create-cinnamon.nix; + create-gnome-vm = import ./nix/vm/create-gnome.nix; + create-plasma6-vm = import ./nix/vm/create-plasma6.nix; + create-xfce-vm = import ./nix/vm/create-xfce.nix; }; nixConfig = { diff --git a/include/ghostty.h b/include/ghostty.h index 0e444a2fab..86de4266df 100644 --- a/include/ghostty.h +++ b/include/ghostty.h @@ -159,7 +159,7 @@ typedef enum { GHOSTTY_KEY_EQUAL, GHOSTTY_KEY_LEFT_BRACKET, // [ GHOSTTY_KEY_RIGHT_BRACKET, // ] - GHOSTTY_KEY_BACKSLASH, // / + GHOSTTY_KEY_BACKSLASH, // \ // control GHOSTTY_KEY_UP, @@ -412,6 +412,7 @@ typedef enum { GHOSTTY_FULLSCREEN_NATIVE, GHOSTTY_FULLSCREEN_NON_NATIVE, GHOSTTY_FULLSCREEN_NON_NATIVE_VISIBLE_MENU, + GHOSTTY_FULLSCREEN_NON_NATIVE_PADDED_NOTCH, } ghostty_action_fullscreen_e; // apprt.action.SecureInput @@ -562,8 +563,10 @@ typedef enum { GHOSTTY_ACTION_QUIT, GHOSTTY_ACTION_NEW_WINDOW, GHOSTTY_ACTION_NEW_TAB, + GHOSTTY_ACTION_CLOSE_TAB, GHOSTTY_ACTION_NEW_SPLIT, GHOSTTY_ACTION_CLOSE_ALL_WINDOWS, + GHOSTTY_ACTION_TOGGLE_MAXIMIZE, GHOSTTY_ACTION_TOGGLE_FULLSCREEN, GHOSTTY_ACTION_TOGGLE_TAB_OVERVIEW, GHOSTTY_ACTION_TOGGLE_WINDOW_DECORATIONS, @@ -583,6 +586,7 @@ typedef enum { GHOSTTY_ACTION_RENDER_INSPECTOR, GHOSTTY_ACTION_DESKTOP_NOTIFICATION, GHOSTTY_ACTION_SET_TITLE, + GHOSTTY_ACTION_PROMPT_TITLE, GHOSTTY_ACTION_PWD, GHOSTTY_ACTION_MOUSE_SHAPE, GHOSTTY_ACTION_MOUSE_VISIBILITY, @@ -642,7 +646,7 @@ typedef void (*ghostty_runtime_write_clipboard_cb)(void*, ghostty_clipboard_e, bool); typedef void (*ghostty_runtime_close_surface_cb)(void*, bool); -typedef void (*ghostty_runtime_action_cb)(ghostty_app_t, +typedef bool (*ghostty_runtime_action_cb)(ghostty_app_t, ghostty_target_s, ghostty_action_s); diff --git a/macos/Assets.xcassets/Alternate Icons/BlueprintImage.imageset/Contents.json b/macos/Assets.xcassets/Alternate Icons/BlueprintImage.imageset/Contents.json new file mode 100644 index 0000000000..1c1b9b47eb --- /dev/null +++ b/macos/Assets.xcassets/Alternate Icons/BlueprintImage.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "macOS-AppIcon-1024px.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macos/Assets.xcassets/Alternate Icons/BlueprintImage.imageset/macOS-AppIcon-1024px.png b/macos/Assets.xcassets/Alternate Icons/BlueprintImage.imageset/macOS-AppIcon-1024px.png new file mode 100644 index 0000000000..ffba7d94dc Binary files /dev/null and b/macos/Assets.xcassets/Alternate Icons/BlueprintImage.imageset/macOS-AppIcon-1024px.png differ diff --git a/macos/Assets.xcassets/Alternate Icons/ChalkboardImage.imageset/Contents.json b/macos/Assets.xcassets/Alternate Icons/ChalkboardImage.imageset/Contents.json new file mode 100644 index 0000000000..1c1b9b47eb --- /dev/null +++ b/macos/Assets.xcassets/Alternate Icons/ChalkboardImage.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "macOS-AppIcon-1024px.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macos/Assets.xcassets/Alternate Icons/ChalkboardImage.imageset/macOS-AppIcon-1024px.png b/macos/Assets.xcassets/Alternate Icons/ChalkboardImage.imageset/macOS-AppIcon-1024px.png new file mode 100644 index 0000000000..eeedb72031 Binary files /dev/null and b/macos/Assets.xcassets/Alternate Icons/ChalkboardImage.imageset/macOS-AppIcon-1024px.png differ diff --git a/macos/Assets.xcassets/Alternate Icons/Contents.json b/macos/Assets.xcassets/Alternate Icons/Contents.json new file mode 100644 index 0000000000..73c00596a7 --- /dev/null +++ b/macos/Assets.xcassets/Alternate Icons/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macos/Assets.xcassets/Alternate Icons/GlassImage.imageset/Contents.json b/macos/Assets.xcassets/Alternate Icons/GlassImage.imageset/Contents.json new file mode 100644 index 0000000000..1c1b9b47eb --- /dev/null +++ b/macos/Assets.xcassets/Alternate Icons/GlassImage.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "macOS-AppIcon-1024px.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macos/Assets.xcassets/Alternate Icons/GlassImage.imageset/macOS-AppIcon-1024px.png b/macos/Assets.xcassets/Alternate Icons/GlassImage.imageset/macOS-AppIcon-1024px.png new file mode 100644 index 0000000000..99d704e27b Binary files /dev/null and b/macos/Assets.xcassets/Alternate Icons/GlassImage.imageset/macOS-AppIcon-1024px.png differ diff --git a/macos/Assets.xcassets/Alternate Icons/HolographicImage.imageset/Contents.json b/macos/Assets.xcassets/Alternate Icons/HolographicImage.imageset/Contents.json new file mode 100644 index 0000000000..1c1b9b47eb --- /dev/null +++ b/macos/Assets.xcassets/Alternate Icons/HolographicImage.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "macOS-AppIcon-1024px.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macos/Assets.xcassets/Alternate Icons/HolographicImage.imageset/macOS-AppIcon-1024px.png b/macos/Assets.xcassets/Alternate Icons/HolographicImage.imageset/macOS-AppIcon-1024px.png new file mode 100644 index 0000000000..b31c9e9736 Binary files /dev/null and b/macos/Assets.xcassets/Alternate Icons/HolographicImage.imageset/macOS-AppIcon-1024px.png differ diff --git a/macos/Assets.xcassets/Alternate Icons/MicrochipImage.imageset/Contents.json b/macos/Assets.xcassets/Alternate Icons/MicrochipImage.imageset/Contents.json new file mode 100644 index 0000000000..1c1b9b47eb --- /dev/null +++ b/macos/Assets.xcassets/Alternate Icons/MicrochipImage.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "macOS-AppIcon-1024px.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macos/Assets.xcassets/Alternate Icons/MicrochipImage.imageset/macOS-AppIcon-1024px.png b/macos/Assets.xcassets/Alternate Icons/MicrochipImage.imageset/macOS-AppIcon-1024px.png new file mode 100644 index 0000000000..add488d362 Binary files /dev/null and b/macos/Assets.xcassets/Alternate Icons/MicrochipImage.imageset/macOS-AppIcon-1024px.png differ diff --git a/macos/Assets.xcassets/Alternate Icons/PaperImage.imageset/Contents.json b/macos/Assets.xcassets/Alternate Icons/PaperImage.imageset/Contents.json new file mode 100644 index 0000000000..1c1b9b47eb --- /dev/null +++ b/macos/Assets.xcassets/Alternate Icons/PaperImage.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "macOS-AppIcon-1024px.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macos/Assets.xcassets/Alternate Icons/PaperImage.imageset/macOS-AppIcon-1024px.png b/macos/Assets.xcassets/Alternate Icons/PaperImage.imageset/macOS-AppIcon-1024px.png new file mode 100644 index 0000000000..fad8dc70b6 Binary files /dev/null and b/macos/Assets.xcassets/Alternate Icons/PaperImage.imageset/macOS-AppIcon-1024px.png differ diff --git a/macos/Assets.xcassets/Alternate Icons/RetroImage.imageset/Contents.json b/macos/Assets.xcassets/Alternate Icons/RetroImage.imageset/Contents.json new file mode 100644 index 0000000000..1c1b9b47eb --- /dev/null +++ b/macos/Assets.xcassets/Alternate Icons/RetroImage.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "macOS-AppIcon-1024px.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macos/Assets.xcassets/Alternate Icons/RetroImage.imageset/macOS-AppIcon-1024px.png b/macos/Assets.xcassets/Alternate Icons/RetroImage.imageset/macOS-AppIcon-1024px.png new file mode 100644 index 0000000000..02619e860e Binary files /dev/null and b/macos/Assets.xcassets/Alternate Icons/RetroImage.imageset/macOS-AppIcon-1024px.png differ diff --git a/macos/Assets.xcassets/Alternate Icons/XrayImage.imageset/Contents.json b/macos/Assets.xcassets/Alternate Icons/XrayImage.imageset/Contents.json new file mode 100644 index 0000000000..1c1b9b47eb --- /dev/null +++ b/macos/Assets.xcassets/Alternate Icons/XrayImage.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "macOS-AppIcon-1024px.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macos/Assets.xcassets/Alternate Icons/XrayImage.imageset/macOS-AppIcon-1024px.png b/macos/Assets.xcassets/Alternate Icons/XrayImage.imageset/macOS-AppIcon-1024px.png new file mode 100644 index 0000000000..9e74a967c2 Binary files /dev/null and b/macos/Assets.xcassets/Alternate Icons/XrayImage.imageset/macOS-AppIcon-1024px.png differ diff --git a/macos/Ghostty.xcodeproj/project.pbxproj b/macos/Ghostty.xcodeproj/project.pbxproj index fded209113..0c68da5346 100644 --- a/macos/Ghostty.xcodeproj/project.pbxproj +++ b/macos/Ghostty.xcodeproj/project.pbxproj @@ -69,9 +69,13 @@ A59FB5CF2AE0DB50009128F3 /* InspectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59FB5CE2AE0DB50009128F3 /* InspectorView.swift */; }; A59FB5D12AE0DEA7009128F3 /* MetalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59FB5D02AE0DEA7009128F3 /* MetalView.swift */; }; A5A1F8852A489D6800D1E8BC /* terminfo in Resources */ = {isa = PBXBuildFile; fileRef = A5A1F8842A489D6800D1E8BC /* terminfo */; }; + A5A2A3CA2D4445E30033CF96 /* Dock.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5A2A3C92D4445E20033CF96 /* Dock.swift */; }; + A5A2A3CC2D444ABB0033CF96 /* NSApplication+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5A2A3CB2D444AB80033CF96 /* NSApplication+Extension.swift */; }; A5A6F72A2CC41B8900B232A5 /* Xcode.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5A6F7292CC41B8700B232A5 /* Xcode.swift */; }; + A5AEB1652D5BE7D000513529 /* LastWindowPosition.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5AEB1642D5BE7BF00513529 /* LastWindowPosition.swift */; }; A5B30539299BEAAB0047F10C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A5B30538299BEAAB0047F10C /* Assets.xcassets */; }; A5CA378C2D2A4DEB00931030 /* KeyboardLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CA378B2D2A4DE800931030 /* KeyboardLayout.swift */; }; + A5CA378E2D31D6C300931030 /* Weak.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CA378D2D31D6C100931030 /* Weak.swift */; }; A5CBD0562C9E65B80017A1AE /* DraggableWindowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CBD0552C9E65A50017A1AE /* DraggableWindowView.swift */; }; A5CBD0582C9F30960017A1AE /* Cursor.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CBD0572C9F30860017A1AE /* Cursor.swift */; }; A5CBD0592C9F37B10017A1AE /* Backport.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CEAFFE29C2410700646FDA /* Backport.swift */; }; @@ -102,6 +106,7 @@ C159E89D2B69A2EF00FDFE9C /* OSColor+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C159E81C2B66A06B00FDFE9C /* OSColor+Extension.swift */; }; C1F26EA72B738B9900404083 /* NSView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1F26EA62B738B9900404083 /* NSView+Extension.swift */; }; C1F26EE92B76CBFC00404083 /* VibrantLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = C1F26EE82B76CBFC00404083 /* VibrantLayer.m */; }; + CFBB5FEA2D231E5000FD62EE /* QuickTerminalSpaceBehavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFBB5FE92D231E5000FD62EE /* QuickTerminalSpaceBehavior.swift */; }; FC5218FA2D10FFCE004C93E0 /* zsh in Resources */ = {isa = PBXBuildFile; fileRef = FC5218F92D10FFC7004C93E0 /* zsh */; }; FC9ABA9C2D0F53F80020D4C8 /* bash-completion in Resources */ = {isa = PBXBuildFile; fileRef = FC9ABA9B2D0F538D0020D4C8 /* bash-completion */; }; /* End PBXBuildFile section */ @@ -161,11 +166,15 @@ A59FB5CE2AE0DB50009128F3 /* InspectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectorView.swift; sourceTree = ""; }; A59FB5D02AE0DEA7009128F3 /* MetalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetalView.swift; sourceTree = ""; }; A5A1F8842A489D6800D1E8BC /* terminfo */ = {isa = PBXFileReference; lastKnownFileType = folder; name = terminfo; path = "../zig-out/share/terminfo"; sourceTree = ""; }; + A5A2A3C92D4445E20033CF96 /* Dock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dock.swift; sourceTree = ""; }; + A5A2A3CB2D444AB80033CF96 /* NSApplication+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSApplication+Extension.swift"; sourceTree = ""; }; A5A6F7292CC41B8700B232A5 /* Xcode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Xcode.swift; sourceTree = ""; }; + A5AEB1642D5BE7BF00513529 /* LastWindowPosition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LastWindowPosition.swift; sourceTree = ""; }; A5B30531299BEAAA0047F10C /* Ghostty.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Ghostty.app; sourceTree = BUILT_PRODUCTS_DIR; }; A5B30538299BEAAB0047F10C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; A5B3053D299BEAAB0047F10C /* Ghostty.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Ghostty.entitlements; sourceTree = ""; }; A5CA378B2D2A4DE800931030 /* KeyboardLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardLayout.swift; sourceTree = ""; }; + A5CA378D2D31D6C100931030 /* Weak.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Weak.swift; sourceTree = ""; }; A5CBD0552C9E65A50017A1AE /* DraggableWindowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DraggableWindowView.swift; sourceTree = ""; }; A5CBD0572C9F30860017A1AE /* Cursor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cursor.swift; sourceTree = ""; }; A5CBD05B2CA0C5C70017A1AE /* QuickTerminal.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = QuickTerminal.xib; sourceTree = ""; }; @@ -198,6 +207,7 @@ C1F26EE72B76CBFC00404083 /* VibrantLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VibrantLayer.h; sourceTree = ""; }; C1F26EE82B76CBFC00404083 /* VibrantLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VibrantLayer.m; sourceTree = ""; }; C1F26EEA2B76CC2400404083 /* ghostty-bridging-header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ghostty-bridging-header.h"; sourceTree = ""; }; + CFBB5FE92D231E5000FD62EE /* QuickTerminalSpaceBehavior.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickTerminalSpaceBehavior.swift; sourceTree = ""; }; FC5218F92D10FFC7004C93E0 /* zsh */ = {isa = PBXFileReference; lastKnownFileType = folder; name = zsh; path = "../zig-out/share/zsh"; sourceTree = ""; }; FC9ABA9B2D0F538D0020D4C8 /* bash-completion */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "bash-completion"; path = "../zig-out/share/bash-completion"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -262,11 +272,13 @@ A534263D2A7DCBB000EBB7A2 /* Helpers */ = { isa = PBXGroup; children = ( + A5AEB1642D5BE7BF00513529 /* LastWindowPosition.swift */, A5A6F7292CC41B8700B232A5 /* Xcode.swift */, A5CEAFFE29C2410700646FDA /* Backport.swift */, A5333E1B2B5A1CE3008AEFF7 /* CrossKit.swift */, A5CBD0572C9F30860017A1AE /* Cursor.swift */, A5D0AF3C2B37804400D21823 /* CodableBridge.swift */, + A5A2A3C92D4445E20033CF96 /* Dock.swift */, A52FFF582CAA4FF1000C6A5B /* Fullscreen.swift */, A59630962AEE163600D64628 /* HostingWindow.swift */, A5CA378B2D2A4DE800931030 /* KeyboardLayout.swift */, @@ -274,12 +286,14 @@ A5CBD0552C9E65A50017A1AE /* DraggableWindowView.swift */, C159E81C2B66A06B00FDFE9C /* OSColor+Extension.swift */, A599CDAF2CF103F20049FA26 /* NSAppearance+Extension.swift */, + A5A2A3CB2D444AB80033CF96 /* NSApplication+Extension.swift */, A54B0CEA2D0CFB4A00CBEFF8 /* NSImage+Extension.swift */, A52FFF5C2CAB4D05000C6A5B /* NSScreen+Extension.swift */, C1F26EA62B738B9900404083 /* NSView+Extension.swift */, AEE8B3442B9AA39600260C5E /* NSPasteboard+Extension.swift */, A5985CD62C320C4500C57AD3 /* String+Extension.swift */, A5CC36142C9CDA03004D6760 /* View+Extension.swift */, + A5CA378D2D31D6C100931030 /* Weak.swift */, C1F26EE72B76CBFC00404083 /* VibrantLayer.h */, C1F26EE82B76CBFC00404083 /* VibrantLayer.m */, A5CEAFDA29B8005900646FDA /* SplitView */, @@ -448,6 +462,7 @@ children = ( A5CBD05B2CA0C5C70017A1AE /* QuickTerminal.xib */, A5CBD05D2CA0C5E70017A1AE /* QuickTerminalController.swift */, + CFBB5FE92D231E5000FD62EE /* QuickTerminalSpaceBehavior.swift */, A5CBD0632CA122E70017A1AE /* QuickTerminalPosition.swift */, A52FFF562CA90481000C6A5B /* QuickTerminalScreen.swift */, A5CBD05F2CA0C9080017A1AE /* QuickTerminalWindow.swift */, @@ -611,11 +626,13 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + A5AEB1652D5BE7D000513529 /* LastWindowPosition.swift in Sources */, A59630A42AF059BB00D64628 /* Ghostty.SplitNode.swift in Sources */, A514C8D62B54A16400493A16 /* Ghostty.Config.swift in Sources */, A54B0CEB2D0CFB4C00CBEFF8 /* NSImage+Extension.swift in Sources */, A54D786C2CA7978E001B19B1 /* BaseTerminalController.swift in Sources */, A59FB5CF2AE0DB50009128F3 /* InspectorView.swift in Sources */, + CFBB5FEA2D231E5000FD62EE /* QuickTerminalSpaceBehavior.swift in Sources */, A54B0CE92D0CECD100CBEFF8 /* ColorizedGhosttyIconView.swift in Sources */, A5D0AF3D2B37804400D21823 /* CodableBridge.swift in Sources */, A5D0AF3B2B36A1DE00D21823 /* TerminalRestorable.swift in Sources */, @@ -628,6 +645,7 @@ A5CBD0602CA0C90A0017A1AE /* QuickTerminalWindow.swift in Sources */, A5CBD05E2CA0C5EC0017A1AE /* QuickTerminalController.swift in Sources */, A5CF66D72D29DDB500139794 /* Ghostty.Event.swift in Sources */, + A5A2A3CA2D4445E30033CF96 /* Dock.swift in Sources */, A51BFC222B2FB6B400E92F16 /* AboutView.swift in Sources */, A5278A9B2AA05B2600CD3039 /* Ghostty.Input.swift in Sources */, A5CBD0562C9E65B80017A1AE /* DraggableWindowView.swift in Sources */, @@ -643,12 +661,14 @@ A5A6F72A2CC41B8900B232A5 /* Xcode.swift in Sources */, A52FFF5B2CAA54B1000C6A5B /* FullscreenMode+Extension.swift in Sources */, A5333E222B5A2128008AEFF7 /* SurfaceView_AppKit.swift in Sources */, + A5CA378E2D31D6C300931030 /* Weak.swift in Sources */, A5CDF1952AAFA19600513312 /* ConfigurationErrorsView.swift in Sources */, A55B7BBC29B6FC330055DE60 /* SurfaceView.swift in Sources */, A5333E1C2B5A1CE3008AEFF7 /* CrossKit.swift in Sources */, A59444F729A2ED5200725BBA /* SettingsView.swift in Sources */, A56D58862ACDDB4100508D2C /* Ghostty.Shell.swift in Sources */, A5985CD72C320C4500C57AD3 /* String+Extension.swift in Sources */, + A5A2A3CC2D444ABB0033CF96 /* NSApplication+Extension.swift in Sources */, A59630A22AF0415000D64628 /* Ghostty.TerminalSplit.swift in Sources */, A5FEB3002ABB69450068369E /* main.swift in Sources */, A55B7BB829B6F53A0055DE60 /* Package.swift in Sources */, diff --git a/macos/Ghostty.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/macos/Ghostty.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index d380910a8b..5ace476e08 100644 --- a/macos/Ghostty.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/macos/Ghostty.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -6,8 +6,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/sparkle-project/Sparkle", "state" : { - "revision" : "b456fd404954a9e13f55aa0c88cd5a40b8399638", - "version" : "2.6.3" + "revision" : "0ef1ee0220239b3776f433314515fd849025673f", + "version" : "2.6.4" } } ], diff --git a/macos/Sources/App/macOS/AppDelegate.swift b/macos/Sources/App/macOS/AppDelegate.swift index e3518cd2b5..12f8f8c31b 100644 --- a/macos/Sources/App/macOS/AppDelegate.swift +++ b/macos/Sources/App/macOS/AppDelegate.swift @@ -30,15 +30,18 @@ class AppDelegate: NSObject, @IBOutlet private var menuSplitRight: NSMenuItem? @IBOutlet private var menuSplitDown: NSMenuItem? @IBOutlet private var menuClose: NSMenuItem? + @IBOutlet private var menuCloseTab: NSMenuItem? @IBOutlet private var menuCloseWindow: NSMenuItem? @IBOutlet private var menuCloseAllWindows: NSMenuItem? @IBOutlet private var menuCopy: NSMenuItem? @IBOutlet private var menuPaste: NSMenuItem? + @IBOutlet private var menuPasteSelection: NSMenuItem? @IBOutlet private var menuSelectAll: NSMenuItem? @IBOutlet private var menuToggleVisibility: NSMenuItem? @IBOutlet private var menuToggleFullScreen: NSMenuItem? + @IBOutlet private var menuBringAllToFront: NSMenuItem? @IBOutlet private var menuZoomSplit: NSMenuItem? @IBOutlet private var menuPreviousSplit: NSMenuItem? @IBOutlet private var menuNextSplit: NSMenuItem? @@ -50,6 +53,7 @@ class AppDelegate: NSObject, @IBOutlet private var menuIncreaseFontSize: NSMenuItem? @IBOutlet private var menuDecreaseFontSize: NSMenuItem? @IBOutlet private var menuResetFontSize: NSMenuItem? + @IBOutlet private var menuChangeTitle: NSMenuItem? @IBOutlet private var menuQuickTerminal: NSMenuItem? @IBOutlet private var menuTerminalInspector: NSMenuItem? @@ -90,10 +94,8 @@ class AppDelegate: NSObject, return ProcessInfo.processInfo.systemUptime - applicationLaunchTime } - /// Tracks whether the application is currently visible. This can be gamed, i.e. if a user manually - /// brings each window one by one to the front. But at worst its off by one set of toggles and this - /// makes our logic very easy. - private var isVisible: Bool = true + /// Tracks the windows that we hid for toggleVisibility. + private var hiddenState: ToggleVisibilityState? = nil /// The observer for the app appearance. private var appearanceObserver: NSKeyValueObservation? = nil @@ -217,15 +219,20 @@ class AppDelegate: NSObject, } func applicationDidBecomeActive(_ notification: Notification) { - guard !applicationHasBecomeActive else { return } - applicationHasBecomeActive = true - - // Let's launch our first window. We only do this if we have no other windows. It - // is possible to have other windows in a few scenarios: - // - if we're opening a URL since `application(_:openFile:)` is called before this. - // - if we're restoring from persisted state - if terminalManager.windows.count == 0 && derivedConfig.initialWindow { - terminalManager.newWindow() + // If we're back manually then clear the hidden state because macOS handles it. + self.hiddenState = nil + + // First launch stuff + if (!applicationHasBecomeActive) { + applicationHasBecomeActive = true + + // Let's launch our first window. We only do this if we have no other windows. It + // is possible to have other windows in a few scenarios: + // - if we're opening a URL since `application(_:openFile:)` is called before this. + // - if we're restoring from persisted state + if terminalManager.windows.count == 0 && derivedConfig.initialWindow { + terminalManager.newWindow() + } } } @@ -240,7 +247,13 @@ class AppDelegate: NSObject, // This probably isn't fully safe. The isEmpty check above is aspirational, it doesn't // quite work with SwiftUI because windows are retained on close. So instead we check // if there are any that are visible. I'm guessing this breaks under certain scenarios. - if (windows.allSatisfy { !$0.isVisible }) { return .terminateNow } + // + // NOTE(mitchellh): I don't think we need this check at all anymore. I'm keeping it + // here because I don't want to remove it in a patch release cycle but we should + // target removing it soon. + if (self.quickController == nil && windows.allSatisfy { !$0.isVisible }) { + return .terminateNow + } // If the user is shutting down, restarting, or logging out, we don't confirm quit. why: if let event = NSAppleEventManager.shared().currentAppleEvent { @@ -346,6 +359,7 @@ class AppDelegate: NSObject, syncMenuShortcut(config, action: "new_window", menuItem: self.menuNewWindow) syncMenuShortcut(config, action: "new_tab", menuItem: self.menuNewTab) syncMenuShortcut(config, action: "close_surface", menuItem: self.menuClose) + syncMenuShortcut(config, action: "close_tab", menuItem: self.menuCloseTab) syncMenuShortcut(config, action: "close_window", menuItem: self.menuCloseWindow) syncMenuShortcut(config, action: "close_all_windows", menuItem: self.menuCloseAllWindows) syncMenuShortcut(config, action: "new_split:right", menuItem: self.menuSplitRight) @@ -353,6 +367,7 @@ class AppDelegate: NSObject, syncMenuShortcut(config, action: "copy_to_clipboard", menuItem: self.menuCopy) syncMenuShortcut(config, action: "paste_from_clipboard", menuItem: self.menuPaste) + syncMenuShortcut(config, action: "paste_from_selection", menuItem: self.menuPasteSelection) syncMenuShortcut(config, action: "select_all", menuItem: self.menuSelectAll) syncMenuShortcut(config, action: "toggle_split_zoom", menuItem: self.menuZoomSplit) @@ -371,6 +386,7 @@ class AppDelegate: NSObject, syncMenuShortcut(config, action: "increase_font_size:1", menuItem: self.menuIncreaseFontSize) syncMenuShortcut(config, action: "decrease_font_size:1", menuItem: self.menuDecreaseFontSize) syncMenuShortcut(config, action: "reset_font_size", menuItem: self.menuResetFontSize) + syncMenuShortcut(config, action: "change_title_prompt", menuItem: self.menuChangeTitle) syncMenuShortcut(config, action: "toggle_quick_terminal", menuItem: self.menuQuickTerminal) syncMenuShortcut(config, action: "toggle_visibility", menuItem: self.menuToggleVisibility) syncMenuShortcut(config, action: "inspector:toggle", menuItem: self.menuTerminalInspector) @@ -424,7 +440,7 @@ class AppDelegate: NSObject, // If we have a main window then we don't process any of the keys // because we let it capture and propagate. guard NSApp.mainWindow == nil else { return event } - + // If this event as-is would result in a key binding then we send it. if let app = ghostty.app, ghostty_app_key_is_binding( @@ -440,26 +456,26 @@ class AppDelegate: NSObject, return nil } } - + // If this event would be handled by our menu then we do nothing. if let mainMenu = NSApp.mainMenu, mainMenu.performKeyEquivalent(with: event) { return nil } - + // If we reach this point then we try to process the key event // through the Ghostty key mechanism. - + // Ghostty must be loaded guard let ghostty = self.ghostty.app else { return event } - + // Build our event input and call ghostty if (ghostty_app_key(ghostty, event.ghosttyKeyEvent(GHOSTTY_ACTION_PRESS))) { // The key was used so we want to stop it from going to our Mac app Ghostty.logger.debug("local key event handled event=\(event)") return nil } - + return event } @@ -517,6 +533,15 @@ class AppDelegate: NSObject, // AppKit mutex on the appearance. DispatchQueue.main.async { self.syncAppearance(config: config) } + // Decide whether to hide/unhide app from dock and app switcher + switch (config.macosHidden) { + case .never: + NSApp.setActivationPolicy(.regular) + + case .always: + NSApp.setActivationPolicy(.accessory) + } + // If we have configuration errors, we need to show them. let c = ConfigurationErrorsController.sharedInstance c.errors = config.errors @@ -550,6 +575,30 @@ class AppDelegate: NSObject, self.appIcon = nil break + case .blueprint: + self.appIcon = NSImage(named: "BlueprintImage")! + + case .chalkboard: + self.appIcon = NSImage(named: "ChalkboardImage")! + + case .glass: + self.appIcon = NSImage(named: "GlassImage")! + + case .holographic: + self.appIcon = NSImage(named: "HolographicImage")! + + case .microchip: + self.appIcon = NSImage(named: "MicrochipImage")! + + case .paper: + self.appIcon = NSImage(named: "PaperImage")! + + case .retro: + self.appIcon = NSImage(named: "RetroImage")! + + case .xray: + self.appIcon = NSImage(named: "XrayImage")! + case .customStyle: guard let ghostColor = config.macosIconGhostColor else { break } guard let screenColors = config.macosIconScreenColor else { break } @@ -702,21 +751,35 @@ class AppDelegate: NSObject, /// Toggles visibility of all Ghosty Terminal windows. When hidden, activates Ghostty as the frontmost application @IBAction func toggleVisibility(_ sender: Any) { - // We only care about terminal windows. - for window in NSApp.windows.filter({ $0.windowController is BaseTerminalController }) { - if isVisible { - window.orderOut(nil) - } else { - window.makeKeyAndOrderFront(nil) - } + // If we have focus, then we hide all windows. + if NSApp.isActive { + // Toggle visibility doesn't do anything if the focused window is native + // fullscreen. This is only relevant if Ghostty is active. + guard let keyWindow = NSApp.keyWindow, + !keyWindow.styleMask.contains(.fullScreen) else { return } + + // Keep track of our hidden state to restore properly + self.hiddenState = .init() + NSApp.hide(nil) + return } - // After bringing them all to front we make sure our app is active too. - if !isVisible { + // If we're not active, we want to become active + NSApp.activate(ignoringOtherApps: true) + + // Bring all windows to the front. Note: we don't use NSApp.unhide because + // that will unhide ALL hidden windows. We want to only bring forward the + // ones that we hid. + hiddenState?.restore() + hiddenState = nil + } + + @IBAction func bringAllToFront(_ sender: Any) { + if !NSApp.isActive { NSApp.activate(ignoringOtherApps: true) } - - isVisible.toggle() + + NSApplication.shared.arrangeInFront(sender) } private struct DerivedConfig { @@ -736,4 +799,33 @@ class AppDelegate: NSObject, self.quickTerminalPosition = config.quickTerminalPosition } } + + private struct ToggleVisibilityState { + let hiddenWindows: [Weak] + let keyWindow: Weak? + + init() { + // We need to know the key window so that we can bring focus back to the + // right window if it was hidden. + self.keyWindow = if let keyWindow = NSApp.keyWindow { + .init(keyWindow) + } else { + nil + } + + // We need to keep track of the windows that were visible because we only + // want to bring back these windows if we remove the toggle. + // + // We also ignore fullscreen windows because they don't hide anyways. + self.hiddenWindows = NSApp.windows.filter { + $0.isVisible && + !$0.styleMask.contains(.fullScreen) + }.map { Weak($0) } + } + + func restore() { + hiddenWindows.forEach { $0.value?.orderFrontRegardless() } + keyWindow?.value?.makeKey() + } + } } diff --git a/macos/Sources/App/macOS/MainMenu.xib b/macos/Sources/App/macOS/MainMenu.xib index 7a8e0d894b..05364212fe 100644 --- a/macos/Sources/App/macOS/MainMenu.xib +++ b/macos/Sources/App/macOS/MainMenu.xib @@ -1,8 +1,8 @@ - + - + @@ -14,9 +14,12 @@ + + + @@ -31,6 +34,7 @@ + @@ -154,6 +158,12 @@ + + + + + + @@ -185,6 +195,12 @@ + + + + + + @@ -218,6 +234,13 @@ + + + + + + + @@ -256,12 +279,6 @@ - - - - - - @@ -356,6 +373,13 @@ + + + + + + + diff --git a/macos/Sources/Features/QuickTerminal/QuickTerminalController.swift b/macos/Sources/Features/QuickTerminal/QuickTerminalController.swift index 47ee2dfd95..8079358063 100644 --- a/macos/Sources/Features/QuickTerminal/QuickTerminalController.swift +++ b/macos/Sources/Features/QuickTerminal/QuickTerminalController.swift @@ -3,6 +3,12 @@ import Cocoa import SwiftUI import GhosttyKit +// This is a Apple's private function that we need to call to get the active space. +@_silgen_name("CGSGetActiveSpace") +func CGSGetActiveSpace(_ cid: Int) -> size_t +@_silgen_name("CGSMainConnectionID") +func CGSMainConnectionID() -> Int + /// Controller for the "quick" terminal. class QuickTerminalController: BaseTerminalController { override var windowNibName: NSNib.Name? { "QuickTerminal" } @@ -18,6 +24,12 @@ class QuickTerminalController: BaseTerminalController { /// application to the front. private var previousApp: NSRunningApplication? = nil + // The active space when the quick terminal was last shown. + private var previousActiveSpace: size_t = 0 + + /// Non-nil if we have hidden dock state. + private var hiddenDock: HiddenDock? = nil + /// The configuration derived from the Ghostty config so we don't need to rely on references. private var derivedConfig: DerivedConfig @@ -32,6 +44,11 @@ class QuickTerminalController: BaseTerminalController { // Setup our notifications for behaviors let center = NotificationCenter.default + center.addObserver( + self, + selector: #selector(applicationWillTerminate(_:)), + name: NSApplication.willTerminateNotification, + object: nil) center.addObserver( self, selector: #selector(onToggleFullscreen), @@ -52,6 +69,9 @@ class QuickTerminalController: BaseTerminalController { // Remove all of our notificationcenter subscriptions let center = NotificationCenter.default center.removeObserver(self) + + // Make sure we restore our hidden dock + hiddenDock = nil } // MARK: NSWindowController @@ -87,6 +107,17 @@ class QuickTerminalController: BaseTerminalController { // MARK: NSWindowDelegate + override func windowDidBecomeKey(_ notification: Notification) { + super.windowDidBecomeKey(notification) + + // If we're not visible we don't care to run the logic below. It only + // applies if we can be seen. + guard visible else { return } + + // Re-hide the dock if we were hiding it before. + hiddenDock?.hide() + } + override func windowDidResignKey(_ notification: Notification) { super.windowDidResignKey(notification) @@ -107,8 +138,32 @@ class QuickTerminalController: BaseTerminalController { self.previousApp = nil } - if (derivedConfig.quickTerminalAutoHide) { - animateOut() + // Regardless of autohide, we always want to bring the dock back + // when we lose focus. + hiddenDock?.restore() + + if derivedConfig.quickTerminalAutoHide { + switch derivedConfig.quickTerminalSpaceBehavior { + case .remain: + // If we lose focus on the active space, then we can animate out + animateOut() + + case .move: + let currentActiveSpace = CGSGetActiveSpace(CGSMainConnectionID()) + if previousActiveSpace == currentActiveSpace { + // We haven't moved spaces. We lost focus to another app on the + // current space. Animate out. + animateOut() + } else { + // We've moved to a different space. Bring the quick terminal back + // into view. + DispatchQueue.main.async { + self.window?.makeKeyAndOrderFront(nil) + } + + self.previousActiveSpace = currentActiveSpace + } + } } } @@ -163,6 +218,9 @@ class QuickTerminalController: BaseTerminalController { } } + // Set previous active space + self.previousActiveSpace = CGSGetActiveSpace(CGSMainConnectionID()) + // Animate the window in animateWindowIn(window: window, from: position) @@ -198,8 +256,29 @@ class QuickTerminalController: BaseTerminalController { // Move our window off screen to the top position.setInitial(in: window, on: screen) + // We need to set our window level to a high value. In testing, only + // popUpMenu and above do what we want. This gets it above the menu bar + // and lets us render off screen. + window.level = .popUpMenu + // Move it to the visible position since animation requires this - window.makeKeyAndOrderFront(nil) + DispatchQueue.main.async { + window.makeKeyAndOrderFront(nil) + } + + // If our dock position would conflict with our target location then + // we autohide the dock. + if position.conflictsWithDock(on: screen) { + if (hiddenDock == nil) { + hiddenDock = .init() + } + + hiddenDock?.hide() + } else { + // Ensure we don't have any hidden dock if we don't conflict. + // The deinit will restore. + hiddenDock = nil + } // Run the animation that moves our window into the proper place and makes // it visible. @@ -211,8 +290,16 @@ class QuickTerminalController: BaseTerminalController { // There is a very minor delay here so waiting at least an event loop tick // keeps us safe from the view not being on the window. DispatchQueue.main.async { - // If we canceled our animation in we do nothing - guard self.visible else { return } + // If we canceled our animation clean up some state. + guard self.visible else { + self.hiddenDock = nil + return + } + + // After animating in, we reset the window level to a value that + // is above other windows but not as high as popUpMenu. This allows + // things like IME dropdowns to appear properly. + window.level = .floating // Now that the window is visible, sync our appearance. This function // requires the window is visible. @@ -276,6 +363,17 @@ class QuickTerminalController: BaseTerminalController { } private func animateWindowOut(window: NSWindow, to position: QuickTerminalPosition) { + // If we hid the dock then we unhide it. + hiddenDock = nil + + // If the window isn't on our active space then we don't animate, we just + // hide it. + if !window.isOnActiveSpace { + self.previousApp = nil + window.orderOut(self) + return + } + // We always animate out to whatever screen the window is actually on. guard let screen = window.screen ?? NSScreen.main else { return } @@ -297,6 +395,11 @@ class QuickTerminalController: BaseTerminalController { } } + // We need to set our window level to a high value. In testing, only + // popUpMenu and above do what we want. This gets it above the menu bar + // and lets us render off screen. + window.level = .popUpMenu + NSAnimationContext.runAnimationGroup({ context in context.duration = derivedConfig.quickTerminalAnimationDuration context.timingFunction = .init(name: .easeIn) @@ -311,23 +414,13 @@ class QuickTerminalController: BaseTerminalController { private func syncAppearance() { guard let window else { return } + // Change the collection behavior of the window depending on the configuration. + window.collectionBehavior = derivedConfig.quickTerminalSpaceBehavior.collectionBehavior + // If our window is not visible, then no need to sync the appearance yet. // Some APIs such as window blur have no effect unless the window is visible. guard window.isVisible else { return } - // Terminals typically operate in sRGB color space and macOS defaults - // to "native" which is typically P3. There is a lot more resources - // covered in this GitHub issue: https://github.com/mitchellh/ghostty/pull/376 - // Ghostty defaults to sRGB but this can be overridden. - switch (self.derivedConfig.windowColorspace) { - case "display-p3": - window.colorSpace = .displayP3 - case "srgb": - fallthrough - default: - window.colorSpace = .sRGB - } - // If we have window transparency then set it transparent. Otherwise set it opaque. if (self.derivedConfig.backgroundOpacity < 1) { window.isOpaque = false @@ -368,6 +461,13 @@ class QuickTerminalController: BaseTerminalController { // MARK: Notifications + @objc private func applicationWillTerminate(_ notification: Notification) { + // If the application is going to terminate we want to make sure we + // restore any global dock state. I think deinit should be called which + // would call this anyways but I can't be sure so I will do this too. + hiddenDock = nil + } + @objc private func onToggleFullscreen(notification: SwiftUI.Notification) { guard let target = notification.object as? Ghostty.SurfaceView else { return } guard target == self.focusedSurface else { return } @@ -396,14 +496,14 @@ class QuickTerminalController: BaseTerminalController { let quickTerminalScreen: QuickTerminalScreen let quickTerminalAnimationDuration: Double let quickTerminalAutoHide: Bool - let windowColorspace: String + let quickTerminalSpaceBehavior: QuickTerminalSpaceBehavior let backgroundOpacity: Double init() { self.quickTerminalScreen = .main self.quickTerminalAnimationDuration = 0.2 self.quickTerminalAutoHide = true - self.windowColorspace = "" + self.quickTerminalSpaceBehavior = .move self.backgroundOpacity = 1.0 } @@ -411,10 +511,39 @@ class QuickTerminalController: BaseTerminalController { self.quickTerminalScreen = config.quickTerminalScreen self.quickTerminalAnimationDuration = config.quickTerminalAnimationDuration self.quickTerminalAutoHide = config.quickTerminalAutoHide - self.windowColorspace = config.windowColorspace + self.quickTerminalSpaceBehavior = config.quickTerminalSpaceBehavior self.backgroundOpacity = config.backgroundOpacity } } + + /// Hides the dock globally (not just NSApp). This is only used if the quick terminal is + /// in a conflicting position with the dock. + private class HiddenDock { + let previousAutoHide: Bool + private var hidden: Bool = false + + init() { + previousAutoHide = Dock.autoHideEnabled + } + + deinit { + restore() + } + + func hide() { + guard !hidden else { return } + NSApp.acquirePresentationOption(.autoHideDock) + Dock.autoHideEnabled = true + hidden = true + } + + func restore() { + guard hidden else { return } + NSApp.releasePresentationOption(.autoHideDock) + Dock.autoHideEnabled = previousAutoHide + hidden = false + } + } } extension Notification.Name { diff --git a/macos/Sources/Features/QuickTerminal/QuickTerminalPosition.swift b/macos/Sources/Features/QuickTerminal/QuickTerminalPosition.swift index 0acbfec1b2..7ba124a309 100644 --- a/macos/Sources/Features/QuickTerminal/QuickTerminalPosition.swift +++ b/macos/Sources/Features/QuickTerminal/QuickTerminalPosition.swift @@ -69,7 +69,7 @@ enum QuickTerminalPosition : String { finalSize.width = screen.frame.width case .left, .right: - finalSize.height = screen.frame.height + finalSize.height = screen.visibleFrame.height case .center: finalSize.width = screen.frame.width / 2 @@ -118,4 +118,22 @@ enum QuickTerminalPosition : String { return .init(x: screen.visibleFrame.origin.x + (screen.visibleFrame.width - window.frame.width) / 2, y: screen.visibleFrame.origin.y + (screen.visibleFrame.height - window.frame.height) / 2) } } + + func conflictsWithDock(on screen: NSScreen) -> Bool { + // Screen must have a dock for it to conflict + guard screen.hasDock else { return false } + + // Get the dock orientation for this screen + guard let orientation = Dock.orientation else { return false } + + // Depending on the orientation of the dock, we conflict if our quick terminal + // would potentially "hit" the dock. In the future we should probably consider + // the frame of the quick terminal. + return switch (orientation) { + case .top: self == .top || self == .left || self == .right + case .bottom: self == .bottom || self == .left || self == .right + case .left: self == .top || self == .bottom + case .right: self == .top || self == .bottom + } + } } diff --git a/macos/Sources/Features/QuickTerminal/QuickTerminalSpaceBehavior.swift b/macos/Sources/Features/QuickTerminal/QuickTerminalSpaceBehavior.swift new file mode 100644 index 0000000000..0561aaa188 --- /dev/null +++ b/macos/Sources/Features/QuickTerminal/QuickTerminalSpaceBehavior.swift @@ -0,0 +1,36 @@ +import Foundation +import Cocoa + +enum QuickTerminalSpaceBehavior { + case remain + case move + + init?(fromGhosttyConfig string: String) { + switch (string) { + case "move": + self = .move + + case "remain": + self = .remain + + default: + return nil + } + } + + var collectionBehavior: NSWindow.CollectionBehavior { + let commonBehavior: [NSWindow.CollectionBehavior] = [ + .ignoresCycle, + .fullScreenAuxiliary + ] + + switch (self) { + case .move: + // We want this to move the window to the active space. + return NSWindow.CollectionBehavior([.canJoinAllSpaces] + commonBehavior) + case .remain: + // We want this to remain the window in the current space. + return NSWindow.CollectionBehavior([.moveToActiveSpace] + commonBehavior) + } + } +} diff --git a/macos/Sources/Features/QuickTerminal/QuickTerminalWindow.swift b/macos/Sources/Features/QuickTerminal/QuickTerminalWindow.swift index ed3a7f781c..005808a23d 100644 --- a/macos/Sources/Features/QuickTerminal/QuickTerminalWindow.swift +++ b/macos/Sources/Features/QuickTerminal/QuickTerminalWindow.swift @@ -1,6 +1,6 @@ import Cocoa -class QuickTerminalWindow: NSWindow { +class QuickTerminalWindow: NSPanel { // Both of these must be true for windows without decorations to be able to // still become key/main and receive events. override var canBecomeKey: Bool { return true } @@ -26,22 +26,7 @@ class QuickTerminalWindow: NSWindow { // window remains resizable. self.styleMask.remove(.titled) - // We need to set our window level to a high value. In testing, only - // popUpMenu and above do what we want. This gets it above the menu bar - // and lets us render off screen. - self.level = .popUpMenu - - // This plus the level above was what was needed for the animation to work, - // because it gets the window off screen properly. Plus we add some fields - // we just want the behavior of. - self.collectionBehavior = [ - // We want this to be part of every space because it is a singleton. - .canJoinAllSpaces, - - // We don't want to be part of command-tilde - .ignoresCycle, - - // We never support fullscreen - .fullScreenNone] + // We don't want to activate the owning app when quick terminal is triggered. + self.styleMask.insert(.nonactivatingPanel) } } diff --git a/macos/Sources/Features/Terminal/BaseTerminalController.swift b/macos/Sources/Features/Terminal/BaseTerminalController.swift index 393c6ef4dd..bace20f052 100644 --- a/macos/Sources/Features/Terminal/BaseTerminalController.swift +++ b/macos/Sources/Features/Terminal/BaseTerminalController.swift @@ -389,9 +389,9 @@ class BaseTerminalController: NSWindowController, } switch (request) { - case .osc_52_write: + case let .osc_52_write(pasteboard): guard case .confirm = action else { break } - let pb = NSPasteboard.general + let pb = pasteboard ?? NSPasteboard.general pb.declareTypes([.string], owner: nil) pb.setString(cc.contents, forType: .string) case .osc_52_read, .paste: @@ -452,6 +452,7 @@ class BaseTerminalController: NSWindowController, self.alert = nil switch (response) { case .alertFirstButtonReturn: + alert.window.orderOut(nil) window.close() default: diff --git a/macos/Sources/Features/Terminal/TerminalController.swift b/macos/Sources/Features/Terminal/TerminalController.swift index 2da498e3ac..8118103d67 100644 --- a/macos/Sources/Features/Terminal/TerminalController.swift +++ b/macos/Sources/Features/Terminal/TerminalController.swift @@ -22,7 +22,7 @@ class TerminalController: BaseTerminalController { private var restorable: Bool = true /// The configuration derived from the Ghostty config so we don't need to rely on references. - private var derivedConfig: DerivedConfig + private(set) var derivedConfig: DerivedConfig /// The notification cancellable for focused surface property changes. private var surfaceAppearanceCancellables: Set = [] @@ -60,6 +60,11 @@ class TerminalController: BaseTerminalController { selector: #selector(onGotoTab), name: Ghostty.Notification.ghosttyGotoTab, object: nil) + center.addObserver( + self, + selector: #selector(onCloseTab), + name: .ghosttyCloseTab, + object: nil) center.addObserver( self, selector: #selector(ghosttyConfigDidChange(_:)), @@ -278,9 +283,12 @@ class TerminalController: BaseTerminalController { private func setInitialWindowPosition(x: Int16?, y: Int16?, windowDecorations: Bool) { guard let window else { return } - // If we don't have both an X and Y we center. + // If we don't have an X/Y then we try to use the previously saved window pos. guard let x, let y else { - window.center() + if (!LastWindowPosition.shared.restore(window)) { + window.center() + } + return } @@ -310,28 +318,28 @@ class TerminalController: BaseTerminalController { window.styleMask = [ // We need `titled` in the mask to get the normal window frame .titled, - + // Full size content view so we can extend // content in to the hidden titlebar's area - .fullSizeContentView, - - .resizable, + .fullSizeContentView, + + .resizable, .closable, .miniaturizable, ] - + // Hide the title window.titleVisibility = .hidden window.titlebarAppearsTransparent = true - + // Hide the traffic lights (window control buttons) window.standardWindowButton(.closeButton)?.isHidden = true window.standardWindowButton(.miniaturizeButton)?.isHidden = true window.standardWindowButton(.zoomButton)?.isHidden = true - + // Disallow tabbing if the titlebar is hidden, since that will (should) also hide the tab bar. window.tabbingMode = .disallowed - + // Nuke it from orbit -- hide the titlebar container entirely, just in case. There are // some operations that appear to bring back the titlebar visibility so this ensures // it is gone forever. @@ -340,7 +348,7 @@ class TerminalController: BaseTerminalController { titleBarContainer.isHidden = true } } - + override func windowDidLoad() { super.windowDidLoad() guard let window = window as? TerminalWindow else { return } @@ -361,33 +369,31 @@ class TerminalController: BaseTerminalController { // If window decorations are disabled, remove our title if (!config.windowDecorations) { window.styleMask.remove(.titled) } - // Terminals typically operate in sRGB color space and macOS defaults - // to "native" which is typically P3. There is a lot more resources - // covered in this GitHub issue: https://github.com/mitchellh/ghostty/pull/376 - // Ghostty defaults to sRGB but this can be overridden. - switch (config.windowColorspace) { - case "display-p3": - window.colorSpace = .displayP3 - case "srgb": - fallthrough - default: - window.colorSpace = .sRGB - } - // If we have only a single surface (no splits) and that surface requested // an initial size then we set it here now. if case let .leaf(leaf) = surfaceTree { if let initialSize = leaf.surface.initialSize, let screen = window.screen ?? NSScreen.main { - // Setup our frame. We need to first subtract the views frame so that we can - // just get the chrome frame so that we only affect the surface view size. + // Get the current frame of the window var frame = window.frame - frame.size.width -= leaf.surface.frame.size.width - frame.size.height -= leaf.surface.frame.size.height - frame.size.width += min(initialSize.width, screen.frame.width) - frame.size.height += min(initialSize.height, screen.frame.height) - // We have no tabs and we are not a split, so set the initial size of the window. + // Calculate the chrome size (window size minus view size) + let chromeWidth = frame.size.width - leaf.surface.frame.size.width + let chromeHeight = frame.size.height - leaf.surface.frame.size.height + + // Calculate the new width and height, clamping to the screen's size + let newWidth = min(initialSize.width + chromeWidth, screen.visibleFrame.width) + let newHeight = min(initialSize.height + chromeHeight, screen.visibleFrame.height) + + // Update the frame size while keeping the window's position intact + frame.size.width = newWidth + frame.size.height = newHeight + + // Ensure the window doesn't go outside the screen boundaries + frame.origin.x = max(screen.frame.origin.x, min(frame.origin.x, screen.frame.maxX - newWidth)) + frame.origin.y = max(screen.frame.origin.y, min(frame.origin.y, screen.frame.maxY - newHeight)) + + // Set the updated frame to the window window.setFrame(frame, display: true) } } @@ -487,6 +493,20 @@ class TerminalController: BaseTerminalController { override func windowDidMove(_ notification: Notification) { super.windowDidMove(notification) self.fixTabBar() + + // Whenever we move save our last position for the next start. + if let window { + LastWindowPosition.shared.save(window) + } + } + + func windowDidBecomeMain(_ notification: Notification) { + // Whenever we get focused, use that as our last window position for + // restart. This differs from Terminal.app but matches iTerm2 behavior + // and I think its sensible. + if let window { + LastWindowPosition.shared.save(window) + } } // Called when the window will be encoded. We handle the data encoding here in the @@ -508,7 +528,50 @@ class TerminalController: BaseTerminalController { ghostty.newTab(surface: surface) } - @IBAction override func closeWindow(_ sender: Any) { + private func confirmClose( + window: NSWindow, + messageText: String, + informativeText: String, + completion: @escaping () -> Void + ) { + // If we need confirmation by any, show one confirmation for all windows + // in the tab group. + let alert = NSAlert() + alert.messageText = messageText + alert.informativeText = informativeText + alert.addButton(withTitle: "Close") + alert.addButton(withTitle: "Cancel") + alert.alertStyle = .warning + alert.beginSheetModal(for: window) { response in + if response == .alertFirstButtonReturn { + completion() + } + } + } + + @IBAction func closeTab(_ sender: Any?) { + guard let window = window else { return } + guard window.tabGroup != nil else { + // No tabs, no tab group, just perform a normal close. + window.performClose(sender) + return + } + + if surfaceTree?.needsConfirmQuit() ?? false { + confirmClose( + window: window, + messageText: "Close Tab?", + informativeText: "The terminal still has a running process. If you close the tab the process will be killed." + ) { + window.close() + } + return + } + + window.close() + } + + @IBAction override func closeWindow(_ sender: Any?) { guard let window = window else { return } guard let tabGroup = window.tabGroup else { // No tabs, no tab group, just perform a normal close. @@ -523,47 +586,34 @@ class TerminalController: BaseTerminalController { } // Check if any windows require close confirmation. - var needsConfirm: Bool = false - for tabWindow in tabGroup.windows { - guard let c = tabWindow.windowController as? TerminalController else { continue } - if (c.surfaceTree?.needsConfirmQuit() ?? false) { - needsConfirm = true - break + let needsConfirm = tabGroup.windows.contains { tabWindow in + guard let controller = tabWindow.windowController as? TerminalController else { + return false } + return controller.surfaceTree?.needsConfirmQuit() ?? false } // If none need confirmation then we can just close all the windows. - if (!needsConfirm) { - for tabWindow in tabGroup.windows { - tabWindow.close() - } - + if !needsConfirm { + tabGroup.windows.forEach { $0.close() } return } - // If we need confirmation by any, show one confirmation for all windows - // in the tab group. - let alert = NSAlert() - alert.messageText = "Close Window?" - alert.informativeText = "All terminal sessions in this window will be terminated." - alert.addButton(withTitle: "Close Window") - alert.addButton(withTitle: "Cancel") - alert.alertStyle = .warning - alert.beginSheetModal(for: window, completionHandler: { response in - if (response == .alertFirstButtonReturn) { - for tabWindow in tabGroup.windows { - tabWindow.close() - } - } - }) + confirmClose( + window: window, + messageText: "Close Window?", + informativeText: "All terminal sessions in this window will be terminated." + ) { + tabGroup.windows.forEach { $0.close() } + } } - @IBAction func toggleGhosttyFullScreen(_ sender: Any) { + @IBAction func toggleGhosttyFullScreen(_ sender: Any?) { guard let surface = focusedSurface?.surface else { return } ghostty.toggleFullscreen(surface: surface) } - @IBAction func toggleTerminalInspector(_ sender: Any) { + @IBAction func toggleTerminalInspector(_ sender: Any?) { guard let surface = focusedSurface?.surface else { return } ghostty.toggleTerminalInspector(surface: surface) } @@ -659,13 +709,21 @@ class TerminalController: BaseTerminalController { // If our index is the same we do nothing guard finalIndex != selectedIndex else { return } - // Get our parent - let parent = tabbedWindows[finalIndex] + // Get our target window + let targetWindow = tabbedWindows[finalIndex] + + // Begin a group of window operations to minimize visual updates + NSAnimationContext.beginGrouping() + NSAnimationContext.current.duration = 0 - // Move our current selected window to the proper index + // Remove and re-add the window in the correct position tabGroup.removeWindow(selectedWindow) - parent.addTabbedWindow(selectedWindow, ordered: action.amount < 0 ? .below : .above) - selectedWindow.makeKeyAndOrderFront(nil) + targetWindow.addTabbedWindow(selectedWindow, ordered: action.amount < 0 ? .below : .above) + + // Ensure our window remains selected + selectedWindow.makeKey() + + NSAnimationContext.endGrouping() } @objc private func onGotoTab(notification: SwiftUI.Notification) { @@ -720,6 +778,12 @@ class TerminalController: BaseTerminalController { targetWindow.makeKeyAndOrderFront(nil) } + @objc private func onCloseTab(notification: SwiftUI.Notification) { + guard let target = notification.object as? Ghostty.SurfaceView else { return } + guard surfaceTree?.contains(view: target) ?? false else { return } + closeTab(self) + } + @objc private func onToggleFullscreen(notification: SwiftUI.Notification) { guard let target = notification.object as? Ghostty.SurfaceView else { return } guard target == self.focusedSurface else { return } @@ -737,7 +801,7 @@ class TerminalController: BaseTerminalController { toggleFullscreen(mode: fullscreenMode) } - private struct DerivedConfig { + struct DerivedConfig { let backgroundColor: Color let macosTitlebarStyle: String diff --git a/macos/Sources/Features/Terminal/TerminalManager.swift b/macos/Sources/Features/Terminal/TerminalManager.swift index 42e35b90eb..2db29aec91 100644 --- a/macos/Sources/Features/Terminal/TerminalManager.swift +++ b/macos/Sources/Features/Terminal/TerminalManager.swift @@ -86,7 +86,7 @@ class TerminalManager { // fullscreen for the logic later in this method. c.toggleFullscreen(mode: .native) - case .nonNative, .nonNativeVisibleMenu: + case .nonNative, .nonNativeVisibleMenu, .nonNativePaddedNotch: // If we're non-native then we have to do it on a later loop // so that the content view is setup. DispatchQueue.main.async { diff --git a/macos/Sources/Features/Terminal/TerminalView.swift b/macos/Sources/Features/Terminal/TerminalView.swift index 15b5048750..3d4165e915 100644 --- a/macos/Sources/Features/Terminal/TerminalView.swift +++ b/macos/Sources/Features/Terminal/TerminalView.swift @@ -10,7 +10,7 @@ protocol TerminalViewDelegate: AnyObject { /// The title of the terminal should change. func titleDidChange(to: String) - + /// The URL of the pwd should change. func pwdDidChange(to: URL?) @@ -56,15 +56,10 @@ struct TerminalView: View { // The title for our window private var title: String { - var title = "👻" - - if let surfaceTitle = surfaceTitle { - if (surfaceTitle.count > 0) { - title = surfaceTitle - } + if let surfaceTitle, !surfaceTitle.isEmpty { + return surfaceTitle } - - return title + return "👻" } // The pwd of the focused surface as a URL @@ -72,7 +67,7 @@ struct TerminalView: View { guard let surfacePwd, surfacePwd != "" else { return nil } return URL(fileURLWithPath: surfacePwd) } - + var body: some View { switch ghostty.readiness { case .loading: diff --git a/macos/Sources/Features/Terminal/TerminalWindow.swift b/macos/Sources/Features/Terminal/TerminalWindow.swift index 0eb8daeeb3..9d29c193f5 100644 --- a/macos/Sources/Features/Terminal/TerminalWindow.swift +++ b/macos/Sources/Features/Terminal/TerminalWindow.swift @@ -115,6 +115,21 @@ class TerminalWindow: NSWindow { } } + // We override this so that with the hidden titlebar style the titlebar + // area is not draggable. + override var contentLayoutRect: CGRect { + var rect = super.contentLayoutRect + + // If we are using a hidden titlebar style, the content layout is the + // full frame making it so that it is not draggable. + if let controller = windowController as? TerminalController, + controller.derivedConfig.macosTitlebarStyle == "hidden" { + rect.origin.y = 0 + rect.size.height = self.frame.height + } + return rect + } + // The window theme configuration from Ghostty. This is used to control some // behaviors that don't look quite right in certain situations. var windowTheme: TerminalWindowTheme? diff --git a/macos/Sources/Ghostty/FullscreenMode+Extension.swift b/macos/Sources/Ghostty/FullscreenMode+Extension.swift index fffd8e84b6..0c0bba908e 100644 --- a/macos/Sources/Ghostty/FullscreenMode+Extension.swift +++ b/macos/Sources/Ghostty/FullscreenMode+Extension.swift @@ -13,6 +13,9 @@ extension FullscreenMode { case GHOSTTY_FULLSCREEN_NON_NATIVE_VISIBLE_MENU: .nonNativeVisibleMenu + case GHOSTTY_FULLSCREEN_NON_NATIVE_PADDED_NOTCH: + .nonNativePaddedNotch + default: nil } diff --git a/macos/Sources/Ghostty/Ghostty.App.swift b/macos/Sources/Ghostty/Ghostty.App.swift index ed140dcd53..ba249e3d11 100644 --- a/macos/Sources/Ghostty/Ghostty.App.swift +++ b/macos/Sources/Ghostty/Ghostty.App.swift @@ -62,7 +62,7 @@ extension Ghostty { // uses to interface with the application runtime environment. var runtime_cfg = ghostty_runtime_config_s( userdata: Unmanaged.passUnretained(self).toOpaque(), - supports_selection_clipboard: false, + supports_selection_clipboard: true, wakeup_cb: { userdata in App.wakeup(userdata) }, action_cb: { app, target, action in App.action(app!, target: target, action: action) }, read_clipboard_cb: { userdata, loc, state in App.readClipboard(userdata, location: loc, state: state) }, @@ -257,7 +257,7 @@ extension Ghostty { // MARK: Ghostty Callbacks (iOS) static func wakeup(_ userdata: UnsafeMutableRawPointer?) {} - static func action(_ app: ghostty_app_t, target: ghostty_target_s, action: ghostty_action_s) {} + static func action(_ app: ghostty_app_t, target: ghostty_target_s, action: ghostty_action_s) -> Bool { return false } static func readClipboard( _ userdata: UnsafeMutableRawPointer?, location: ghostty_clipboard_e, @@ -320,13 +320,13 @@ extension Ghostty { let surfaceView = self.surfaceUserdata(from: userdata) guard let surface = surfaceView.surface else { return } - // We only support the standard clipboard - if (location != GHOSTTY_CLIPBOARD_STANDARD) { + // Get our pasteboard + guard let pasteboard = NSPasteboard.ghostty(location) else { return completeClipboardRequest(surface, data: "", state: state) } // Get our string - let str = NSPasteboard.general.getOpinionatedStringContents() ?? "" + let str = pasteboard.getOpinionatedStringContents() ?? "" completeClipboardRequest(surface, data: str, state: state) } @@ -364,14 +364,12 @@ extension Ghostty { static func writeClipboard(_ userdata: UnsafeMutableRawPointer?, string: UnsafePointer?, location: ghostty_clipboard_e, confirm: Bool) { let surface = self.surfaceUserdata(from: userdata) - // We only support the standard clipboard - if (location != GHOSTTY_CLIPBOARD_STANDARD) { return } + guard let pasteboard = NSPasteboard.ghostty(location) else { return } guard let valueStr = String(cString: string!, encoding: .utf8) else { return } if !confirm { - let pb = NSPasteboard.general - pb.declareTypes([.string], owner: nil) - pb.setString(valueStr, forType: .string) + pasteboard.declareTypes([.string], owner: nil) + pasteboard.setString(valueStr, forType: .string) return } @@ -380,7 +378,7 @@ extension Ghostty { object: surface, userInfo: [ Notification.ConfirmClipboardStrKey: valueStr, - Notification.ConfirmClipboardRequestKey: Ghostty.ClipboardRequest.osc_52_write, + Notification.ConfirmClipboardRequestKey: Ghostty.ClipboardRequest.osc_52_write(pasteboard), ] ) } @@ -425,7 +423,7 @@ extension Ghostty { // MARK: Actions (macOS) - static func action(_ app: ghostty_app_t, target: ghostty_target_s, action: ghostty_action_s) { + static func action(_ app: ghostty_app_t, target: ghostty_target_s, action: ghostty_action_s) -> Bool { // Make sure it a target we understand so all our action handlers can assert switch (target.tag) { case GHOSTTY_TARGET_APP, GHOSTTY_TARGET_SURFACE: @@ -433,7 +431,7 @@ extension Ghostty { default: Ghostty.logger.warning("unknown action target=\(target.tag.rawValue)") - return + return false } // Action dispatch @@ -450,17 +448,20 @@ extension Ghostty { case GHOSTTY_ACTION_NEW_SPLIT: newSplit(app, target: target, direction: action.action.new_split) + case GHOSTTY_ACTION_CLOSE_TAB: + closeTab(app, target: target) + case GHOSTTY_ACTION_TOGGLE_FULLSCREEN: toggleFullscreen(app, target: target, mode: action.action.toggle_fullscreen) case GHOSTTY_ACTION_MOVE_TAB: - moveTab(app, target: target, move: action.action.move_tab) + return moveTab(app, target: target, move: action.action.move_tab) case GHOSTTY_ACTION_GOTO_TAB: - gotoTab(app, target: target, tab: action.action.goto_tab) + return gotoTab(app, target: target, tab: action.action.goto_tab) case GHOSTTY_ACTION_GOTO_SPLIT: - gotoSplit(app, target: target, direction: action.action.goto_split) + return gotoSplit(app, target: target, direction: action.action.goto_split) case GHOSTTY_ACTION_RESIZE_SPLIT: resizeSplit(app, target: target, resize: action.action.resize_split) @@ -483,6 +484,9 @@ extension Ghostty { case GHOSTTY_ACTION_SET_TITLE: setTitle(app, target: target, v: action.action.set_title) + case GHOSTTY_ACTION_PROMPT_TITLE: + return promptTitle(app, target: target) + case GHOSTTY_ACTION_PWD: pwdChanged(app, target: target, v: action.action.pwd) @@ -540,10 +544,15 @@ extension Ghostty { fallthrough case GHOSTTY_ACTION_QUIT_TIMER: Ghostty.logger.info("known but unimplemented action action=\(action.tag.rawValue)") - + return false default: Ghostty.logger.warning("unknown action action=\(action.tag.rawValue)") + return false } + + // If we reached here then we assume performed since all unknown actions + // are captured in the switch and return false. + return true } private static func quit(_ app: ghostty_app_t) { @@ -653,6 +662,27 @@ extension Ghostty { } } + private static func closeTab(_ app: ghostty_app_t, target: ghostty_target_s) { + switch (target.tag) { + case GHOSTTY_TARGET_APP: + Ghostty.logger.warning("close tab does nothing with an app target") + return + + case GHOSTTY_TARGET_SURFACE: + guard let surface = target.target.surface else { return } + guard let surfaceView = self.surfaceView(from: surface) else { return } + + NotificationCenter.default.post( + name: .ghosttyCloseTab, + object: surfaceView + ) + + + default: + assertionFailure() + } + } + private static func toggleFullscreen( _ app: ghostty_app_t, target: ghostty_target_s, @@ -694,15 +724,19 @@ extension Ghostty { private static func moveTab( _ app: ghostty_app_t, target: ghostty_target_s, - move: ghostty_action_move_tab_s) { + move: ghostty_action_move_tab_s) -> Bool { switch (target.tag) { case GHOSTTY_TARGET_APP: Ghostty.logger.warning("move tab does nothing with an app target") - return + return false case GHOSTTY_TARGET_SURFACE: - guard let surface = target.target.surface else { return } - guard let surfaceView = self.surfaceView(from: surface) else { return } + guard let surface = target.target.surface else { return false } + guard let surfaceView = self.surfaceView(from: surface) else { return false } + + // See gotoTab for notes on this check. + guard (surfaceView.window?.tabGroup?.windows.count ?? 0) > 1 else { return false } + NotificationCenter.default.post( name: .ghosttyMoveTab, object: surfaceView, @@ -714,20 +748,27 @@ extension Ghostty { default: assertionFailure() } + + return true } private static func gotoTab( _ app: ghostty_app_t, target: ghostty_target_s, - tab: ghostty_action_goto_tab_e) { + tab: ghostty_action_goto_tab_e) -> Bool { switch (target.tag) { case GHOSTTY_TARGET_APP: Ghostty.logger.warning("goto tab does nothing with an app target") - return + return false case GHOSTTY_TARGET_SURFACE: - guard let surface = target.target.surface else { return } - guard let surfaceView = self.surfaceView(from: surface) else { return } + guard let surface = target.target.surface else { return false } + guard let surfaceView = self.surfaceView(from: surface) else { return false } + + // Similar to goto_split (see comment there) about our performability, + // we should make this more accurate later. + guard (surfaceView.window?.tabGroup?.windows.count ?? 0) > 1 else { return false } + NotificationCenter.default.post( name: Notification.ghosttyGotoTab, object: surfaceView, @@ -739,20 +780,31 @@ extension Ghostty { default: assertionFailure() } + + return true } private static func gotoSplit( _ app: ghostty_app_t, target: ghostty_target_s, - direction: ghostty_action_goto_split_e) { + direction: ghostty_action_goto_split_e) -> Bool { switch (target.tag) { case GHOSTTY_TARGET_APP: Ghostty.logger.warning("goto split does nothing with an app target") - return + return false case GHOSTTY_TARGET_SURFACE: - guard let surface = target.target.surface else { return } - guard let surfaceView = self.surfaceView(from: surface) else { return } + guard let surface = target.target.surface else { return false } + guard let surfaceView = self.surfaceView(from: surface) else { return false } + guard let controller = surfaceView.window?.windowController as? BaseTerminalController else { return false } + + // For now, we return false if the window has no splits and we return + // true if the window has ANY splits. This isn't strictly correct because + // we should only be returning true if we actually performed the action, + // but this handles the most common case of caring about goto_split performability + // which is the no-split case. + guard controller.surfaceTree?.isSplit ?? false else { return false } + NotificationCenter.default.post( name: Notification.ghosttyFocusSplit, object: surfaceView, @@ -764,6 +816,8 @@ extension Ghostty { default: assertionFailure() } + + return true } private static func resizeSplit( @@ -956,6 +1010,26 @@ extension Ghostty { } } + private static func promptTitle( + _ app: ghostty_app_t, + target: ghostty_target_s) -> Bool { + switch (target.tag) { + case GHOSTTY_TARGET_APP: + Ghostty.logger.warning("set title prompt does nothing with an app target") + return false + + case GHOSTTY_TARGET_SURFACE: + guard let surface = target.target.surface else { return false } + guard let surfaceView = self.surfaceView(from: surface) else { return false } + surfaceView.promptTitle() + + default: + assertionFailure() + } + + return true + } + private static func pwdChanged( _ app: ghostty_app_t, target: ghostty_target_s, diff --git a/macos/Sources/Ghostty/Ghostty.Config.swift b/macos/Sources/Ghostty/Ghostty.Config.swift index ed9364914d..ec23632f75 100644 --- a/macos/Sources/Ghostty/Ghostty.Config.swift +++ b/macos/Sources/Ghostty/Ghostty.Config.swift @@ -132,15 +132,6 @@ extension Ghostty { return v } - var windowColorspace: String { - guard let config = self.config else { return "" } - var v: UnsafePointer? = nil - let key = "window-colorspace" - guard ghostty_config_get(config, &v, key, UInt(key.count)) else { return "" } - guard let ptr = v else { return "" } - return String(cString: ptr) - } - var windowSaveState: String { guard let config = self.config else { return "" } var v: UnsafePointer? = nil @@ -174,11 +165,14 @@ extension Ghostty { } var windowDecorations: Bool { - guard let config = self.config else { return true } - var v = false; + let defaultValue = true + guard let config = self.config else { return defaultValue } + var v: UnsafePointer? = nil let key = "window-decoration" - _ = ghostty_config_get(config, &v, key, UInt(key.count)) - return v; + guard ghostty_config_get(config, &v, key, UInt(key.count)) else { return defaultValue } + guard let ptr = v else { return defaultValue } + let str = String(cString: ptr) + return WindowDecoration(rawValue: str)?.enabled() ?? defaultValue } var windowTheme: String? { @@ -222,6 +216,8 @@ extension Ghostty { .nonNative case "visible-menu": .nonNativeVisibleMenu + case "padded-notch": + .nonNativePaddedNotch default: defaultValue } @@ -306,6 +302,16 @@ extension Ghostty { return buffer.map { .init(ghostty: $0) } } + var macosHidden: MacHidden { + guard let config = self.config else { return .never } + var v: UnsafePointer? = nil + let key = "macos-hidden" + guard ghostty_config_get(config, &v, key, UInt(key.count)) else { return .never } + guard let ptr = v else { return .never } + let str = String(cString: ptr) + return MacHidden(rawValue: str) ?? .never + } + var focusFollowsMouse : Bool { guard let config = self.config else { return false } var v = false; @@ -345,7 +351,7 @@ extension Ghostty { var backgroundBlurRadius: Int { guard let config = self.config else { return 1 } var v: Int = 0 - let key = "background-blur-radius" + let key = "background-blur" _ = ghostty_config_get(config, &v, key, UInt(key.count)) return v; } @@ -431,6 +437,16 @@ extension Ghostty { _ = ghostty_config_get(config, &v, key, UInt(key.count)) return v } + + var quickTerminalSpaceBehavior: QuickTerminalSpaceBehavior { + guard let config = self.config else { return .move } + var v: UnsafePointer? = nil + let key = "quick-terminal-space-behavior" + guard ghostty_config_get(config, &v, key, UInt(key.count)) else { return .move } + guard let ptr = v else { return .move } + let str = String(cString: ptr) + return QuickTerminalSpaceBehavior(fromGhosttyConfig: str) ?? .move + } #endif var resizeOverlay: ResizeOverlay { @@ -510,6 +526,11 @@ extension Ghostty.Config { case download } + enum MacHidden : String { + case never + case always + } + enum ResizeOverlay : String { case always case never @@ -553,4 +574,18 @@ extension Ghostty.Config { } } } + + enum WindowDecoration: String { + case none + case client + case server + case auto + + func enabled() -> Bool { + switch self { + case .client, .server, .auto: return true + case .none: return false + } + } + } } diff --git a/macos/Sources/Ghostty/Ghostty.SplitNode.swift b/macos/Sources/Ghostty/Ghostty.SplitNode.swift index 899825d37b..95c019b1f9 100644 --- a/macos/Sources/Ghostty/Ghostty.SplitNode.swift +++ b/macos/Sources/Ghostty/Ghostty.SplitNode.swift @@ -38,6 +38,15 @@ extension Ghostty { } } + /// Returns true if the tree is split. + var isSplit: Bool { + return if case .leaf = self { + false + } else { + true + } + } + func topLeft() -> SurfaceView { switch (self) { case .leaf(let leaf): @@ -120,14 +129,7 @@ extension Ghostty { /// Returns true if the split tree contains the given view. func contains(view: SurfaceView) -> Bool { - switch (self) { - case .leaf(let leaf): - return leaf.surface == view - - case .split(let container): - return container.topLeft.contains(view: view) || - container.bottomRight.contains(view: view) - } + return leaf(for: view) != nil } /// Find a surface view by UUID. @@ -164,6 +166,22 @@ extension Ghostty { } } + /// Return the node for the given view if its in the tree. + func leaf(for view: SurfaceView) -> Leaf? { + switch (self) { + case .leaf(let leaf): + if leaf.surface == view { + return leaf + } else { + return nil + } + + case .split(let container): + return container.topLeft.leaf(for: view) ?? + container.bottomRight.leaf(for: view) + } + } + // MARK: - Sequence func makeIterator() -> IndexingIterator<[Leaf]> { diff --git a/macos/Sources/Ghostty/Ghostty.TerminalSplit.swift b/macos/Sources/Ghostty/Ghostty.TerminalSplit.swift index cc3bef1492..cec1782459 100644 --- a/macos/Sources/Ghostty/Ghostty.TerminalSplit.swift +++ b/macos/Sources/Ghostty/Ghostty.TerminalSplit.swift @@ -205,6 +205,7 @@ extension Ghostty { alert.beginSheetModal(for: window, completionHandler: { response in switch (response) { case .alertFirstButtonReturn: + alert.window.orderOut(nil) node = nil default: diff --git a/macos/Sources/Ghostty/Package.swift b/macos/Sources/Ghostty/Package.swift index d091002123..18ef3f3a73 100644 --- a/macos/Sources/Ghostty/Package.swift +++ b/macos/Sources/Ghostty/Package.swift @@ -159,7 +159,7 @@ extension Ghostty { case osc_52_read /// An application is attempting to write to the clipboard using OSC 52 - case osc_52_write + case osc_52_write(OSPasteboard?) /// The text to show in the clipboard confirmation prompt for a given request type func text() -> String { @@ -188,7 +188,7 @@ extension Ghostty { case GHOSTTY_CLIPBOARD_REQUEST_OSC_52_READ: return .osc_52_read case GHOSTTY_CLIPBOARD_REQUEST_OSC_52_WRITE: - return .osc_52_write + return .osc_52_write(nil) default: return nil } @@ -198,6 +198,14 @@ extension Ghostty { /// macos-icon enum MacOSIcon: String { case official + case blueprint + case chalkboard + case glass + case holographic + case microchip + case paper + case retro + case xray case customStyle = "custom-style" } @@ -236,6 +244,9 @@ extension Notification.Name { /// Goto tab. Has tab index in the userinfo. static let ghosttyMoveTab = Notification.Name("com.mitchellh.ghostty.moveTab") static let GhosttyMoveTabKey = ghosttyMoveTab.rawValue + + /// Close tab + static let ghosttyCloseTab = Notification.Name("com.mitchellh.ghostty.closeTab") } // NOTE: I am moving all of these to Notification.Name extensions over time. This diff --git a/macos/Sources/Ghostty/SurfaceView.swift b/macos/Sources/Ghostty/SurfaceView.swift index 4abf87c7fa..beae503314 100644 --- a/macos/Sources/Ghostty/SurfaceView.swift +++ b/macos/Sources/Ghostty/SurfaceView.swift @@ -92,22 +92,6 @@ extension Ghostty { windowFocus = false } } - .onDrop(of: [.fileURL], isTargeted: nil) { providers in - providers.forEach { provider in - _ = provider.loadObject(ofClass: URL.self) { url, _ in - guard let url = url else { return } - let path = Shell.escape(url.path) - DispatchQueue.main.async { - surfaceView.insertText( - path, - replacementRange: NSMakeRange(0, 0) - ) - } - } - } - - return true - } #endif // If our geo size changed then we show the resize overlay as configured. diff --git a/macos/Sources/Ghostty/SurfaceView_AppKit.swift b/macos/Sources/Ghostty/SurfaceView_AppKit.swift index cf4357a8ca..daa2ccfde5 100644 --- a/macos/Sources/Ghostty/SurfaceView_AppKit.swift +++ b/macos/Sources/Ghostty/SurfaceView_AppKit.swift @@ -1,3 +1,4 @@ +import AppKit import SwiftUI import CoreText import UserNotifications @@ -12,7 +13,14 @@ extension Ghostty { // The current title of the surface as defined by the pty. This can be // changed with escape codes. This is public because the callbacks go // to the app level and it is set from there. - @Published private(set) var title: String = "👻" + @Published private(set) var title: String = "" { + didSet { + if !title.isEmpty { + titleFallbackTimer?.invalidate() + titleFallbackTimer = nil + } + } + } // The current pwd of the surface as defined by the pty. This can be // changed with escape codes. @@ -113,6 +121,14 @@ extension Ghostty { // A small delay that is introduced before a title change to avoid flickers private var titleChangeTimer: Timer? + // A timer to fallback to ghost emoji if no title is set within the grace period + private var titleFallbackTimer: Timer? + + // This is the title from the terminal. This is nil if we're currently using + // the terminal title as the main title property. If the title is set manually + // by the user, this is set to the prior value (which may be empty, but non-nil). + private var titleFromTerminal: String? + /// Event monitor (see individual events for why) private var eventMonitor: Any? = nil @@ -139,6 +155,13 @@ extension Ghostty { // can do SOMETHING. super.init(frame: NSMakeRect(0, 0, 800, 600)) + // Set a timer to show the ghost emoji after 500ms if no title is set + titleFallbackTimer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false) { [weak self] _ in + if let self = self, self.title.isEmpty { + self.title = "👻" + } + } + // Before we initialize the surface we want to register our notifications // so there is no window where we can't receive them. let center = NotificationCenter.default @@ -213,6 +236,9 @@ extension Ghostty { ghostty_surface_set_color_scheme(surface, scheme) } + + // The UTTypes that can be dragged onto this view. + registerForDraggedTypes(Array(Self.dropTypes)) } required init?(coder: NSCoder) { @@ -359,6 +385,45 @@ extension Ghostty { NSCursor.setHiddenUntilMouseMoves(!visible) } + /// Set the title by prompting the user. + func promptTitle() { + // Create an alert dialog + let alert = NSAlert() + alert.messageText = "Change Terminal Title" + alert.informativeText = "Leave blank to restore the default." + alert.alertStyle = .informational + + // Add a text field to the alert + let textField = NSTextField(frame: NSRect(x: 0, y: 0, width: 250, height: 24)) + textField.stringValue = title + alert.accessoryView = textField + + // Add buttons + alert.addButton(withTitle: "OK") + alert.addButton(withTitle: "Cancel") + + let response = alert.runModal() + + // Check if the user clicked "OK" + if response == .alertFirstButtonReturn { + // Get the input text + let newTitle = textField.stringValue + + if newTitle.isEmpty { + // Empty means that user wants the title to be set automatically + // We also need to reload the config for the "title" property to be + // used again by this tab. + let prevTitle = titleFromTerminal ?? "👻" + titleFromTerminal = nil + setTitle(prevTitle) + } else { + // Set the title and prevent it from being changed automatically + titleFromTerminal = title + title = newTitle + } + } + } + func setTitle(_ title: String) { // This fixes an issue where very quick changes to the title could // cause an unpleasant flickering. We set a timer so that we can @@ -369,6 +434,11 @@ extension Ghostty { withTimeInterval: 0.075, repeats: false ) { [weak self] _ in + // Set the title if it wasn't manually set. + guard self?.titleFromTerminal == nil else { + self?.titleFromTerminal = title + return + } self?.title = title } } @@ -1096,6 +1166,8 @@ extension Ghostty { menu.addItem(.separator()) menu.addItem(withTitle: "Reset Terminal", action: #selector(resetTerminal(_:)), keyEquivalent: "") menu.addItem(withTitle: "Toggle Terminal Inspector", action: #selector(toggleTerminalInspector(_:)), keyEquivalent: "") + menu.addItem(.separator()) + menu.addItem(withTitle: "Change Title...", action: #selector(changeTitle(_:)), keyEquivalent: "") return menu } @@ -1127,6 +1199,14 @@ extension Ghostty { } } + @IBAction func pasteSelection(_ sender: Any?) { + guard let surface = self.surface else { return } + let action = "paste_from_selection" + if (!ghostty_surface_binding_action(surface, action, UInt(action.count))) { + AppDelegate.logger.warning("action failed action=\(action)") + } + } + @IBAction override func selectAll(_ sender: Any?) { guard let surface = self.surface else { return } let action = "select_all" @@ -1160,6 +1240,10 @@ extension Ghostty { AppDelegate.logger.warning("action failed action=\(action)") } } + + @IBAction func changeTitle(_ sender: Any) { + promptTitle() + } /// Show a user notification and associate it with this surface func showUserNotification(title: String, body: String) { @@ -1448,3 +1532,78 @@ extension Ghostty.SurfaceView: NSServicesMenuRequestor { return true } } + +// MARK: NSMenuItemValidation + +extension Ghostty.SurfaceView: NSMenuItemValidation { + func validateMenuItem(_ item: NSMenuItem) -> Bool { + switch item.action { + case #selector(pasteSelection): + let pb = NSPasteboard.ghosttySelection + guard let str = pb.getOpinionatedStringContents() else { return false } + return !str.isEmpty + + default: + return true + } + } +} + +// MARK: NSDraggingDestination + +extension Ghostty.SurfaceView { + static let dropTypes: Set = [ + .string, + .fileURL, + .URL + ] + + override func draggingEntered(_ sender: any NSDraggingInfo) -> NSDragOperation { + guard let types = sender.draggingPasteboard.types else { return [] } + + // If the dragging object contains none of our types then we return none. + // This shouldn't happen because AppKit should guarantee that we only + // receive types we registered for but its good to check. + if Set(types).isDisjoint(with: Self.dropTypes) { + return [] + } + + // We use copy to get the proper icon + return .copy + } + + override func performDragOperation(_ sender: any NSDraggingInfo) -> Bool { + let pb = sender.draggingPasteboard + + let content: String? + if let url = pb.string(forType: .URL) { + // URLs first, they get escaped as-is. + content = Ghostty.Shell.escape(url) + } else if let urls = pb.readObjects(forClasses: [NSURL.self]) as? [URL], + urls.count > 0 { + // File URLs next. They get escaped individually and then joined by a + // space if there are multiple. + content = urls + .map { Ghostty.Shell.escape($0.path) } + .joined(separator: " ") + } else if let str = pb.string(forType: .string) { + // Strings are not escaped because they may be copy/pasting a + // command they want to execute. + content = str + } else { + content = nil + } + + if let content { + DispatchQueue.main.async { + self.insertText( + content, + replacementRange: NSMakeRange(0, 0) + ) + } + return true + } + + return false + } +} diff --git a/macos/Sources/Helpers/CrossKit.swift b/macos/Sources/Helpers/CrossKit.swift index 5a69b45a37..690e811bb5 100644 --- a/macos/Sources/Helpers/CrossKit.swift +++ b/macos/Sources/Helpers/CrossKit.swift @@ -10,6 +10,7 @@ import AppKit typealias OSView = NSView typealias OSColor = NSColor typealias OSSize = NSSize +typealias OSPasteboard = NSPasteboard protocol OSViewRepresentable: NSViewRepresentable where NSViewType == OSViewType { associatedtype OSViewType: NSView @@ -34,6 +35,7 @@ import UIKit typealias OSView = UIView typealias OSColor = UIColor typealias OSSize = CGSize +typealias OSPasteboard = UIPasteboard protocol OSViewRepresentable: UIViewRepresentable { associatedtype OSViewType: UIView diff --git a/macos/Sources/Helpers/Dock.swift b/macos/Sources/Helpers/Dock.swift new file mode 100644 index 0000000000..a71fcaa5b2 --- /dev/null +++ b/macos/Sources/Helpers/Dock.swift @@ -0,0 +1,38 @@ +import Cocoa + +// Private API to get Dock location +@_silgen_name("CoreDockGetOrientationAndPinning") +func CoreDockGetOrientationAndPinning( + _ outOrientation: UnsafeMutablePointer, + _ outPinning: UnsafeMutablePointer) + +// Private API to get the current Dock auto-hide state +@_silgen_name("CoreDockGetAutoHideEnabled") +func CoreDockGetAutoHideEnabled() -> Bool + +// Toggles the Dock's auto-hide state +@_silgen_name("CoreDockSetAutoHideEnabled") +func CoreDockSetAutoHideEnabled(_ flag: Bool) + +enum DockOrientation: Int { + case top = 1 + case bottom = 2 + case left = 3 + case right = 4 +} + +class Dock { + /// Returns the orientation of the dock or nil if it can't be determined. + static var orientation: DockOrientation? { + var orientation: Int32 = 0 + var pinning: Int32 = 0 + CoreDockGetOrientationAndPinning(&orientation, &pinning) + return .init(rawValue: Int(orientation)) ?? nil + } + + /// Set the dock autohide. + static var autoHideEnabled: Bool { + get { return CoreDockGetAutoHideEnabled() } + set { CoreDockSetAutoHideEnabled(newValue) } + } +} diff --git a/macos/Sources/Helpers/Fullscreen.swift b/macos/Sources/Helpers/Fullscreen.swift index a16f329f88..59865fc9eb 100644 --- a/macos/Sources/Helpers/Fullscreen.swift +++ b/macos/Sources/Helpers/Fullscreen.swift @@ -6,6 +6,7 @@ enum FullscreenMode { case native case nonNative case nonNativeVisibleMenu + case nonNativePaddedNotch /// Initializes the fullscreen style implementation for the mode. This will not toggle any /// fullscreen properties. This may fail if the window isn't configured properly for a given @@ -20,6 +21,9 @@ enum FullscreenMode { case .nonNativeVisibleMenu: return NonNativeFullscreenVisibleMenu(window) + + case .nonNativePaddedNotch: + return NonNativeFullscreenPaddedNotch(window) } } } @@ -141,6 +145,7 @@ class NonNativeFullscreen: FullscreenBase, FullscreenStyle { struct Properties { var hideMenu: Bool = true + var paddedNotch: Bool = false } private var savedState: SavedState? @@ -278,6 +283,9 @@ class NonNativeFullscreen: FullscreenBase, FullscreenStyle { // put an #available check, but it was in a bug fix release so I think // if a bug is reported to Ghostty we can just advise the user to // update. + } else if (properties.paddedNotch) { + // We are hiding the menu, we may need to avoid the notch. + frame.size.height -= screen.safeAreaInsets.top } return frame @@ -307,21 +315,21 @@ class NonNativeFullscreen: FullscreenBase, FullscreenStyle { // MARK: Dock private func hideDock() { - NSApp.presentationOptions.insert(.autoHideDock) + NSApp.acquirePresentationOption(.autoHideDock) } private func unhideDock() { - NSApp.presentationOptions.remove(.autoHideDock) + NSApp.releasePresentationOption(.autoHideDock) } // MARK: Menu func hideMenu() { - NSApp.presentationOptions.insert(.autoHideMenuBar) + NSApp.acquirePresentationOption(.autoHideMenuBar) } func unhideMenu() { - NSApp.presentationOptions.remove(.autoHideMenuBar) + NSApp.releasePresentationOption(.autoHideMenuBar) } /// The state that must be saved for non-native fullscreen to exit fullscreen. @@ -349,3 +357,7 @@ class NonNativeFullscreen: FullscreenBase, FullscreenStyle { class NonNativeFullscreenVisibleMenu: NonNativeFullscreen { override var properties: Properties { Properties(hideMenu: false) } } + +class NonNativeFullscreenPaddedNotch: NonNativeFullscreen { + override var properties: Properties { Properties(paddedNotch: true) } +} diff --git a/macos/Sources/Helpers/LastWindowPosition.swift b/macos/Sources/Helpers/LastWindowPosition.swift new file mode 100644 index 0000000000..a0dfa90dda --- /dev/null +++ b/macos/Sources/Helpers/LastWindowPosition.swift @@ -0,0 +1,34 @@ +import Cocoa + +/// Manages the persistence and restoration of window positions across app launches. +class LastWindowPosition { + static let shared = LastWindowPosition() + + private let positionKey = "NSWindowLastPosition" + + func save(_ window: NSWindow) { + let origin = window.frame.origin + let point = [origin.x, origin.y] + UserDefaults.standard.set(point, forKey: positionKey) + } + + func restore(_ window: NSWindow) -> Bool { + guard let points = UserDefaults.standard.array(forKey: positionKey) as? [Double], + points.count == 2 else { return false } + + let lastPosition = CGPoint(x: points[0], y: points[1]) + + guard let screen = window.screen ?? NSScreen.main else { return false } + let visibleFrame = screen.visibleFrame + + var newFrame = window.frame + newFrame.origin = lastPosition + if !visibleFrame.contains(newFrame.origin) { + newFrame.origin.x = max(visibleFrame.minX, min(visibleFrame.maxX - newFrame.width, newFrame.origin.x)) + newFrame.origin.y = max(visibleFrame.minY, min(visibleFrame.maxY - newFrame.height, newFrame.origin.y)) + } + + window.setFrame(newFrame, display: true) + return true + } +} diff --git a/macos/Sources/Helpers/NSApplication+Extension.swift b/macos/Sources/Helpers/NSApplication+Extension.swift new file mode 100644 index 0000000000..0580cd5fc7 --- /dev/null +++ b/macos/Sources/Helpers/NSApplication+Extension.swift @@ -0,0 +1,31 @@ +import Cocoa + +extension NSApplication { + private static var presentationOptionCounts: [NSApplication.PresentationOptions.Element: UInt] = [:] + + /// Add a presentation option to the application and main a reference count so that and equal + /// number of pops is required to disable it. This is useful so that multiple classes can affect global + /// app state without overriding others. + func acquirePresentationOption(_ option: NSApplication.PresentationOptions.Element) { + Self.presentationOptionCounts[option, default: 0] += 1 + presentationOptions.insert(option) + } + + /// See acquirePresentationOption + func releasePresentationOption(_ option: NSApplication.PresentationOptions.Element) { + guard let value = Self.presentationOptionCounts[option] else { return } + guard value > 0 else { return } + if (value == 1) { + presentationOptions.remove(option) + Self.presentationOptionCounts.removeValue(forKey: option) + } else { + Self.presentationOptionCounts[option] = value - 1 + } + } +} + +extension NSApplication.PresentationOptions.Element: @retroactive Hashable { + public func hash(into hasher: inout Hasher) { + hasher.combine(rawValue) + } +} diff --git a/macos/Sources/Helpers/NSPasteboard+Extension.swift b/macos/Sources/Helpers/NSPasteboard+Extension.swift index b1755fea01..11815fbc87 100644 --- a/macos/Sources/Helpers/NSPasteboard+Extension.swift +++ b/macos/Sources/Helpers/NSPasteboard+Extension.swift @@ -1,17 +1,39 @@ import AppKit +import GhosttyKit extension NSPasteboard { + /// The pasteboard to used for Ghostty selection. + static var ghosttySelection: NSPasteboard = { + NSPasteboard(name: .init("com.mitchellh.ghostty.selection")) + }() + /// Gets the contents of the pasteboard as a string following a specific set of semantics. /// Does these things in order: - /// - Tries to get the absolute filesystem path of the file in the pasteboard if there is one. + /// - Tries to get the absolute filesystem path of the file in the pasteboard if there is one and ensures the file path is properly escaped. /// - Tries to get any string from the pasteboard. /// If all of the above fail, returns None. func getOpinionatedStringContents() -> String? { - if let file = self.string(forType: .fileURL) { - if let path = NSURL(string: file)?.path { - return path - } + if let urls = readObjects(forClasses: [NSURL.self]) as? [URL], + urls.count > 0 { + return urls + .map { $0.isFileURL ? Ghostty.Shell.escape($0.path) : $0.absoluteString } + .joined(separator: " ") } + return self.string(forType: .string) } + + /// The pasteboard for the Ghostty enum type. + static func ghostty(_ clipboard: ghostty_clipboard_e) -> NSPasteboard? { + switch (clipboard) { + case GHOSTTY_CLIPBOARD_STANDARD: + return Self.general + + case GHOSTTY_CLIPBOARD_SELECTION: + return Self.ghosttySelection + + default: + return nil + } + } } diff --git a/macos/Sources/Helpers/Weak.swift b/macos/Sources/Helpers/Weak.swift new file mode 100644 index 0000000000..d5f784844f --- /dev/null +++ b/macos/Sources/Helpers/Weak.swift @@ -0,0 +1,9 @@ +/// A wrapper that holds a weak reference to an object. This lets us create native containers +/// of weak references. +class Weak { + weak var value: T? + + init(_ value: T) { + self.value = value + } +} diff --git a/nix/build-support/check-zig-cache-hash.sh b/nix/build-support/check-zig-cache-hash.sh deleted file mode 100755 index 49ea29ffbf..0000000000 --- a/nix/build-support/check-zig-cache-hash.sh +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env bash - -# Nothing in this script should fail. -set -e - -CACHE_HASH_FILE="$(realpath "$(dirname "$0")/../zigCacheHash.nix")" - -help() { - echo "" - echo "To fix, please (manually) re-run the script from the repository root," - echo "commit, and push the update:" - echo "" - echo " ./nix/build-support/check-zig-cache-hash.sh --update" - echo " git add nix/zigCacheHash.nix" - echo " git commit -m \"nix: update Zig cache hash\"" - echo " git push" - echo "" -} - -if [ -f "${CACHE_HASH_FILE}" ]; then - OLD_CACHE_HASH="$(nix eval --raw --file "${CACHE_HASH_FILE}")" -elif [ "$1" != "--update" ]; then - echo -e "\nERROR: Zig cache hash file missing." - help - exit 1 -fi - -ZIG_GLOBAL_CACHE_DIR="$(mktemp --directory --suffix nix-zig-cache)" -export ZIG_GLOBAL_CACHE_DIR - -# This is not 100% necessary in CI but is helpful when running locally to keep -# a local workstation clean. -trap 'rm -rf "${ZIG_GLOBAL_CACHE_DIR}"' EXIT - -# Run Zig and download the cache to the temporary directory. - -sh ./nix/build-support/fetch-zig-cache.sh - -# Now, calculate the hash. -ZIG_CACHE_HASH="sha256-$(nix-hash --type sha256 --to-base64 "$(nix-hash --type sha256 "${ZIG_GLOBAL_CACHE_DIR}")")" - -if [ "${OLD_CACHE_HASH}" == "${ZIG_CACHE_HASH}" ]; then - echo -e "\nOK: Zig cache store hash unchanged." - exit 0 -elif [ "$1" != "--update" ]; then - echo -e "\nERROR: The Zig cache store hash has updated." - echo "" - echo " * Old hash: ${OLD_CACHE_HASH}" - echo " * New hash: ${ZIG_CACHE_HASH}" - help - exit 1 -else - echo -e "\nNew Zig cache store hash: ${ZIG_CACHE_HASH}" -fi - -# Write out the cache file -cat > "${CACHE_HASH_FILE}" < "$WORK_DIR/build.zig.zon.nix" +alejandra --quiet "$WORK_DIR/build.zig.zon.nix" + +NEW_HASH=$(sha512sum "$WORK_DIR/build.zig.zon.nix" | awk '{print $1}') + +if [ "${OLD_HASH}" == "${NEW_HASH}" ]; then + echo -e "\nOK: build.zig.zon.nix unchanged." + exit 0 +elif [ "$1" != "--update" ]; then + echo -e "\nERROR: build.zig.zon.nix needs to be updated." + echo "" + echo " * Old hash: ${OLD_HASH}" + echo " * New hash: ${NEW_HASH}" + help + exit 1 +else + jq -r '.[] .url' "$BUILD_ZIG_ZON_LOCK" | sort > "$BUILD_ZIG_ZON_TXT" + mv "$WORK_DIR/build.zig.zon.nix" "$BUILD_ZIG_ZON_NIX" + echo -e "\nOK: build.zig.zon.nix updated." + exit 0 +fi + diff --git a/nix/build-support/fetch-zig-cache.sh b/nix/build-support/fetch-zig-cache.sh index 56b94e35df..8ff00cad0d 100755 --- a/nix/build-support/fetch-zig-cache.sh +++ b/nix/build-support/fetch-zig-cache.sh @@ -1,32 +1,13 @@ #!/bin/sh -set -e - -# Because Zig does not fetch recursive dependencies when you run `zig build -# --fetch` (see https://github.com/ziglang/zig/issues/20976) we need to do some -# extra work to fetch everything that we actually need to build without Internet -# access (such as when building a Nix package). -# -# An example of this happening: -# -# error: builder for '/nix/store/cx8qcwrhjmjxik2547fw99v5j6np5san-ghostty-0.1.0.drv' failed with exit code 1; -# la/build/tmp.xgHOheUF7V/p/12208cfdda4d5fdbc81b0c44b82e4d6dba2d4a86bff644a153e026fdfc80f8469133/build.zig.zon:7:20: error: unable to discover remote git server capabilities: TemporaryNameServerFailure -# > .url = "git+https://github.com/zigimg/zigimg#3a667bdb3d7f0955a5a51c8468eac83210c1439e", -# > ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# > /build/tmp.xgHOheUF7V/p/12208cfdda4d5fdbc81b0c44b82e4d6dba2d4a86bff644a153e026fdfc80f8469133/build.zig.zon:16:20: error: unable to discover remote git server capabilities: TemporaryNameServerFailure -# > .url = "git+https://github.com/mitchellh/libxev#f6a672a78436d8efee1aa847a43a900ad773618b", -# > ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# > -# For full logs, run 'nix log /nix/store/cx8qcwrhjmjxik2547fw99v5j6np5san-ghostty-0.1.0.drv'. -# -# To update this script, add any failing URLs with a line like this: -# -# zig fetch +# NOTE THIS IS A TEMPORARY SCRIPT TO SUPPORT PACKAGE MAINTAINERS. # -# Periodically old URLs may need to be cleaned out. +# A future Zig version will hopefully fix the issue where +# `zig build --fetch` doesn't fetch transitive dependencies[1]. When that +# is resolved, we won't need any special machinery for the general use case +# at all and packagers can just use `zig build --fetch`. # -# Hopefully when the Zig issue is fixed this script can be eliminated in favor -# of a plain `zig build --fetch`. +# [1]: https://github.com/ziglang/zig/issues/20976 if [ -z ${ZIG_GLOBAL_CACHE_DIR+x} ] then @@ -34,6 +15,13 @@ then exit 1 fi -zig build --fetch -zig fetch git+https://github.com/zigimg/zigimg#3a667bdb3d7f0955a5a51c8468eac83210c1439e -zig fetch git+https://github.com/mitchellh/libxev#f6a672a78436d8efee1aa847a43a900ad773618b +# Go through each line of our build.zig.zon.txt and fetch it. +SCRIPT_PATH="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)" +ZON_TXT_FILE="$SCRIPT_PATH/../../build.zig.zon.txt" +while IFS= read -r url; do + echo "Fetching: $url" + zig fetch "$url" >/dev/null 2>&1 || { + echo "Failed to fetch: $url" >&2 + exit 1 + } +done < "$ZON_TXT_FILE" diff --git a/nix/build-support/update-mirror.sh b/nix/build-support/update-mirror.sh new file mode 100755 index 0000000000..35fd841e2e --- /dev/null +++ b/nix/build-support/update-mirror.sh @@ -0,0 +1,30 @@ +#!/bin/sh +# +# This script generates a directory that can be uploaded to blob +# storage to mirror our dependencies. The dependencies are unmodified +# so their checksum and content hashes will match. + +set -e # Exit immediately if a command exits with a non-zero status + +SCRIPT_PATH="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)" +INPUT_FILE="$SCRIPT_PATH/../../build.zig.zon2json-lock" +OUTPUT_DIR="blob" + +# Ensure the output directory exists +mkdir -p "$OUTPUT_DIR" + +# Use jq to iterate over the JSON and download files +jq -r 'to_entries[] | "\(.key) \(.value.name) \(.value.url)"' "$INPUT_FILE" | while read -r key name url; do + # Skip URLs that don't start with http(s). They aren't necessary for + # our mirror. + if ! echo "$url" | grep -Eq "^https?://"; then + continue + fi + + # Extract the file extension from the URL + extension=$(echo "$url" | grep -oE '\.[a-z0-9]+(\.[a-z0-9]+)?$') + + filename="${name}-${key}${extension}" + echo "$url -> $filename" + curl -L -o "$OUTPUT_DIR/$filename" "$url" +done diff --git a/nix/devShell.nix b/nix/devShell.nix index c52afb6c0c..66f2596566 100644 --- a/nix/devShell.nix +++ b/nix/devShell.nix @@ -14,6 +14,7 @@ python3, qemu, scdoc, + snapcraft, valgrind, #, vulkan-loader # unused vttest, @@ -30,7 +31,9 @@ glib, glslang, gtk4, + gobject-introspection, libadwaita, + blueprint-compiler, adwaita-icon-theme, hicolor-icon-theme, harfbuzz, @@ -47,6 +50,7 @@ simdutf, zlib, alejandra, + jq, minisign, pandoc, hyperfine, @@ -54,6 +58,8 @@ wayland, wayland-scanner, wayland-protocols, + zig2nix, + system, }: let # See package.nix. Keep in sync. rpathLibs = @@ -83,6 +89,7 @@ libadwaita gtk4 glib + gobject-introspection wayland ]; in @@ -92,6 +99,7 @@ in packages = [ # For builds + jq llvmPackages_latest.llvm minisign ncurses @@ -100,6 +108,7 @@ in scdoc zig zip + zig2nix.packages.${system}.zon2nix # For web and wasm stuff nodejs @@ -129,6 +138,7 @@ in qemu gdb + snapcraft valgrind wraptest @@ -154,9 +164,11 @@ in libXrandr # Only needed for GTK builds + blueprint-compiler libadwaita gtk4 glib + gobject-introspection wayland wayland-scanner wayland-protocols diff --git a/nix/package.nix b/nix/package.nix index 2f7825a562..45f189cf5b 100644 --- a/nix/package.nix +++ b/nix/package.nix @@ -2,6 +2,7 @@ lib, stdenv, bzip2, + callPackage, expat, fontconfig, freetype, @@ -12,7 +13,9 @@ libGL, glib, gtk4, + gobject-introspection, libadwaita, + blueprint-compiler, wrapGAppsHook4, gsettings-desktop-schemas, git, @@ -40,82 +43,36 @@ # ultimately acted on and has made its way to a nixpkgs implementation, this # can probably be removed in favor of that. zig_hook = zig_0_13.hook.overrideAttrs { - zig_default_flags = "-Dcpu=baseline -Doptimize=${optimize}"; - }; - - # We limit source like this to try and reduce the amount of rebuilds as possible - # thus we only provide the source that is needed for the build - # - # NOTE: as of the current moment only linux files are provided, - # since darwin support is not finished - src = lib.fileset.toSource { - root = ../.; - fileset = lib.fileset.intersection (lib.fileset.fromSource (lib.sources.cleanSource ../.)) ( - lib.fileset.unions [ - ../dist/linux - ../images - ../include - ../pkg - ../src - ../vendor - ../build.zig - ../build.zig.zon - ./build-support/fetch-zig-cache.sh - ] - ); - }; - - # This hash is the computation of the zigCache fixed-output derivation. This - # allows us to use remote package dependencies without breaking the sandbox. - # - # This will need updating whenever dependencies get updated (e.g. changes are - # made to zig.build.zon). If you see that the main build is trying to reach - # out to the internet and failing, this is likely the cause. Change this - # value back to lib.fakeHash, and re-run. The build failure should emit the - # updated hash, which of course, should be validated before updating here. - # - # (It's also possible that you might see a hash mismatch - without the - # network errors - if you don't have a previous instance of the cache - # derivation in your store already. If so, just update the value as above.) - zigCacheHash = import ./zigCacheHash.nix; - - zigCache = stdenv.mkDerivation { - inherit src; - name = "ghostty-cache"; - nativeBuildInputs = [ - git - zig_hook - ]; - - dontConfigure = true; - dontUseZigBuild = true; - dontUseZigInstall = true; - dontFixup = true; - - buildPhase = '' - runHook preBuild - - sh ./nix/build-support/fetch-zig-cache.sh - - runHook postBuild - ''; - - installPhase = '' - runHook preInstall - - cp -r --reflink=auto $ZIG_GLOBAL_CACHE_DIR $out - - runHook postInstall - ''; - - outputHashMode = "recursive"; - outputHash = zigCacheHash; + zig_default_flags = "-Dcpu=baseline -Doptimize=${optimize} --color off"; }; in stdenv.mkDerivation (finalAttrs: { pname = "ghostty"; - version = "1.0.2"; - inherit src; + version = "1.1.3"; + + # We limit source like this to try and reduce the amount of rebuilds as possible + # thus we only provide the source that is needed for the build + # + # NOTE: as of the current moment only linux files are provided, + # since darwin support is not finished + src = lib.fileset.toSource { + root = ../.; + fileset = lib.fileset.intersection (lib.fileset.fromSource (lib.sources.cleanSource ../.)) ( + lib.fileset.unions [ + ../dist/linux + ../images + ../include + ../pkg + ../src + ../vendor + ../build.zig + ../build.zig.zon + ../build.zig.zon.nix + ] + ); + }; + + deps = callPackage ../build.zig.zon.nix {name = "ghostty-cache-${finalAttrs.version}";}; nativeBuildInputs = [ @@ -124,7 +81,9 @@ in pandoc pkg-config zig_hook + gobject-introspection wrapGAppsHook4 + blueprint-compiler ] ++ lib.optionals enableWayland [ wayland-scanner @@ -162,13 +121,13 @@ in dontConfigure = true; - zigBuildFlags = "-Dversion-string=${finalAttrs.version}-${revision}-nix -Dgtk-x11=${lib.boolToString enableX11} -Dgtk-wayland=${lib.boolToString enableWayland}"; - - preBuild = '' - rm -rf $ZIG_GLOBAL_CACHE_DIR - cp -r --reflink=auto ${zigCache} $ZIG_GLOBAL_CACHE_DIR - chmod u+rwX -R $ZIG_GLOBAL_CACHE_DIR - ''; + zigBuildFlags = [ + "--system" + "${finalAttrs.deps}" + "-Dversion-string=${finalAttrs.version}-${revision}-nix" + "-Dgtk-x11=${lib.boolToString enableX11}" + "-Dgtk-wayland=${lib.boolToString enableWayland}" + ]; outputs = [ "out" @@ -202,7 +161,7 @@ in ''; meta = { - homepage = "https://github.com/ghostty-org/ghostty"; + homepage = "https://ghostty.org"; license = lib.licenses.mit; platforms = [ "x86_64-linux" diff --git a/nix/vm/common-cinnamon.nix b/nix/vm/common-cinnamon.nix new file mode 100644 index 0000000000..dabe5e7018 --- /dev/null +++ b/nix/vm/common-cinnamon.nix @@ -0,0 +1,18 @@ +{...}: { + imports = [ + ./common.nix + ]; + + services.xserver = { + displayManager = { + lightdm = { + enable = true; + }; + }; + desktopManager = { + cinnamon = { + enable = true; + }; + }; + }; +} diff --git a/nix/vm/common-gnome.nix b/nix/vm/common-gnome.nix new file mode 100644 index 0000000000..0c2bef150c --- /dev/null +++ b/nix/vm/common-gnome.nix @@ -0,0 +1,136 @@ +{ + config, + lib, + pkgs, + ... +}: { + imports = [ + ./common.nix + ]; + + services.xserver = { + displayManager = { + gdm = { + enable = true; + autoSuspend = false; + }; + }; + desktopManager = { + gnome = { + enable = true; + }; + }; + }; + + environment.systemPackages = [ + pkgs.gnomeExtensions.no-overview + ]; + + environment.gnome.excludePackages = with pkgs; [ + atomix + baobab + cheese + epiphany + evince + file-roller + geary + gnome-backgrounds + gnome-calculator + gnome-calendar + gnome-clocks + gnome-connections + gnome-contacts + gnome-disk-utility + gnome-extension-manager + gnome-logs + gnome-maps + gnome-music + gnome-photos + gnome-software + gnome-system-monitor + gnome-text-editor + gnome-themes-extra + gnome-tour + gnome-user-docs + gnome-weather + hitori + iagno + loupe + nautilus + orca + seahorse + simple-scan + snapshot + sushi + tali + totem + yelp + ]; + + programs.dconf = { + enable = true; + profiles.user.databases = [ + { + settings = with lib.gvariant; { + "org/gnome/desktop/background" = { + picture-uri = "file://${pkgs.ghostty}/share/icons/hicolor/512x512/apps/com.mitchellh.ghostty.png"; + picture-uri-dark = "file://${pkgs.ghostty}/share/icons/hicolor/512x512/apps/com.mitchellh.ghostty.png"; + picture-options = "centered"; + primary-color = "#000000000000"; + secondary-color = "#000000000000"; + }; + "org/gnome/desktop/interface" = { + color-scheme = "prefer-dark"; + }; + "org/gnome/desktop/notifications" = { + show-in-lock-screen = false; + }; + "org/gnome/desktop/screensaver" = { + lock-enabled = false; + picture-uri = "file://${pkgs.ghostty}/share/icons/hicolor/512x512/apps/com.mitchellh.ghostty.png"; + picture-options = "centered"; + primary-color = "#000000000000"; + secondary-color = "#000000000000"; + }; + "org/gnome/desktop/session" = { + idle-delay = mkUint32 0; + }; + "org/gnome/shell" = { + disable-user-extensions = false; + enabled-extensions = builtins.map (x: x.extensionUuid) ( + lib.filter (p: p ? extensionUuid) config.environment.systemPackages + ); + }; + }; + } + ]; + }; + + programs.geary.enable = false; + + services.gnome = { + gnome-browser-connector.enable = false; + gnome-initial-setup.enable = false; + gnome-online-accounts.enable = false; + gnome-remote-desktop.enable = false; + rygel.enable = false; + }; + + system.activationScripts = { + face = { + text = '' + mkdir -p /var/lib/AccountsService/{icons,users} + + cp ${pkgs.ghostty}/share/icons/hicolor/1024x1024/apps/com.mitchellh.ghostty.png /var/lib/AccountsService/icons/ghostty + + echo -e "[User]\nIcon=/var/lib/AccountsService/icons/ghostty\n" > /var/lib/AccountsService/users/ghostty + + chown root:root /var/lib/AccountsService/users/ghostty + chmod 0600 /var/lib/AccountsService/users/ghostty + + chown root:root /var/lib/AccountsService/icons/ghostty + chmod 0444 /var/lib/AccountsService/icons/ghostty + ''; + }; + }; +} diff --git a/nix/vm/common-plasma6.nix b/nix/vm/common-plasma6.nix new file mode 100644 index 0000000000..e5c9bd4d87 --- /dev/null +++ b/nix/vm/common-plasma6.nix @@ -0,0 +1,21 @@ +{...}: { + imports = [ + ./common.nix + ]; + + services = { + displayManager = { + sddm = { + enable = true; + wayland = { + enable = true; + }; + }; + }; + desktopManager = { + plasma6 = { + enable = true; + }; + }; + }; +} diff --git a/nix/vm/common-xfce.nix b/nix/vm/common-xfce.nix new file mode 100644 index 0000000000..12a20d8d86 --- /dev/null +++ b/nix/vm/common-xfce.nix @@ -0,0 +1,18 @@ +{...}: { + imports = [ + ./common.nix + ]; + + services.xserver = { + displayManager = { + lightdm = { + enable = true; + }; + }; + desktopManager = { + xfce = { + enable = true; + }; + }; + }; +} diff --git a/nix/vm/common.nix b/nix/vm/common.nix new file mode 100644 index 0000000000..eefd7c1c03 --- /dev/null +++ b/nix/vm/common.nix @@ -0,0 +1,83 @@ +{pkgs, ...}: { + boot.loader.systemd-boot.enable = true; + boot.loader.efi.canTouchEfiVariables = true; + + documentation.nixos.enable = false; + + networking.hostName = "ghostty"; + networking.domain = "mitchellh.com"; + + virtualisation.vmVariant = { + virtualisation.memorySize = 2048; + }; + + nix = { + settings = { + trusted-users = [ + "root" + "ghostty" + ]; + }; + extraOptions = '' + experimental-features = nix-command flakes + ''; + }; + + users.mutableUsers = false; + + users.groups.ghostty = {}; + + users.users.ghostty = { + description = "Ghostty"; + group = "ghostty"; + extraGroups = ["wheel"]; + isNormalUser = true; + initialPassword = "ghostty"; + }; + + environment.etc = { + "xdg/autostart/com.mitchellh.ghostty.desktop" = { + source = "${pkgs.ghostty}/share/applications/com.mitchellh.ghostty.desktop"; + }; + }; + + environment.systemPackages = [ + pkgs.kitty + pkgs.fish + pkgs.ghostty + pkgs.helix + pkgs.neovim + pkgs.xterm + pkgs.zsh + ]; + + security.polkit = { + enable = true; + }; + + services.dbus = { + enable = true; + }; + + services.displayManager = { + autoLogin = { + user = "ghostty"; + }; + }; + + services.libinput = { + enable = true; + }; + + services.qemuGuest = { + enable = true; + }; + + services.spice-vdagentd = { + enable = true; + }; + + services.xserver = { + enable = true; + }; +} diff --git a/nix/vm/create-cinnamon.nix b/nix/vm/create-cinnamon.nix new file mode 100644 index 0000000000..a9d9e44d77 --- /dev/null +++ b/nix/vm/create-cinnamon.nix @@ -0,0 +1,12 @@ +{ + system, + nixpkgs, + overlay, + module, + uid ? 1000, + gid ? 1000, +}: +import ./create.nix { + inherit system nixpkgs overlay module uid gid; + common = ./common-cinnamon.nix; +} diff --git a/nix/vm/create-gnome.nix b/nix/vm/create-gnome.nix new file mode 100644 index 0000000000..bcd31f2b63 --- /dev/null +++ b/nix/vm/create-gnome.nix @@ -0,0 +1,12 @@ +{ + system, + nixpkgs, + overlay, + module, + uid ? 1000, + gid ? 1000, +}: +import ./create.nix { + inherit system nixpkgs overlay module uid gid; + common = ./common-gnome.nix; +} diff --git a/nix/vm/create-plasma6.nix b/nix/vm/create-plasma6.nix new file mode 100644 index 0000000000..ede5371f34 --- /dev/null +++ b/nix/vm/create-plasma6.nix @@ -0,0 +1,12 @@ +{ + system, + nixpkgs, + overlay, + module, + uid ? 1000, + gid ? 1000, +}: +import ./create.nix { + inherit system nixpkgs overlay module uid gid; + common = ./common-plasma6.nix; +} diff --git a/nix/vm/create-xfce.nix b/nix/vm/create-xfce.nix new file mode 100644 index 0000000000..d1789472d4 --- /dev/null +++ b/nix/vm/create-xfce.nix @@ -0,0 +1,12 @@ +{ + system, + nixpkgs, + overlay, + module, + uid ? 1000, + gid ? 1000, +}: +import ./create.nix { + inherit system nixpkgs overlay module uid gid; + common = ./common-xfce.nix; +} diff --git a/nix/vm/create.nix b/nix/vm/create.nix new file mode 100644 index 0000000000..f8fe8500da --- /dev/null +++ b/nix/vm/create.nix @@ -0,0 +1,42 @@ +{ + system, + nixpkgs, + overlay, + module, + common ? ./common.nix, + uid ? 1000, + gid ? 1000, +}: let + pkgs = import nixpkgs { + inherit system; + overlays = [ + overlay + ]; + }; +in + nixpkgs.lib.nixosSystem { + system = builtins.replaceStrings ["darwin"] ["linux"] system; + modules = [ + { + virtualisation.vmVariant = { + virtualisation.host.pkgs = pkgs; + }; + + nixpkgs.overlays = [ + overlay + ]; + + users.groups.ghostty = { + gid = gid; + }; + + users.users.ghostty = { + uid = uid; + }; + + system.stateVersion = nixpkgs.lib.trivial.release; + } + common + module + ]; + } diff --git a/nix/vm/wayland-cinnamon.nix b/nix/vm/wayland-cinnamon.nix new file mode 100644 index 0000000000..531c882b64 --- /dev/null +++ b/nix/vm/wayland-cinnamon.nix @@ -0,0 +1,7 @@ +{...}: { + imports = [ + ./common-cinnamon.nix + ]; + + services.displayManager.defaultSession = "cinnamon-wayland"; +} diff --git a/nix/vm/wayland-gnome.nix b/nix/vm/wayland-gnome.nix new file mode 100644 index 0000000000..eb277d5d1b --- /dev/null +++ b/nix/vm/wayland-gnome.nix @@ -0,0 +1,9 @@ +{...}: { + imports = [ + ./common-gnome.nix + ]; + + services.displayManager = { + defaultSession = "gnome"; + }; +} diff --git a/nix/vm/wayland-plasma6.nix b/nix/vm/wayland-plasma6.nix new file mode 100644 index 0000000000..6e5a253b89 --- /dev/null +++ b/nix/vm/wayland-plasma6.nix @@ -0,0 +1,6 @@ +{...}: { + imports = [ + ./common-plasma6.nix + ]; + services.displayManager.defaultSession = "plasma"; +} diff --git a/nix/vm/x11-cinnamon.nix b/nix/vm/x11-cinnamon.nix new file mode 100644 index 0000000000..636f235a2c --- /dev/null +++ b/nix/vm/x11-cinnamon.nix @@ -0,0 +1,7 @@ +{...}: { + imports = [ + ./common-cinnamon.nix + ]; + + services.displayManager.defaultSession = "cinnamon"; +} diff --git a/nix/vm/x11-gnome.nix b/nix/vm/x11-gnome.nix new file mode 100644 index 0000000000..1994aea82f --- /dev/null +++ b/nix/vm/x11-gnome.nix @@ -0,0 +1,9 @@ +{...}: { + imports = [ + ./common-gnome.nix + ]; + + services.displayManager = { + defaultSession = "gnome-xorg"; + }; +} diff --git a/nix/vm/x11-plasma6.nix b/nix/vm/x11-plasma6.nix new file mode 100644 index 0000000000..7818a80ca1 --- /dev/null +++ b/nix/vm/x11-plasma6.nix @@ -0,0 +1,6 @@ +{...}: { + imports = [ + ./common-plasma6.nix + ]; + services.displayManager.defaultSession = "plasmax11"; +} diff --git a/nix/vm/x11-xfce.nix b/nix/vm/x11-xfce.nix new file mode 100644 index 0000000000..71eb87f2fb --- /dev/null +++ b/nix/vm/x11-xfce.nix @@ -0,0 +1,7 @@ +{...}: { + imports = [ + ./common-xfce.nix + ]; + + services.displayManager.defaultSession = "xfce"; +} diff --git a/nix/zigCacheHash.nix b/nix/zigCacheHash.nix index 3806c64c9d..12f855e5f1 100644 --- a/nix/zigCacheHash.nix +++ b/nix/zigCacheHash.nix @@ -1,3 +1,3 @@ # This file is auto-generated! check build-support/check-zig-cache-hash.sh for # more details. -"sha256-PnfSy793kcVt85q47kWR0xkivXoMOZAAmuUyKO9vqAI=" +"sha256-S8kS+gO17dl9LJGKL1+kgDUre+vPTmdTvXzgc585Fl8=" diff --git a/pkg/cimgui/build.zig.zon b/pkg/cimgui/build.zig.zon index 2f2c9cfa02..d5d90b1327 100644 --- a/pkg/cimgui/build.zig.zon +++ b/pkg/cimgui/build.zig.zon @@ -6,7 +6,8 @@ // This should be kept in sync with the submodule in the cimgui source // code in ./vendor/ to be safe that they're compatible. .imgui = .{ - .url = "https://github.com/ocornut/imgui/archive/e391fe2e66eb1c96b1624ae8444dc64c23146ef4.tar.gz", + // ocornut/imgui + .url = "https://deps.files.ghostty.org/imgui-1220bc6b9daceaf7c8c60f3c3998058045ba0c5c5f48ae255ff97776d9cd8bfc6402.tar.gz", .hash = "1220bc6b9daceaf7c8c60f3c3998058045ba0c5c5f48ae255ff97776d9cd8bfc6402", }, diff --git a/pkg/freetype/build.zig.zon b/pkg/freetype/build.zig.zon index 5c6538fd57..581666dd9d 100644 --- a/pkg/freetype/build.zig.zon +++ b/pkg/freetype/build.zig.zon @@ -3,8 +3,9 @@ .version = "2.13.2", .paths = .{""}, .dependencies = .{ + // freetype/freetype .freetype = .{ - .url = "https://github.com/freetype/freetype/archive/refs/tags/VER-2-13-2.tar.gz", + .url = "https://deps.files.ghostty.org/freetype-1220b81f6ecfb3fd222f76cf9106fecfa6554ab07ec7fdc4124b9bb063ae2adf969d.tar.gz", .hash = "1220b81f6ecfb3fd222f76cf9106fecfa6554ab07ec7fdc4124b9bb063ae2adf969d", }, diff --git a/pkg/glslang/build.zig.zon b/pkg/glslang/build.zig.zon index f80552d4fd..091276502c 100644 --- a/pkg/glslang/build.zig.zon +++ b/pkg/glslang/build.zig.zon @@ -3,8 +3,9 @@ .version = "14.2.0", .paths = .{""}, .dependencies = .{ + // KhronosGroup/glslang .glslang = .{ - .url = "https://github.com/KhronosGroup/glslang/archive/refs/tags/14.2.0.tar.gz", + .url = "https://deps.files.ghostty.org/glslang-12201278a1a05c0ce0b6eb6026c65cd3e9247aa041b1c260324bf29cee559dd23ba1.tar.gz", .hash = "12201278a1a05c0ce0b6eb6026c65cd3e9247aa041b1c260324bf29cee559dd23ba1", }, diff --git a/pkg/harfbuzz/build.zig.zon b/pkg/harfbuzz/build.zig.zon index dc036f09d4..41f2b4e4f8 100644 --- a/pkg/harfbuzz/build.zig.zon +++ b/pkg/harfbuzz/build.zig.zon @@ -3,8 +3,9 @@ .version = "8.4.0", .paths = .{""}, .dependencies = .{ + // harfbuzz/harfbuzz .harfbuzz = .{ - .url = "https://github.com/harfbuzz/harfbuzz/archive/refs/tags/8.4.0.tar.gz", + .url = "https://deps.files.ghostty.org/harfbuzz-1220b8588f106c996af10249bfa092c6fb2f35fbacb1505ef477a0b04a7dd1063122.tar.gz", .hash = "1220b8588f106c996af10249bfa092c6fb2f35fbacb1505ef477a0b04a7dd1063122", }, diff --git a/pkg/highway/build.zig.zon b/pkg/highway/build.zig.zon index c8bc405f84..da631f93df 100644 --- a/pkg/highway/build.zig.zon +++ b/pkg/highway/build.zig.zon @@ -3,8 +3,9 @@ .version = "1.1.0", .paths = .{""}, .dependencies = .{ + // google/highway .highway = .{ - .url = "https://github.com/google/highway/archive/refs/tags/1.1.0.tar.gz", + .url = "https://deps.files.ghostty.org/highway-12205c83b8311a24b1d5ae6d21640df04f4b0726e314337c043cde1432758cbe165b.tar.gz", .hash = "12205c83b8311a24b1d5ae6d21640df04f4b0726e314337c043cde1432758cbe165b", }, diff --git a/pkg/libpng/build.zig.zon b/pkg/libpng/build.zig.zon index dc9bd3712b..be37d8c09d 100644 --- a/pkg/libpng/build.zig.zon +++ b/pkg/libpng/build.zig.zon @@ -3,8 +3,9 @@ .version = "1.6.43", .paths = .{""}, .dependencies = .{ + // glennrp/libpng .libpng = .{ - .url = "https://github.com/glennrp/libpng/archive/refs/tags/v1.6.43.tar.gz", + .url = "https://deps.files.ghostty.org/libpng-1220aa013f0c83da3fb64ea6d327f9173fa008d10e28bc9349eac3463457723b1c66.tar.gz", .hash = "1220aa013f0c83da3fb64ea6d327f9173fa008d10e28bc9349eac3463457723b1c66", }, diff --git a/pkg/macos/graphics/color_space.zig b/pkg/macos/graphics/color_space.zig index 459f063029..16960591b2 100644 --- a/pkg/macos/graphics/color_space.zig +++ b/pkg/macos/graphics/color_space.zig @@ -18,9 +18,72 @@ pub const ColorSpace = opaque { ) orelse Allocator.Error.OutOfMemory; } + pub fn createNamed(name: Name) Allocator.Error!*ColorSpace { + return @as( + ?*ColorSpace, + @ptrFromInt(@intFromPtr(c.CGColorSpaceCreateWithName(name.cfstring()))), + ) orelse Allocator.Error.OutOfMemory; + } + pub fn release(self: *ColorSpace) void { c.CGColorSpaceRelease(@ptrCast(self)); } + + pub const Name = enum { + /// This color space uses the DCI P3 primaries, a D65 white point, and + /// the sRGB transfer function. + displayP3, + /// The Display P3 color space with a linear transfer function and + /// extended-range values. + extendedLinearDisplayP3, + /// The sRGB colorimetry and non-linear transfer function are specified + /// in IEC 61966-2-1. + sRGB, + /// This color space has the same colorimetry as `sRGB`, but uses a + /// linear transfer function. + linearSRGB, + /// This color space has the same colorimetry as `sRGB`, but you can + /// encode component values below `0.0` and above `1.0`. Negative values + /// are encoded as the signed reflection of the original encoding + /// function, as shown in the formula below: + /// ``` + /// extendedTransferFunction(x) = sign(x) * sRGBTransferFunction(abs(x)) + /// ``` + extendedSRGB, + /// This color space has the same colorimetry as `sRGB`; in addition, + /// you may encode component values below `0.0` and above `1.0`. + extendedLinearSRGB, + /// ... + genericGrayGamma2_2, + /// ... + linearGray, + /// This color space has the same colorimetry as `genericGrayGamma2_2`, + /// but you can encode component values below `0.0` and above `1.0`. + /// Negative values are encoded as the signed reflection of the + /// original encoding function, as shown in the formula below: + /// ``` + /// extendedGrayTransferFunction(x) = sign(x) * gamma22Function(abs(x)) + /// ``` + extendedGray, + /// This color space has the same colorimetry as `linearGray`; in + /// addition, you may encode component values below `0.0` and above `1.0`. + extendedLinearGray, + + fn cfstring(self: Name) c.CFStringRef { + return switch (self) { + .displayP3 => c.kCGColorSpaceDisplayP3, + .extendedLinearDisplayP3 => c.kCGColorSpaceExtendedLinearDisplayP3, + .sRGB => c.kCGColorSpaceSRGB, + .extendedSRGB => c.kCGColorSpaceExtendedSRGB, + .linearSRGB => c.kCGColorSpaceLinearSRGB, + .extendedLinearSRGB => c.kCGColorSpaceExtendedLinearSRGB, + .genericGrayGamma2_2 => c.kCGColorSpaceGenericGrayGamma2_2, + .extendedGray => c.kCGColorSpaceExtendedGray, + .linearGray => c.kCGColorSpaceLinearGray, + .extendedLinearGray => c.kCGColorSpaceExtendedLinearGray, + }; + } + }; }; test { diff --git a/pkg/oniguruma/build.zig.zon b/pkg/oniguruma/build.zig.zon index 2120f77ae3..b1ba96dc8c 100644 --- a/pkg/oniguruma/build.zig.zon +++ b/pkg/oniguruma/build.zig.zon @@ -3,8 +3,9 @@ .version = "6.9.9", .paths = .{""}, .dependencies = .{ + // kkos/oniguruma .oniguruma = .{ - .url = "https://github.com/kkos/oniguruma/archive/refs/tags/v6.9.9.tar.gz", + .url = "https://deps.files.ghostty.org/oniguruma-1220c15e72eadd0d9085a8af134904d9a0f5dfcbed5f606ad60edc60ebeccd9706bb.tar.gz", .hash = "1220c15e72eadd0d9085a8af134904d9a0f5dfcbed5f606ad60edc60ebeccd9706bb", }, diff --git a/pkg/opengl/Texture.zig b/pkg/opengl/Texture.zig index 4cd1cf9f9b..a9fa5d4fe3 100644 --- a/pkg/opengl/Texture.zig +++ b/pkg/opengl/Texture.zig @@ -162,4 +162,26 @@ pub const Binding = struct { data, ); } + + pub fn copySubImage2D( + b: Binding, + level: c.GLint, + xoffset: c.GLint, + yoffset: c.GLint, + x: c.GLint, + y: c.GLint, + width: c.GLsizei, + height: c.GLsizei, + ) !void { + glad.context.CopyTexSubImage2D.?( + @intFromEnum(b.target), + level, + xoffset, + yoffset, + x, + y, + width, + height + ); + } }; diff --git a/pkg/sentry/build.zig.zon b/pkg/sentry/build.zig.zon index 385cd69d8c..8d1162cb42 100644 --- a/pkg/sentry/build.zig.zon +++ b/pkg/sentry/build.zig.zon @@ -3,8 +3,9 @@ .version = "0.7.8", .paths = .{""}, .dependencies = .{ + // getsentry/sentry-native .sentry = .{ - .url = "https://github.com/getsentry/sentry-native/archive/refs/tags/0.7.8.tar.gz", + .url = "https://deps.files.ghostty.org/sentry-1220446be831adcca918167647c06c7b825849fa3fba5f22da394667974537a9c77e.tar.gz", .hash = "1220446be831adcca918167647c06c7b825849fa3fba5f22da394667974537a9c77e", }, diff --git a/pkg/spirv-cross/build.zig.zon b/pkg/spirv-cross/build.zig.zon index 39fcac5294..dedf5e964a 100644 --- a/pkg/spirv-cross/build.zig.zon +++ b/pkg/spirv-cross/build.zig.zon @@ -3,8 +3,9 @@ .version = "13.1.1", .paths = .{""}, .dependencies = .{ + // KhronosGroup/SPIRV-Cross .spirv_cross = .{ - .url = "https://github.com/KhronosGroup/SPIRV-Cross/archive/476f384eb7d9e48613c45179e502a15ab95b6b49.tar.gz", + .url = "https://deps.files.ghostty.org/spirv_cross-1220fb3b5586e8be67bc3feb34cbe749cf42a60d628d2953632c2f8141302748c8da.tar.gz", .hash = "1220fb3b5586e8be67bc3feb34cbe749cf42a60d628d2953632c2f8141302748c8da", }, diff --git a/pkg/utfcpp/build.zig.zon b/pkg/utfcpp/build.zig.zon index e56d77f193..795a027496 100644 --- a/pkg/utfcpp/build.zig.zon +++ b/pkg/utfcpp/build.zig.zon @@ -3,8 +3,9 @@ .version = "4.0.5", .paths = .{""}, .dependencies = .{ + // nemtrif/utfcpp .utfcpp = .{ - .url = "https://github.com/nemtrif/utfcpp/archive/refs/tags/v4.0.5.tar.gz", + .url = "https://deps.files.ghostty.org/utfcpp-1220d4d18426ca72fc2b7e56ce47273149815501d0d2395c2a98c726b31ba931e641.tar.gz", .hash = "1220d4d18426ca72fc2b7e56ce47273149815501d0d2395c2a98c726b31ba931e641", }, diff --git a/pkg/wuffs/build.zig.zon b/pkg/wuffs/build.zig.zon index d84d6957e2..caa28f1c6e 100644 --- a/pkg/wuffs/build.zig.zon +++ b/pkg/wuffs/build.zig.zon @@ -2,13 +2,15 @@ .name = "wuffs", .version = "0.0.0", .dependencies = .{ + // google/wuffs .wuffs = .{ - .url = "https://github.com/google/wuffs/archive/refs/tags/v0.4.0-alpha.9.tar.gz", + .url = "https://deps.files.ghostty.org/wuffs-122037b39d577ec2db3fd7b2130e7b69ef6cc1807d68607a7c232c958315d381b5cd.tar.gz", .hash = "122037b39d577ec2db3fd7b2130e7b69ef6cc1807d68607a7c232c958315d381b5cd", }, + // make-github-pseudonymous-again/pixels .pixels = .{ - .url = "git+https://github.com/make-github-pseudonymous-again/pixels?ref=main#d843c2714d32e15b48b8d7eeb480295af537f877", + .url = "https://deps.files.ghostty.org/pixels-12207ff340169c7d40c570b4b6a97db614fe47e0d83b5801a932dcd44917424c8806.tar.gz", .hash = "12207ff340169c7d40c570b4b6a97db614fe47e0d83b5801a932dcd44917424c8806", }, diff --git a/pkg/wuffs/src/jpeg.zig b/pkg/wuffs/src/jpeg.zig index 69628f582f..c07278eed3 100644 --- a/pkg/wuffs/src/jpeg.zig +++ b/pkg/wuffs/src/jpeg.zig @@ -55,7 +55,7 @@ pub fn decode(alloc: Allocator, data: []const u8) Error!ImageData { c.wuffs_base__pixel_config__set( &image_config.pixcfg, - c.WUFFS_BASE__PIXEL_FORMAT__RGBA_PREMUL, + c.WUFFS_BASE__PIXEL_FORMAT__RGBA_NONPREMUL, c.WUFFS_BASE__PIXEL_SUBSAMPLING__NONE, width, height, @@ -95,16 +95,6 @@ pub fn decode(alloc: Allocator, data: []const u8) Error!ImageData { try check(log, &status); } - var frame_config: c.wuffs_base__frame_config = undefined; - { - const status = c.wuffs_jpeg__decoder__decode_frame_config( - decoder, - &frame_config, - &source_buffer, - ); - try check(log, &status); - } - { const status = c.wuffs_jpeg__decoder__decode_frame( decoder, diff --git a/pkg/wuffs/src/png.zig b/pkg/wuffs/src/png.zig index b85e4d7474..1f37bb375a 100644 --- a/pkg/wuffs/src/png.zig +++ b/pkg/wuffs/src/png.zig @@ -55,7 +55,7 @@ pub fn decode(alloc: Allocator, data: []const u8) Error!ImageData { c.wuffs_base__pixel_config__set( &image_config.pixcfg, - c.WUFFS_BASE__PIXEL_FORMAT__RGBA_PREMUL, + c.WUFFS_BASE__PIXEL_FORMAT__RGBA_NONPREMUL, c.WUFFS_BASE__PIXEL_SUBSAMPLING__NONE, width, height, @@ -95,16 +95,6 @@ pub fn decode(alloc: Allocator, data: []const u8) Error!ImageData { try check(log, &status); } - var frame_config: c.wuffs_base__frame_config = undefined; - { - const status = c.wuffs_png__decoder__decode_frame_config( - decoder, - &frame_config, - &source_buffer, - ); - try check(log, &status); - } - { const status = c.wuffs_png__decoder__decode_frame( decoder, diff --git a/pkg/zlib/build.zig.zon b/pkg/zlib/build.zig.zon index 94aa184de7..43176352a0 100644 --- a/pkg/zlib/build.zig.zon +++ b/pkg/zlib/build.zig.zon @@ -3,8 +3,9 @@ .version = "1.3.1", .paths = .{""}, .dependencies = .{ + // madler/zlib .zlib = .{ - .url = "https://github.com/madler/zlib/archive/refs/tags/v1.3.1.tar.gz", + .url = "https://deps.files.ghostty.org/zlib-1220fed0c74e1019b3ee29edae2051788b080cd96e90d56836eea857b0b966742efb.tar.gz", .hash = "1220fed0c74e1019b3ee29edae2051788b080cd96e90d56836eea857b0b966742efb", }, diff --git a/snap/local/launcher b/snap/local/launcher new file mode 100755 index 0000000000..11597f238e --- /dev/null +++ b/snap/local/launcher @@ -0,0 +1,52 @@ +#!/bin/sh +set -e + +# Set these to reasonable defaults if not already set +if [ -z "$XDG_CONFIG_HOME" ]; then + export XDG_CONFIG_HOME="$SNAP_REAL_HOME/.config" +fi + +if [ -z "$XDG_CACHE_HOME" ]; then + export XDG_CACHE_HOME="$SNAP_REAL_HOME/.cache" +fi + +if [ -z "$XDG_DATA_HOME" ]; then + export XDG_DATA_HOME="$SNAP_REAL_HOME/.local/share" +fi + +export HOME="$SNAP_REAL_HOME" + +if [ "$SNAP_ARCH" = "amd64" ]; then + ARCH="x86_64-linux-gnu" +elif [ "$SNAP_ARCH" = "arm64" ]; then + ARCH="aarch64-linux-gnu" +else + ARCH="$SNAP_ARCH-linux-gnu" +fi + +export LD_LIBRARY_PATH=${SNAP}/usr/lib/${ARCH}:${SNAP}/usr/lib/${ARCH}/vdpau:${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:} +export LIBGL_DRIVERS_PATH=${LIBGL_DRIVERS_PATH:+$LIBGL_DRIVERS_PATH:}${SNAP}/usr/lib/${ARCH}/dri/ +export LIBVA_DRIVERS_PATH=${LIBVA_DRIVERS_PATH:+$LIBVA_DRIVERS_PATH:}${SNAP}/usr/lib/${ARCH}/dri/ +export __EGL_VENDOR_LIBRARY_DIRS=${__EGL_VENDOR_LIBRARY_DIRS:+$__EGL_VENDOR_LIBRARY_DIRS:}${SNAP}/usr/share/glvnd/egl_vendor.d +export __EGL_EXTERNAL_PLATFORM_CONFIG_DIRS=${__EGL_EXTERNAL_PLATFORM_CONFIG_DIRS:+$__EGL_EXTERNAL_PLATFORM_CONFIG_DIRS:}${SNAP}/usr/share/egl/egl_external_platform.d +export DRIRC_CONFIGDIR=${SNAP}/usr/share/drirc.d +export VK_LAYER_PATH=${VK_LAYER_PATH:+$VK_LAYER_PATH:}${SNAP}/usr/share/vulkan/implicit_layer.d/:${SNAP}/usr/share/vulkan/explicit_layer.d/ +export XDG_DATA_DIRS=${XDG_DATA_DIRS:+$XDG_DATA_DIRS:}${SNAP}/usr/share +export XLOCALEDIR="${SNAP}/usr/share/X11/locale" +export GDK_PIXBUF_MODULE_FILE="$XDG_CACHE_HOME/gdk-pixbuf-loaders.cache" +export GDK_PIXBUF_MODULEDIR="$SNAP/usr/lib/$ARCH/gdk-pixbuf-2.0/2.10.0/loaders" +export GTK_PATH="$SNAP/usr/lib/$ARCH/gtk-4.0" + +if [ "${__NV_PRIME_RENDER_OFFLOAD:-}" != 1 ]; then + # Prevent picking VA-API (Intel/AMD) over NVIDIA VDPAU + # https://download.nvidia.com/XFree86/Linux-x86_64/510.54/README/primerenderoffload.html#configureapplications + export LIBVA_DRIVERS_PATH +fi + +# Unset all SNAP specific environment variables to keep them from leaking +# into other snaps that might get executed from within the shell +for var in $(printenv | grep SNAP_ | cut -d= -f1); do + unset $var +done + +exec "$@" diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml new file mode 100644 index 0000000000..9ef2f5cc41 --- /dev/null +++ b/snap/snapcraft.yaml @@ -0,0 +1,139 @@ +name: ghostty +base: core24 +summary: A terminal emulator +description: | + Ghostty is a fast, feature-rich, and cross-platform terminal emulator that + uses platform-native UI and GPU acceleration. +grade: stable +confinement: classic +contact: https://github.com/ghostty-org/ghostty/discussions +issues: https://github.com/ghostty-org/ghostty/issues +website: https://ghostty.org +license: MIT +icon: images/icons/icon_512.png +adopt-info: ghostty + +platforms: + amd64: + arm64: + +apps: + ghostty: + command: bin/ghostty + command-chain: [bin/launcher] + completer: share/bash-completion/completions/ghostty.bash + desktop: share/applications/com.mitchellh.ghostty.desktop + #refresh-mode: ignore-running # Store rejects this, needs fix in review-tools + environment: + PATH: /snap/ghostty/current/bin:/snap/ghostty/current/usr/bin:$PATH + LC_ALL: C.UTF-8 + GHOSTTY_RESOURCES_DIR: /snap/ghostty/current/share/ghostty + +parts: + launcher: + plugin: dump + source: snap/local + source-type: local + organize: + launcher: bin/ + + zig: + plugin: nil + build-packages: + - curl + override-pull: | + set -ex + case "$CRAFT_ARCH_BUILD_FOR" in + amd64) arch=x86_64 ;; + arm64) arch=aarch64 ;; + *) arch="" ;; + esac + + rm -rf $CRAFT_PART_SRC/* + + if [[ -n $arch ]]; then + curl -LO --retry-connrefused --retry 10 https://ziglang.org/download/0.13.0/zig-linux-$arch-0.13.0.tar.xz + else + echo "Unsupported arch" + exit 1 + fi + + tar xf zig-lin*xz + rm -f *xz + mv zig-linux*/* . + prime: + - -* + + ghostty: + source: . + after: [zig] + plugin: nil + build-attributes: [enable-patchelf] + build-packages: + - libgtk-4-dev + - libadwaita-1-dev + - git + - patchelf + override-build: | + craftctl set version=$(git describe --abbrev=8) + $CRAFT_PART_SRC/../../zig/src/zig build -Dpatch-rpath=\$ORIGIN/../usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR:/snap/core24/current/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR -Doptimize=ReleaseFast + cp -rp zig-out/* $CRAFT_PART_INSTALL/ + sed -i 's|Icon=com.mitchellh.ghostty|Icon=/snap/ghostty/current/share/icons/hicolor/512x512/apps/com.mitchellh.ghostty.png|g' $CRAFT_PART_INSTALL/share/applications/com.mitchellh.ghostty.desktop + + libs: + plugin: nil + build-attributes: [enable-patchelf] + stage-packages: + - libadwaita-1-0 + - libglib2.0-0t64 + - libgtk-4-1 + - libgtk-4-media-gstreamer + - ibus-gtk4 + - libpciaccess0 + - libtinfo6 + - libedit2 + - libelf1t64 + - libsensors5 + - libllvm17 + - libunistring5 + - librsvg2-2 + - on amd64: + [ + i965-va-driver, + libdrm-intel1, + libdrm-nouveau2, + libdrm-amdgpu1, + libdrm-radeon1, + ] + stage: + # The libraries in dri need no-patchelf, so they come from the mesa-unpatched part + - -usr/lib/*/dri + + mesa: + plugin: nil + build-attributes: [enable-patchelf] + stage-packages: + - libglu1-mesa + - libgl1-mesa-dri + - libegl-mesa0 + - libegl1 + - libglx-mesa0 + - mesa-libgallium + stage: + # The libraries in dri need no-patchelf, so they come from the mesa-unpatched part + - usr/lib/*/*.so* + - usr/lib/*/dri/libdril_dri.so + - -usr/lib/*/libgallium*so + - -usr/lib/*/dri + + mesa-gl1-dri: + plugin: nil + stage-packages: + - libgl1-mesa-dri + build-attributes: [no-patchelf] + stage: + # Only the libraries in dri need to not be patched, the rest come from the mesa part + # Otherwise snapcraft may strip the build ID and cause the driver to crash + - usr/lib/*/libgallium*so + - -usr/lib/*/dri/libdril_dri.so + - usr/lib/*/dri diff --git a/src/App.zig b/src/App.zig index a6b54db232..15859d1155 100644 --- a/src/App.zig +++ b/src/App.zig @@ -161,7 +161,7 @@ pub fn updateConfig(self: *App, rt_app: *apprt.App, config: *const Config) !void const applied: *const configpkg.Config = if (applied_) |*c| c else config; // Notify the apprt that the app has changed configuration. - try rt_app.performAction( + _ = try rt_app.performAction( .app, .config_change, .{ .config = applied }, @@ -180,7 +180,7 @@ pub fn addSurface( // Since we have non-zero surfaces, we can cancel the quit timer. // It is up to the apprt if there is a quit timer at all and if it // should be canceled. - rt_surface.app.performAction( + _ = rt_surface.app.performAction( .app, .quit_timer, .stop, @@ -214,7 +214,7 @@ pub fn deleteSurface(self: *App, rt_surface: *apprt.Surface) void { // If we have no surfaces, we can start the quit timer. It is up to the // apprt to determine if this is necessary. - if (self.surfaces.items.len == 0) rt_surface.app.performAction( + if (self.surfaces.items.len == 0) _ = rt_surface.app.performAction( .app, .quit_timer, .start, @@ -294,7 +294,7 @@ pub fn newWindow(self: *App, rt_app: *apprt.App, msg: Message.NewWindow) !void { break :target .app; }; - try rt_app.performAction( + _ = try rt_app.performAction( target, .new_window, {}, @@ -419,7 +419,7 @@ pub fn colorSchemeEvent( // Request our configuration be reloaded because the new scheme may // impact the colors of the app. - try rt_app.performAction( + _ = try rt_app.performAction( .app, .reload_config, .{ .soft = true }, @@ -437,13 +437,13 @@ pub fn performAction( switch (action) { .unbind => unreachable, .ignore => {}, - .quit => try rt_app.performAction(.app, .quit, {}), - .new_window => try self.newWindow(rt_app, .{ .parent = null }), - .open_config => try rt_app.performAction(.app, .open_config, {}), - .reload_config => try rt_app.performAction(.app, .reload_config, .{}), - .close_all_windows => try rt_app.performAction(.app, .close_all_windows, {}), - .toggle_quick_terminal => try rt_app.performAction(.app, .toggle_quick_terminal, {}), - .toggle_visibility => try rt_app.performAction(.app, .toggle_visibility, {}), + .quit => _ = try rt_app.performAction(.app, .quit, {}), + .new_window => _ = try self.newWindow(rt_app, .{ .parent = null }), + .open_config => _ = try rt_app.performAction(.app, .open_config, {}), + .reload_config => _ = try rt_app.performAction(.app, .reload_config, .{}), + .close_all_windows => _ = try rt_app.performAction(.app, .close_all_windows, {}), + .toggle_quick_terminal => _ = try rt_app.performAction(.app, .toggle_quick_terminal, {}), + .toggle_visibility => _ = try rt_app.performAction(.app, .toggle_visibility, {}), } } diff --git a/src/Surface.zig b/src/Surface.zig index 06701b5362..14ddde294c 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -519,9 +519,19 @@ pub fn init( // This separate block ({}) is important because our errdefers must // be scoped here to be valid. { + var env = rt_surface.defaultTermioEnv() catch |err| env: { + // If an error occurs, we don't want to block surface startup. + log.warn("error getting env map for surface err={}", .{err}); + break :env internal_os.getEnvMap(alloc) catch + std.process.EnvMap.init(alloc); + }; + errdefer env.deinit(); + // Initialize our IO backend var io_exec = try termio.Exec.init(alloc, .{ .command = command, + .env = env, + .env_override = config.env, .shell_integration = config.@"shell-integration", .shell_integration_features = config.@"shell-integration-features", .working_directory = config.@"working-directory", @@ -561,7 +571,7 @@ pub fn init( errdefer self.io.deinit(); // Report initial cell size on surface creation - try rt_app.performAction( + _ = try rt_app.performAction( .{ .surface = self }, .cell_size, .{ .width = size.cell.width, .height = size.cell.height }, @@ -573,7 +583,7 @@ pub fn init( const min_window_width_cells: u32 = 10; const min_window_height_cells: u32 = 4; - try rt_app.performAction( + _ = try rt_app.performAction( .{ .surface = self }, .size_limit, .{ @@ -637,7 +647,7 @@ pub fn init( size.padding.top + size.padding.bottom; - rt_app.performAction( + _ = rt_app.performAction( .{ .surface = self }, .initial_size, .{ .width = final_width, .height = final_height }, @@ -649,7 +659,7 @@ pub fn init( } if (config.title) |title| { - try rt_app.performAction( + _ = try rt_app.performAction( .{ .surface = self }, .set_title, .{ .title = title }, @@ -670,7 +680,7 @@ pub fn init( break :xdg; }; defer alloc.free(title); - try rt_app.performAction( + _ = try rt_app.performAction( .{ .surface = self }, .set_title, .{ .title = title }, @@ -823,7 +833,7 @@ pub fn handleMessage(self: *Surface, msg: Message) !void { // We know that our title should end in 0. const slice = std.mem.sliceTo(@as([*:0]const u8, @ptrCast(v)), 0); log.debug("changing title \"{s}\"", .{slice}); - try self.rt_app.performAction( + _ = try self.rt_app.performAction( .{ .surface = self }, .set_title, .{ .title = slice }, @@ -859,7 +869,7 @@ pub fn handleMessage(self: *Surface, msg: Message) !void { .color_change => |change| { // Notify our apprt, but don't send a mode 2031 DSR report // because VT sequences were used to change the color. - try self.rt_app.performAction( + _ = try self.rt_app.performAction( .{ .surface = self }, .color_change, .{ @@ -878,7 +888,7 @@ pub fn handleMessage(self: *Surface, msg: Message) !void { .set_mouse_shape => |shape| { log.debug("changing mouse shape: {}", .{shape}); - try self.rt_app.performAction( + _ = try self.rt_app.performAction( .{ .surface = self }, .mouse_shape, shape, @@ -910,7 +920,7 @@ pub fn handleMessage(self: *Surface, msg: Message) !void { const str = try self.alloc.dupeZ(u8, w.slice()); defer self.alloc.free(str); - try self.rt_app.performAction( + _ = try self.rt_app.performAction( .{ .surface = self }, .pwd, .{ .pwd = str }, @@ -961,7 +971,7 @@ fn passwordInput(self: *Surface, v: bool) !void { } // Notify our apprt so it can do whatever it wants. - self.rt_app.performAction( + _ = self.rt_app.performAction( .{ .surface = self }, .secure_input, if (v) .on else .off, @@ -1041,13 +1051,16 @@ fn mouseRefreshLinks( pos_vp: terminal.point.Coordinate, over_link: bool, ) !void { + // If the position is outside our viewport, do nothing + if (pos.x < 0 or pos.y < 0) return; + self.mouse.link_point = pos_vp; if (try self.linkAtPos(pos)) |link| { self.renderer_state.mouse.point = pos_vp; self.mouse.over_link = true; self.renderer_state.terminal.screen.dirty.hyperlink_hover = true; - try self.rt_app.performAction( + _ = try self.rt_app.performAction( .{ .surface = self }, .mouse_shape, .pointer, @@ -1060,7 +1073,7 @@ fn mouseRefreshLinks( .trim = false, }); defer self.alloc.free(str); - try self.rt_app.performAction( + _ = try self.rt_app.performAction( .{ .surface = self }, .mouse_over_link, .{ .url = str }, @@ -1074,7 +1087,7 @@ fn mouseRefreshLinks( log.warn("failed to get URI for OSC8 hyperlink", .{}); break :link; }; - try self.rt_app.performAction( + _ = try self.rt_app.performAction( .{ .surface = self }, .mouse_over_link, .{ .url = uri }, @@ -1084,12 +1097,12 @@ fn mouseRefreshLinks( try self.queueRender(); } else if (over_link) { - try self.rt_app.performAction( + _ = try self.rt_app.performAction( .{ .surface = self }, .mouse_shape, self.io.terminal.mouse_shape, ); - try self.rt_app.performAction( + _ = try self.rt_app.performAction( .{ .surface = self }, .mouse_over_link, .{ .url = "" }, @@ -1101,7 +1114,7 @@ fn mouseRefreshLinks( /// Called when our renderer health state changes. fn updateRendererHealth(self: *Surface, health: renderer.Health) void { log.warn("renderer health status change status={}", .{health}); - self.rt_app.performAction( + _ = self.rt_app.performAction( .{ .surface = self }, .renderer_health, health, @@ -1113,7 +1126,7 @@ fn updateRendererHealth(self: *Surface, health: renderer.Health) void { /// This should be called anytime `config_conditional_state` changes /// so that the apprt can reload the configuration. fn notifyConfigConditionalState(self: *Surface) void { - self.rt_app.performAction( + _ = self.rt_app.performAction( .{ .surface = self }, .reload_config, .{ .soft = true }, @@ -1193,14 +1206,14 @@ pub fn updateConfig( // If we have a title set then we update our window to have the // newly configured title. - if (config.title) |title| try self.rt_app.performAction( + if (config.title) |title| _ = try self.rt_app.performAction( .{ .surface = self }, .set_title, .{ .title = title }, ); // Notify the window - try self.rt_app.performAction( + _ = try self.rt_app.performAction( .{ .surface = self }, .config_change, .{ .config = config }, @@ -1316,8 +1329,8 @@ pub fn imePoint(self: *const Surface) apprt.IMEPos { const content_scale = self.rt_surface.getContentScale() catch .{ .x = 1, .y = 1 }; const x: f64 = x: { - // Simple x * cell width gives the top-left corner - var x: f64 = @floatFromInt(cursor.x * self.size.cell.width); + // Simple x * cell width gives the top-left corner, then add padding offset + var x: f64 = @floatFromInt(cursor.x * self.size.cell.width + self.size.padding.left); // We want the midpoint x += @as(f64, @floatFromInt(self.size.cell.width)) / 2; @@ -1329,8 +1342,8 @@ pub fn imePoint(self: *const Surface) apprt.IMEPos { }; const y: f64 = y: { - // Simple x * cell width gives the top-left corner - var y: f64 = @floatFromInt(cursor.y * self.size.cell.height); + // Simple y * cell height gives the top-left corner, then add padding offset + var y: f64 = @floatFromInt(cursor.y * self.size.cell.height + self.size.padding.top); // We want the bottom y += @floatFromInt(self.size.cell.height); @@ -1467,7 +1480,7 @@ fn setCellSize(self: *Surface, size: renderer.CellSize) !void { self.io.queueMessage(.{ .resize = self.size }, .unlocked); // Notify the window - try self.rt_app.performAction( + _ = try self.rt_app.performAction( .{ .surface = self }, .cell_size, .{ .width = size.width, .height = size.height }, @@ -1591,6 +1604,15 @@ pub fn preeditCallback(self: *Surface, preedit_: ?[]const u8) !void { self.renderer_state.mutex.lock(); defer self.renderer_state.mutex.unlock(); + // We clear our selection when ANY OF: + // 1. We have an existing preedit + // 2. We have preedit text + if (self.renderer_state.preedit != null or + preedit_ != null) + { + self.setSelection(null) catch {}; + } + // We always clear our prior preedit if (self.renderer_state.preedit) |p| { self.alloc.free(p.codepoints); @@ -1754,12 +1776,12 @@ pub fn keyCallback( }; } else if (self.io.terminal.flags.mouse_event != .none and !self.mouse.mods.shift) { // If we have mouse reports on and we don't have shift pressed, we reset state - try self.rt_app.performAction( + _ = try self.rt_app.performAction( .{ .surface = self }, .mouse_shape, self.io.terminal.mouse_shape, ); - try self.rt_app.performAction( + _ = try self.rt_app.performAction( .{ .surface = self }, .mouse_over_link, .{ .url = "" }, @@ -1777,7 +1799,7 @@ pub fn keyCallback( .mods = self.mouse.mods, .over_link = self.mouse.over_link, .hidden = self.mouse.hidden, - }).keyToMouseShape()) |shape| try self.rt_app.performAction( + }).keyToMouseShape()) |shape| _ = try self.rt_app.performAction( .{ .surface = self }, .mouse_shape, shape, @@ -1902,7 +1924,7 @@ fn maybeHandleBinding( } // Start or continue our key sequence - self.rt_app.performAction( + _ = self.rt_app.performAction( .{ .surface = self }, .key_sequence, .{ .trigger = entry.key_ptr.* }, @@ -2011,7 +2033,7 @@ fn endKeySequence( mem: KeySequenceMemory, ) void { // Notify apprt key sequence ended - self.rt_app.performAction( + _ = self.rt_app.performAction( .{ .surface = self }, .key_sequence, .end, @@ -3203,26 +3225,9 @@ fn linkAtPos( })); defer strmap.deinit(self.alloc); - // Go through each link and see if we clicked it - for (self.config.links) |link| { - switch (link.highlight) { - .always, .hover => {}, - .always_mods, .hover_mods => |v| if (!v.equal(mouse_mods)) continue, - } - - var it = strmap.searchIterator(link.regex); - while (true) { - var match = (try it.next()) orelse break; - defer match.deinit(); - const sel = match.selection(); - if (!sel.contains(screen, mouse_pin)) continue; - return .{ .action = link.action, .selection = sel }; - } - } - - // this is the last chance to return any substr as clickable or not - // by checking if any given str is path resolvable or not. below lines of code - // will work only for file paths. this covers both relative and absolute paths. + // Check if any substr as clickable or not. + // If any given str is path resolvable or not. Below lines of code + // will work only for file paths. This covers both relative and absolute paths. // some of the paths may not work it is based on the `std.fs.realpath` const cwd = self.io.terminal.getPwd(); var split_str = std.mem.splitSequence(u8, strmap.string, " "); @@ -3261,6 +3266,23 @@ fn linkAtPos( } } + // Go through each link and see if we clicked it + for (self.config.links) |link| { + switch (link.highlight) { + .always, .hover => {}, + .always_mods, .hover_mods => |v| if (!v.equal(mouse_mods)) continue, + } + + var it = strmap.searchIterator(link.regex); + while (true) { + var match = (try it.next()) orelse break; + defer match.deinit(); + const sel = match.selection(); + if (!sel.contains(screen, mouse_pin)) continue; + return .{ .action = link.action, .selection = sel }; + } + } + return null; } @@ -3398,12 +3420,12 @@ pub fn cursorPosCallback( self.mouse.link_point = null; if (self.mouse.over_link) { self.mouse.over_link = false; - try self.rt_app.performAction( + _ = try self.rt_app.performAction( .{ .surface = self }, .mouse_shape, self.io.terminal.mouse_shape, ); - try self.rt_app.performAction( + _ = try self.rt_app.performAction( .{ .surface = self }, .mouse_over_link, .{ .url = "" }, @@ -3605,22 +3627,21 @@ fn dragLeftClickTriple( const screen = &self.io.terminal.screen; const click_pin = self.mouse.left_click_pin.?.*; - // Get the word under our current point. If there isn't a word, do nothing. - const word = screen.selectLine(.{ .pin = drag_pin }) orelse return; + // Get the line selection under our current drag point. If there isn't a + // line, do nothing. + const line = screen.selectLine(.{ .pin = drag_pin }) orelse return; - // Get our selection to grow it. If we don't have a selection, start it now. - // We may not have a selection if we started our dbl-click in an area - // that had no data, then we dragged our mouse into an area with data. - var sel = screen.selectLine(.{ .pin = click_pin }) orelse { - try self.setSelection(word); - return; - }; + // Get the selection under our click point. We first try to trim + // whitespace if we've selected a word. But if no word exists then + // we select the blank line. + const sel_ = screen.selectLine(.{ .pin = click_pin }) orelse + screen.selectLine(.{ .pin = click_pin, .whitespace = null }); - // Grow our selection + var sel = sel_ orelse return; if (drag_pin.before(click_pin)) { - sel.startPtr().* = word.start(); + sel.startPtr().* = line.start(); } else { - sel.endPtr().* = word.end(); + sel.endPtr().* = line.end(); } try self.setSelection(sel); } @@ -3830,7 +3851,7 @@ fn scrollToBottom(self: *Surface) !void { fn hideMouse(self: *Surface) void { if (self.mouse.hidden) return; self.mouse.hidden = true; - self.rt_app.performAction( + _ = self.rt_app.performAction( .{ .surface = self }, .mouse_visibility, .hidden, @@ -3842,7 +3863,7 @@ fn hideMouse(self: *Surface) void { fn showMouse(self: *Surface) void { if (!self.mouse.hidden) return; self.mouse.hidden = false; - self.rt_app.performAction( + _ = self.rt_app.performAction( .{ .surface = self }, .mouse_visibility, .visible, @@ -3987,6 +4008,33 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool return false; }, + .copy_url_to_clipboard => { + // If the mouse isn't over a link, nothing we can do. + if (!self.mouse.over_link) return false; + + const pos = try self.rt_surface.getCursorPos(); + if (try self.linkAtPos(pos)) |link_info| { + // Get the URL text from selection + const url_text = (self.io.terminal.screen.selectionString(self.alloc, .{ + .sel = link_info.selection, + .trim = self.config.clipboard_trim_trailing_spaces, + })) catch |err| { + log.err("error reading url string err={}", .{err}); + return false; + }; + defer self.alloc.free(url_text); + + self.rt_surface.setClipboardString(url_text, .standard, false) catch |err| { + log.err("error copying url to clipboard err={}", .{err}); + return true; + }; + + return true; + } + + return false; + }, + .paste_from_clipboard => try self.startClipboardRequest( .standard, .{ .paste = {} }, @@ -4028,6 +4076,12 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool try self.setFontSize(size); }, + .prompt_surface_title => return try self.rt_app.performAction( + .{ .surface = self }, + .prompt_title, + {}, + ), + .clear_screen => { // This is a duplicate of some of the logic in termio.clearScreen // but we need to do this here so we can know the answer before @@ -4106,17 +4160,23 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool v, ), - .new_tab => try self.rt_app.performAction( + .new_tab => return try self.rt_app.performAction( .{ .surface = self }, .new_tab, {}, ), + .close_tab => return try self.rt_app.performAction( + .{ .surface = self }, + .close_tab, + {}, + ), + inline .previous_tab, .next_tab, .last_tab, .goto_tab, - => |v, tag| try self.rt_app.performAction( + => |v, tag| return try self.rt_app.performAction( .{ .surface = self }, .goto_tab, switch (tag) { @@ -4128,13 +4188,13 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool }, ), - .move_tab => |position| try self.rt_app.performAction( + .move_tab => |position| return try self.rt_app.performAction( .{ .surface = self }, .move_tab, .{ .amount = position }, ), - .new_split => |direction| try self.rt_app.performAction( + .new_split => |direction| return try self.rt_app.performAction( .{ .surface = self }, .new_split, switch (direction) { @@ -4149,7 +4209,7 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool }, ), - .goto_split => |direction| try self.rt_app.performAction( + .goto_split => |direction| return try self.rt_app.performAction( .{ .surface = self }, .goto_split, switch (direction) { @@ -4160,7 +4220,7 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool }, ), - .resize_split => |value| try self.rt_app.performAction( + .resize_split => |value| return try self.rt_app.performAction( .{ .surface = self }, .resize_split, .{ @@ -4174,41 +4234,48 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool }, ), - .equalize_splits => try self.rt_app.performAction( + .equalize_splits => return try self.rt_app.performAction( .{ .surface = self }, .equalize_splits, {}, ), - .toggle_split_zoom => try self.rt_app.performAction( + .toggle_split_zoom => return try self.rt_app.performAction( .{ .surface = self }, .toggle_split_zoom, {}, ), - .toggle_fullscreen => try self.rt_app.performAction( + .toggle_maximize => return try self.rt_app.performAction( + .{ .surface = self }, + .toggle_maximize, + {}, + ), + + .toggle_fullscreen => return try self.rt_app.performAction( .{ .surface = self }, .toggle_fullscreen, switch (self.config.macos_non_native_fullscreen) { .false => .native, .true => .macos_non_native, .@"visible-menu" => .macos_non_native_visible_menu, + .@"padded-notch" => .macos_non_native_padded_notch, }, ), - .toggle_window_decorations => try self.rt_app.performAction( + .toggle_window_decorations => return try self.rt_app.performAction( .{ .surface = self }, .toggle_window_decorations, {}, ), - .toggle_tab_overview => try self.rt_app.performAction( + .toggle_tab_overview => return try self.rt_app.performAction( .{ .surface = self }, .toggle_tab_overview, {}, ), - .toggle_secure_input => try self.rt_app.performAction( + .toggle_secure_input => return try self.rt_app.performAction( .{ .surface = self }, .secure_input, .toggle, @@ -4222,7 +4289,7 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool } }, - .inspector => |mode| try self.rt_app.performAction( + .inspector => |mode| return try self.rt_app.performAction( .{ .surface = self }, .inspector, switch (mode) { @@ -4311,6 +4378,7 @@ fn closingAction(action: input.Binding.Action) bool { return switch (action) { .close_surface, .close_window, + .close_tab, => true, else => false, @@ -4668,7 +4736,7 @@ fn showDesktopNotification(self: *Surface, title: [:0]const u8, body: [:0]const self.app.last_notification_time = now; self.app.last_notification_digest = new_digest; - try self.rt_app.performAction( + _ = try self.rt_app.performAction( .{ .surface = self }, .desktop_notification, .{ @@ -4688,7 +4756,7 @@ fn crashThreadState(self: *Surface) crash.sentry.ThreadState { /// Tell the surface to present itself to the user. This may involve raising the /// window and switching tabs. fn presentSurface(self: *Surface) !void { - try self.rt_app.performAction( + _ = try self.rt_app.performAction( .{ .surface = self }, .present_terminal, {}, diff --git a/src/apprt/action.zig b/src/apprt/action.zig index df30f7b7be..20b86707e1 100644 --- a/src/apprt/action.zig +++ b/src/apprt/action.zig @@ -82,6 +82,9 @@ pub const Action = union(Key) { /// the tab should be opened in a new window. new_tab, + /// Closes the tab belonging to the currently focused split. + close_tab, + /// Create a new split. The value determines the location of the split /// relative to the target. new_split: SplitDirection, @@ -89,6 +92,9 @@ pub const Action = union(Key) { /// Close all open windows. close_all_windows, + /// Toggle maximized window state. + toggle_maximize, + /// Toggle fullscreen mode. toggle_fullscreen: Fullscreen, @@ -152,9 +158,13 @@ pub const Action = union(Key) { /// Show a desktop notification. desktop_notification: DesktopNotification, - /// Set the title of the target. + /// Set the title of the target to the requested value. set_title: SetTitle, + /// Set the title of the target to a prompted value. It is up to + /// the apprt to prompt. + prompt_title, + /// The current working directory has changed for the target terminal. pwd: Pwd, @@ -225,8 +235,10 @@ pub const Action = union(Key) { quit, new_window, new_tab, + close_tab, new_split, close_all_windows, + toggle_maximize, toggle_fullscreen, toggle_tab_overview, toggle_window_decorations, @@ -246,6 +258,7 @@ pub const Action = union(Key) { render_inspector, desktop_notification, set_title, + prompt_title, pwd, mouse_shape, mouse_visibility, @@ -377,6 +390,7 @@ pub const Fullscreen = enum(c_int) { /// window. This is much faster to enter and exit than the native mode. macos_non_native, macos_non_native_visible_menu, + macos_non_native_padded_notch, }; pub const SecureInput = enum(c_int) { diff --git a/src/apprt/embedded.zig b/src/apprt/embedded.zig index 50d1e90e4b..18674bc385 100644 --- a/src/apprt/embedded.zig +++ b/src/apprt/embedded.zig @@ -12,6 +12,7 @@ const objc = @import("objc"); const apprt = @import("../apprt.zig"); const font = @import("../font/main.zig"); const input = @import("../input.zig"); +const internal_os = @import("../os/main.zig"); const renderer = @import("../renderer.zig"); const terminal = @import("../terminal/main.zig"); const CoreApp = @import("../App.zig"); @@ -45,7 +46,7 @@ pub const App = struct { wakeup: *const fn (AppUD) callconv(.C) void, /// Callback called to handle an action. - action: *const fn (*App, apprt.Target.C, apprt.Action.C) callconv(.C) void, + action: *const fn (*App, apprt.Target.C, apprt.Action.C) callconv(.C) bool, /// Read the clipboard value. The return value must be preserved /// by the host until the next call. If there is no valid clipboard @@ -181,14 +182,9 @@ pub const App = struct { if (strip) translate_mods.alt = false; } - // On macOS we strip ctrl because UCKeyTranslate - // converts to the masked values (i.e. ctrl+c becomes 3) - // and we don't want that behavior. - // - // We also strip super because its not used for translation - // on macos and it results in a bad translation. + // We strip super on macOS because its not used for translation + // it results in a bad translation. if (comptime builtin.target.isDarwin()) { - translate_mods.ctrl = false; translate_mods.super = false; } @@ -199,6 +195,11 @@ pub const App = struct { // This logic only applies to macOS. if (comptime builtin.os.tag != .macos) break :event_text event.text; + // If we're in a preedit state then we allow it through. This + // allows ctrl sequences that affect IME to work. For example, + // Ctrl+H deletes a character with Japanese input. + if (event.composing) break :event_text event.text; + // If the modifiers are ONLY "control" then we never process // the event text because we want to do our own translation so // we can handle ctrl+c, ctrl+z, etc. @@ -223,6 +224,7 @@ pub const App = struct { const result: input.Keymap.Translation = if (event_text) |text| .{ .text = text, .composing = event.composing, + .mods = translate_mods, } else try self.keymap.translate( &buf, switch (target) { @@ -233,6 +235,14 @@ pub const App = struct { translate_mods, ); + // TODO(mitchellh): I think we can get rid of the above keymap + // translation code completely and defer to AppKit/Swift + // (for macOS) for handling all translations. The translation + // within libghostty is an artifact of an earlier design and + // it is buggy (see #5558). We should move closer to a GTK-style + // model of tracking composing states and preedit in the apprt + // and not in libghostty. + // If this is a dead key, then we're composing a character and // we need to set our proper preedit state if we're targeting a // surface. @@ -259,16 +269,12 @@ pub const App = struct { // then we clear the text. We handle non-printables in the // key encoder manual (such as tab, ctrl+c, etc.) if (result.text.len == 1 and result.text[0] < 0x20) { - break :translate .{ .composing = false, .text = "" }; + break :translate .{}; } } break :translate result; - } else .{ .composing = false, .text = "" }; - - // UCKeyTranslate always consumes all mods, so if we have any output - // then we've consumed our translate mods. - const consumed_mods: input.Mods = if (result.text.len > 0) translate_mods else .{}; + } else .{}; // We need to always do a translation with no modifiers at all in // order to get the "unshifted_codepoint" for the key event. @@ -340,7 +346,7 @@ pub const App = struct { .key = key, .physical_key = physical_key, .mods = mods, - .consumed_mods = consumed_mods, + .consumed_mods = result.mods, .composing = result.composing, .utf8 = result.text, .unshifted_codepoint = unshifted_codepoint, @@ -464,13 +470,14 @@ pub const App = struct { surface.queueInspectorRender(); } - /// Perform a given action. + /// Perform a given action. Returns `true` if the action was able to be + /// performed, `false` otherwise. pub fn performAction( self: *App, target: apprt.Target, comptime action: apprt.Action.Key, value: apprt.Action.Value(action), - ) !void { + ) !bool { // Special case certain actions before they are sent to the // embedded apprt. self.performPreAction(target, action, value); @@ -480,7 +487,7 @@ pub const App = struct { action, value, }); - self.opts.action( + return self.opts.action( self, target.cval(), @unionInit(apprt.Action, @tagName(action), value).cval(), @@ -633,7 +640,7 @@ pub const Surface = struct { .y = @floatCast(opts.scale_factor), }, .size = .{ .width = 800, .height = 600 }, - .cursor_pos = .{ .x = 0, .y = 0 }, + .cursor_pos = .{ .x = -1, .y = -1 }, .keymap_state = .{}, }; @@ -992,7 +999,7 @@ pub const Surface = struct { } fn queueInspectorRender(self: *Surface) void { - self.app.performAction( + _ = self.app.performAction( .{ .surface = &self.core_surface }, .render_inspector, {}, @@ -1013,6 +1020,30 @@ pub const Surface = struct { }; } + pub fn defaultTermioEnv(self: *const Surface) !std.process.EnvMap { + const alloc = self.app.core_app.alloc; + var env = try internal_os.getEnvMap(alloc); + errdefer env.deinit(); + + if (comptime builtin.target.isDarwin()) { + if (env.get("__XCODE_BUILT_PRODUCTS_DIR_PATHS") != null) { + env.remove("__XCODE_BUILT_PRODUCTS_DIR_PATHS"); + env.remove("__XPC_DYLD_LIBRARY_PATH"); + env.remove("DYLD_FRAMEWORK_PATH"); + env.remove("DYLD_INSERT_LIBRARIES"); + env.remove("DYLD_LIBRARY_PATH"); + env.remove("LD_LIBRARY_PATH"); + env.remove("SECURITYSESSIONID"); + env.remove("XPC_SERVICE_NAME"); + } + + // Remove this so that running `ghostty` within Ghostty works. + env.remove("GHOSTTY_MAC_APP"); + } + + return env; + } + /// The cursor position from the host directly is in screen coordinates but /// all our interface works in pixels. fn cursorPosToPixels(self: *const Surface, pos: apprt.CursorPos) !apprt.CursorPos { @@ -1419,7 +1450,7 @@ pub const CAPI = struct { /// Open the configuration. export fn ghostty_app_open_config(v: *App) void { - v.performAction(.app, .open_config, {}) catch |err| { + _ = v.performAction(.app, .open_config, {}) catch |err| { log.err("error reloading config err={}", .{err}); return; }; @@ -1647,7 +1678,12 @@ pub const CAPI = struct { event: KeyEvent, ) bool { const core_event = surface.app.coreKeyEvent( - .{ .surface = surface }, + // Note: this "app" target here looks like a bug, but it is + // intentional. coreKeyEvent uses the target only as a way to + // trigger preedit callbacks for keymap translation and we don't + // want to trigger that here. See the todo item in coreKeyEvent + // for a long term solution to this and removing target altogether. + .app, event.keyEvent(), ) catch |err| { log.warn("error processing key event err={}", .{err}); @@ -1757,7 +1793,7 @@ pub const CAPI = struct { /// Request that the surface split in the given direction. export fn ghostty_surface_split(ptr: *Surface, direction: apprt.action.SplitDirection) void { - ptr.app.performAction( + _ = ptr.app.performAction( .{ .surface = &ptr.core_surface }, .new_split, direction, @@ -1772,7 +1808,7 @@ pub const CAPI = struct { ptr: *Surface, direction: apprt.action.GotoSplit, ) void { - ptr.app.performAction( + _ = ptr.app.performAction( .{ .surface = &ptr.core_surface }, .goto_split, direction, @@ -1791,7 +1827,7 @@ pub const CAPI = struct { direction: apprt.action.ResizeSplit.Direction, amount: u16, ) void { - ptr.app.performAction( + _ = ptr.app.performAction( .{ .surface = &ptr.core_surface }, .resize_split, .{ .direction = direction, .amount = amount }, @@ -1803,7 +1839,7 @@ pub const CAPI = struct { /// Equalize the size of all splits in the current window. export fn ghostty_surface_split_equalize(ptr: *Surface) void { - ptr.app.performAction( + _ = ptr.app.performAction( .{ .surface = &ptr.core_surface }, .equalize_splits, {}, @@ -1953,7 +1989,7 @@ pub const CAPI = struct { _ = CGSSetWindowBackgroundBlurRadius( CGSDefaultConnectionForThread(), nswindow.msgSend(usize, objc.sel("windowNumber"), .{}), - @intCast(config.@"background-blur-radius".cval()), + @intCast(config.@"background-blur".cval()), ); } diff --git a/src/apprt/glfw.zig b/src/apprt/glfw.zig index c91464068a..531269ee14 100644 --- a/src/apprt/glfw.zig +++ b/src/apprt/glfw.zig @@ -147,13 +147,14 @@ pub const App = struct { glfw.postEmptyEvent(); } - /// Perform a given action. + /// Perform a given action. Returns `true` if the action was able to be + /// performed, `false` otherwise. pub fn performAction( self: *App, target: apprt.Target, comptime action: apprt.Action.Key, value: apprt.Action.Value(action), - ) !void { + ) !bool { switch (action) { .quit => self.quit = true, @@ -218,6 +219,7 @@ pub const App = struct { .toggle_split_zoom, .present_terminal, .close_all_windows, + .close_tab, .toggle_tab_overview, .toggle_window_decorations, .toggle_quick_terminal, @@ -236,8 +238,15 @@ pub const App = struct { .color_change, .pwd, .config_change, - => log.info("unimplemented action={}", .{action}), + .toggle_maximize, + .prompt_title, + => { + log.info("unimplemented action={}", .{action}); + return false; + }, } + + return true; } /// Reload the configuration. This should return the new configuration. @@ -872,6 +881,10 @@ pub const Surface = struct { }; } + pub fn defaultTermioEnv(self: *Surface) !std.process.EnvMap { + return try internal_os.getEnvMap(self.app.app.alloc); + } + fn sizeCallback(window: glfw.Window, width: i32, height: i32) void { _ = width; _ = height; diff --git a/src/apprt/gtk/App.zig b/src/apprt/gtk/App.zig index 38c019b3e5..227c36ec45 100644 --- a/src/apprt/gtk/App.zig +++ b/src/apprt/gtk/App.zig @@ -25,7 +25,6 @@ const Config = configpkg.Config; const CoreApp = @import("../../App.zig"); const CoreSurface = @import("../../Surface.zig"); -const adwaita = @import("adwaita.zig"); const cgroup = @import("cgroup.zig"); const Surface = @import("Surface.zig"); const Window = @import("Window.zig"); @@ -36,8 +35,7 @@ const c = @import("c.zig").c; const version = @import("version.zig"); const inspector = @import("inspector.zig"); const key = @import("key.zig"); -const x11 = @import("x11.zig"); -const wayland = @import("wayland.zig"); +const winproto = @import("winproto.zig"); const testing = std.testing; const log = std.log.scoped(.gtk); @@ -50,6 +48,9 @@ config: Config, app: *c.GtkApplication, ctx: *c.GMainContext, +/// State and logic for the underlying windowing protocol. +winproto: winproto.App, + /// True if the app was launched with single instance mode. single_instance: bool, @@ -71,11 +72,10 @@ clipboard_confirmation_window: ?*ClipboardConfirmationWindow = null, /// This is set to false when the main loop should exit. running: bool = true, -/// Xkb state (X11 only). Will be null on Wayland. -x11_xkb: ?x11.Xkb = null, - -/// Wayland app state. Will be null on X11. -wayland: ?wayland.AppState = null, +/// If we should retry querying D-Bus for the color scheme with the deprecated +/// Read method, instead of the recommended ReadOne method. This is kind of +/// nasty to have as struct state but its just a byte... +dbus_color_scheme_retry: bool = true, /// The base path of the transient cgroup used to put all surfaces /// into their own cgroup. This is only set if cgroups are enabled @@ -108,41 +108,13 @@ pub fn init(core_app: *CoreApp, opts: Options) !App { c.gtk_get_micro_version(), }); - // Disabling Vulkan can improve startup times by hundreds of - // milliseconds on some systems. We don't use Vulkan so we can just - // disable it. - if (version.runtimeAtLeast(4, 16, 0)) { - // From gtk 4.16, GDK_DEBUG is split into GDK_DEBUG and GDK_DISABLE. - // For the remainder of "why" see the 4.14 comment below. - _ = internal_os.setenv("GDK_DISABLE", "gles-api,vulkan"); - _ = internal_os.setenv("GDK_DEBUG", "opengl,gl-no-fractional"); - } else if (version.runtimeAtLeast(4, 14, 0)) { - // We need to export GDK_DEBUG to run on Wayland after GTK 4.14. - // Older versions of GTK do not support these values so it is safe - // to always set this. Forwards versions are uncertain so we'll have to - // reassess... - // - // Upstream issue: https://gitlab.gnome.org/GNOME/gtk/-/issues/6589 - // - // Specific details about values: - // - "opengl" - output OpenGL debug information - // - "gl-disable-gles" - disable GLES, Ghostty can't use GLES - // - "vulkan-disable" - disable Vulkan, Ghostty can't use Vulkan - // and initializing a Vulkan context was causing a longer delay - // on some systems. - _ = internal_os.setenv("GDK_DEBUG", "opengl,gl-disable-gles,vulkan-disable,gl-no-fractional"); - } else { - // Versions prior to 4.14 are a bit of an unknown for Ghostty. It - // is an environment that isn't tested well and we don't have a - // good understanding of what we may need to do. - _ = internal_os.setenv("GDK_DEBUG", "vulkan-disable"); - } - - if (version.runtimeAtLeast(4, 14, 0)) { - // We need to export GSK_RENDERER to opengl because GTK uses ngl by - // default after 4.14 - _ = internal_os.setenv("GSK_RENDERER", "opengl"); - } + // log the adwaita version + log.info("libadwaita version build={s} runtime={}.{}.{}", .{ + c.ADW_VERSION_S, + c.adw_get_major_version(), + c.adw_get_minor_version(), + c.adw_get_micro_version(), + }); // Load our configuration var config = try Config.load(core_app.alloc); @@ -165,19 +137,121 @@ pub fn init(core_app: *CoreApp, opts: Options) !App { } } - c.gtk_init(); - const display = c.gdk_display_get_default(); + var gdk_debug: struct { + /// output OpenGL debug information + opengl: bool = false, + /// disable GLES, Ghostty can't use GLES + @"gl-disable-gles": bool = false, + @"gl-no-fractional": bool = false, + /// Disabling Vulkan can improve startup times by hundreds of + /// milliseconds on some systems. We don't use Vulkan so we can just + /// disable it. + @"vulkan-disable": bool = false, + } = .{ + .opengl = config.@"gtk-opengl-debug", + }; - // If we're using libadwaita, log the version - if (adwaita.enabled(&config)) { - log.info("libadwaita version build={s} runtime={}.{}.{}", .{ - c.ADW_VERSION_S, - c.adw_get_major_version(), - c.adw_get_minor_version(), - c.adw_get_micro_version(), - }); + var gdk_disable: struct { + @"gles-api": bool = false, + /// current gtk implementation for color management is not good enough. + /// see: https://bugs.kde.org/show_bug.cgi?id=495647 + /// gtk issue: https://gitlab.gnome.org/GNOME/gtk/-/issues/6864 + @"color-mgmt": bool = true, + /// Disabling Vulkan can improve startup times by hundreds of + /// milliseconds on some systems. We don't use Vulkan so we can just + /// disable it. + vulkan: bool = false, + } = .{}; + + environment: { + if (version.runtimeAtLeast(4, 18, 0)) { + gdk_disable.@"color-mgmt" = false; + } + + if (version.runtimeAtLeast(4, 16, 0)) { + // From gtk 4.16, GDK_DEBUG is split into GDK_DEBUG and GDK_DISABLE. + // For the remainder of "why" see the 4.14 comment below. + gdk_disable.@"gles-api" = true; + gdk_disable.vulkan = true; + gdk_debug.@"gl-no-fractional" = true; + break :environment; + } + if (version.runtimeAtLeast(4, 14, 0)) { + // We need to export GDK_DEBUG to run on Wayland after GTK 4.14. + // Older versions of GTK do not support these values so it is safe + // to always set this. Forwards versions are uncertain so we'll have + // to reassess... + // + // Upstream issue: https://gitlab.gnome.org/GNOME/gtk/-/issues/6589 + gdk_debug.@"gl-disable-gles" = true; + gdk_debug.@"gl-no-fractional" = true; + gdk_debug.@"vulkan-disable" = true; + break :environment; + } + // Versions prior to 4.14 are a bit of an unknown for Ghostty. It + // is an environment that isn't tested well and we don't have a + // good understanding of what we may need to do. + gdk_debug.@"vulkan-disable" = true; + } + + { + var buf: [128]u8 = undefined; + var fmt = std.io.fixedBufferStream(&buf); + const writer = fmt.writer(); + var first: bool = true; + inline for (@typeInfo(@TypeOf(gdk_debug)).Struct.fields) |field| { + if (@field(gdk_debug, field.name)) { + if (!first) try writer.writeAll(","); + try writer.writeAll(field.name); + first = false; + } + } + try writer.writeByte(0); + const value = fmt.getWritten(); + log.warn("setting GDK_DEBUG={s}", .{value[0 .. value.len - 1]}); + _ = internal_os.setenv("GDK_DEBUG", value[0 .. value.len - 1 :0]); + } + + { + var buf: [128]u8 = undefined; + var fmt = std.io.fixedBufferStream(&buf); + const writer = fmt.writer(); + var first: bool = true; + inline for (@typeInfo(@TypeOf(gdk_disable)).Struct.fields) |field| { + if (@field(gdk_disable, field.name)) { + if (!first) try writer.writeAll(","); + try writer.writeAll(field.name); + first = false; + } + } + try writer.writeByte(0); + const value = fmt.getWritten(); + log.warn("setting GDK_DISABLE={s}", .{value[0 .. value.len - 1]}); + _ = internal_os.setenv("GDK_DISABLE", value[0 .. value.len - 1 :0]); } + if (version.runtimeAtLeast(4, 14, 0)) { + switch (config.@"gtk-gsk-renderer") { + .default => {}, + else => |renderer| { + // Force the GSK renderer to a specific value. After GTK 4.14 the + // `ngl` renderer is used by default which causes artifacts when + // used with Ghostty so it should be avoided. + log.warn("setting GSK_RENDERER={s}", .{@tagName(renderer)}); + _ = internal_os.setenv("GSK_RENDERER", @tagName(renderer)); + }, + } + } + + c.adw_init(); + + const display: *c.GdkDisplay = c.gdk_display_get_default() orelse { + // I'm unsure of any scenario where this happens. Because we don't + // want to litter null checks everywhere, we just exit here. + log.warn("gdk display is null, exiting", .{}); + std.posix.exit(1); + }; + // The "none" cursor is used for hiding the cursor const cursor_none = c.gdk_cursor_new_from_name("none", null); errdefer if (cursor_none) |cursor| c.g_object_unref(cursor); @@ -212,103 +286,38 @@ pub fn init(core_app: *CoreApp, opts: Options) !App { }; // Create our GTK Application which encapsulates our process. - const app: *c.GtkApplication = app: { - log.debug("creating GTK application id={s} single-instance={} adwaita={}", .{ - app_id, - single_instance, - adwaita, - }); - - // If not libadwaita, create a standard GTK application. - if ((comptime !adwaita.versionAtLeast(0, 0, 0)) or - !adwaita.enabled(&config)) - { - { - const provider = c.gtk_css_provider_new(); - defer c.g_object_unref(provider); - switch (config.@"window-theme") { - .system, .light => {}, - .dark => { - const settings = c.gtk_settings_get_default(); - c.g_object_set(@ptrCast(@alignCast(settings)), "gtk-application-prefer-dark-theme", true, @as([*c]const u8, null)); - - c.gtk_css_provider_load_from_resource( - provider, - "/com/mitchellh/ghostty/style-dark.css", - ); - c.gtk_style_context_add_provider_for_display( - display, - @ptrCast(provider), - c.GTK_STYLE_PROVIDER_PRIORITY_APPLICATION + 2, - ); - }, - .auto, .ghostty => { - const lum = config.background.toTerminalRGB().perceivedLuminance(); - if (lum <= 0.5) { - const settings = c.gtk_settings_get_default(); - c.g_object_set(@ptrCast(@alignCast(settings)), "gtk-application-prefer-dark-theme", true, @as([*c]const u8, null)); - - c.gtk_css_provider_load_from_resource( - provider, - "/com/mitchellh/ghostty/style-dark.css", - ); - c.gtk_style_context_add_provider_for_display( - display, - @ptrCast(provider), - c.GTK_STYLE_PROVIDER_PRIORITY_APPLICATION + 2, - ); - } - }, - } - } - - { - const provider = c.gtk_css_provider_new(); - defer c.g_object_unref(provider); - - c.gtk_css_provider_load_from_resource(provider, "/com/mitchellh/ghostty/style.css"); - c.gtk_style_context_add_provider_for_display( - display, - @ptrCast(provider), - c.GTK_STYLE_PROVIDER_PRIORITY_APPLICATION + 1, - ); - } - - break :app @as(?*c.GtkApplication, @ptrCast(c.gtk_application_new( - app_id.ptr, - app_flags, - ))) orelse return error.GtkInitFailed; - } + log.debug("creating GTK application id={s} single-instance={}", .{ + app_id, + single_instance, + }); - // Use libadwaita if requested. Using an AdwApplication lets us use - // Adwaita widgets and access things such as the color scheme. - const adw_app = @as(?*c.AdwApplication, @ptrCast(c.adw_application_new( - app_id.ptr, - app_flags, - ))) orelse return error.GtkInitFailed; - - const style_manager = c.adw_application_get_style_manager(adw_app); - c.adw_style_manager_set_color_scheme( - style_manager, - switch (config.@"window-theme") { - .auto, .ghostty => auto: { - const lum = config.background.toTerminalRGB().perceivedLuminance(); - break :auto if (lum > 0.5) - c.ADW_COLOR_SCHEME_PREFER_LIGHT - else - c.ADW_COLOR_SCHEME_PREFER_DARK; - }, - - .system => c.ADW_COLOR_SCHEME_PREFER_LIGHT, - .dark => c.ADW_COLOR_SCHEME_FORCE_DARK, - .light => c.ADW_COLOR_SCHEME_FORCE_LIGHT, + // Using an AdwApplication lets us use Adwaita widgets and access things + // such as the color scheme. + const adw_app = @as(?*c.AdwApplication, @ptrCast(c.adw_application_new( + app_id.ptr, + app_flags, + ))) orelse return error.GtkInitFailed; + errdefer c.g_object_unref(adw_app); + + const style_manager = c.adw_application_get_style_manager(adw_app); + c.adw_style_manager_set_color_scheme( + style_manager, + switch (config.@"window-theme") { + .auto, .ghostty => auto: { + const lum = config.background.toTerminalRGB().perceivedLuminance(); + break :auto if (lum > 0.5) + c.ADW_COLOR_SCHEME_PREFER_LIGHT + else + c.ADW_COLOR_SCHEME_PREFER_DARK; }, - ); + .system => c.ADW_COLOR_SCHEME_PREFER_LIGHT, + .dark => c.ADW_COLOR_SCHEME_FORCE_DARK, + .light => c.ADW_COLOR_SCHEME_FORCE_LIGHT, + }, + ); - break :app @ptrCast(adw_app); - }; - errdefer c.g_object_unref(app); - const gapp = @as(*c.GApplication, @ptrCast(app)); + const app: *c.GtkApplication = @ptrCast(adw_app); + const gapp: *c.GApplication = @ptrCast(app); // force the resource path to a known value so that it doesn't depend on // the app id and load in compiled resources @@ -364,46 +373,15 @@ pub fn init(core_app: *CoreApp, opts: Options) !App { return error.GtkApplicationRegisterFailed; } - // Perform all X11 initialization. This ultimately returns the X11 - // keyboard state but the block does more than that (i.e. setting up - // WM_CLASS). - const x11_xkb: ?x11.Xkb = x11_xkb: { - if (comptime !build_options.x11) break :x11_xkb null; - if (!x11.is_display(display)) break :x11_xkb null; - - // Set the X11 window class property (WM_CLASS) if are are on an X11 - // display. - // - // Note that we also set the program name here using g_set_prgname. - // This is how the instance name field for WM_CLASS is derived when - // calling gdk_x11_display_set_program_class; there does not seem to be - // a way to set it directly. It does not look like this is being set by - // our other app initialization routines currently, but since we're - // currently deriving its value from x11-instance-name effectively, I - // feel like gating it behind an X11 check is better intent. - // - // This makes the property show up like so when using xprop: - // - // WM_CLASS(STRING) = "ghostty", "com.mitchellh.ghostty" - // - // Append "-debug" on both when using the debug build. - // - const prgname = if (config.@"x11-instance-name") |pn| - pn - else if (builtin.mode == .Debug) - "ghostty-debug" - else - "ghostty"; - c.g_set_prgname(prgname); - c.gdk_x11_display_set_program_class(display, app_id); - - // Set up Xkb - break :x11_xkb try x11.Xkb.init(display); - }; - - // Initialize Wayland state - var wl = wayland.AppState.init(display); - if (wl) |*w| try w.register(); + // Setup our windowing protocol logic + var winproto_app = try winproto.App.init( + core_app.alloc, + display, + app_id, + &config, + ); + errdefer winproto_app.deinit(core_app.alloc); + log.debug("windowing protocol={s}", .{@tagName(winproto_app)}); // This just calls the `activate` signal but its part of the normal startup // routine so we just call it, but only if the config allows it (this allows @@ -429,8 +407,7 @@ pub fn init(core_app: *CoreApp, opts: Options) !App { .config = config, .ctx = ctx, .cursor_none = cursor_none, - .x11_xkb = x11_xkb, - .wayland = wl, + .winproto = winproto_app, .single_instance = single_instance, // If we are NOT the primary instance, then we never want to run. // This means that another instance of the GTK app is running and @@ -458,31 +435,36 @@ pub fn terminate(self: *App) void { } self.custom_css_providers.deinit(self.core_app.alloc); + self.winproto.deinit(self.core_app.alloc); + self.config.deinit(); } -/// Perform a given action. +/// Perform a given action. Returns `true` if the action was able to be +/// performed, `false` otherwise. pub fn performAction( self: *App, target: apprt.Target, comptime action: apprt.Action.Key, value: apprt.Action.Value(action), -) !void { +) !bool { switch (action) { .quit => self.quit(), .new_window => _ = try self.newWindow(switch (target) { .app => null, .surface => |v| v, }), + .toggle_maximize => self.toggleMaximize(target), .toggle_fullscreen => self.toggleFullscreen(target, value), .new_tab => try self.newTab(target), - .goto_tab => self.gotoTab(target, value), + .close_tab => try self.closeTab(target), + .goto_tab => return self.gotoTab(target, value), .move_tab => self.moveTab(target, value), .new_split => try self.newSplit(target, value), .resize_split => self.resizeSplit(target, value), .equalize_splits => self.equalizeSplits(target), - .goto_split => self.gotoSplit(target, value), + .goto_split => return self.gotoSplit(target, value), .open_config => try configpkg.edit.open(self.core_app.alloc), .config_change => self.configChange(target, value.config), .reload_config => try self.reloadConfig(target, value), @@ -492,6 +474,7 @@ pub fn performAction( .pwd => try self.setPwd(target, value), .present_terminal => self.presentTerminal(target), .initial_size => try self.setInitialSize(target, value), + .size_limit => try self.setSizeLimit(target, value), .mouse_visibility => self.setMouseVisibility(target, value), .mouse_shape => try self.setMouseShape(target, value), .mouse_over_link => self.setMouseOverLink(target, value), @@ -504,15 +487,22 @@ pub fn performAction( .close_all_windows, .toggle_quick_terminal, .toggle_visibility, - .size_limit, .cell_size, .secure_input, .key_sequence, .render_inspector, .renderer_health, .color_change, - => log.warn("unimplemented action={}", .{action}), + .prompt_title, + => { + log.warn("unimplemented action={}", .{action}); + return false; + }, } + + // We can assume it was handled because all unknown/unimplemented actions + // are caught above. + return true; } fn newTab(_: *App, target: apprt.Target) !void { @@ -532,24 +522,41 @@ fn newTab(_: *App, target: apprt.Target) !void { } } -fn gotoTab(_: *App, target: apprt.Target, tab: apprt.action.GotoTab) void { +fn closeTab(_: *App, target: apprt.Target) !void { switch (target) { .app => {}, + .surface => |v| { + const tab = v.rt_surface.container.tab() orelse { + log.info( + "close_tab invalid for container={s}", + .{@tagName(v.rt_surface.container)}, + ); + return; + }; + + tab.closeWithConfirmation(); + }, + } +} + +fn gotoTab(_: *App, target: apprt.Target, tab: apprt.action.GotoTab) bool { + switch (target) { + .app => return false, .surface => |v| { const window = v.rt_surface.container.window() orelse { log.info( "gotoTab invalid for container={s}", .{@tagName(v.rt_surface.container)}, ); - return; + return false; }; - switch (tab) { + return switch (tab) { .previous => window.gotoPreviousTab(v.rt_surface), .next => window.gotoNextTab(v.rt_surface), .last => window.gotoLastTab(), else => window.gotoTab(@intCast(@intFromEnum(tab))), - } + }; }, } } @@ -603,18 +610,22 @@ fn gotoSplit( _: *const App, target: apprt.Target, direction: apprt.action.GotoSplit, -) void { +) bool { switch (target) { - .app => {}, + .app => return false, .surface => |v| { - const s = v.rt_surface.container.split() orelse return; + const s = v.rt_surface.container.split() orelse return false; const map = s.directionMap(switch (v.rt_surface.container) { .split_tl => .top_left, .split_br => .bottom_right, .none, .tab_ => unreachable, }); - const surface_ = map.get(direction) orelse return; - if (surface_) |surface| surface.grabFocus(); + const surface_ = map.get(direction) orelse return false; + if (surface_) |surface| { + surface.grabFocus(); + return true; + } + return false; }, } } @@ -658,6 +669,22 @@ fn controlInspector( surface.controlInspector(mode); } +fn toggleMaximize(_: *App, target: apprt.Target) void { + switch (target) { + .app => {}, + .surface => |v| { + const window = v.rt_surface.container.window() orelse { + log.info( + "toggleMaximize invalid for container={s}", + .{@tagName(v.rt_surface.container)}, + ); + return; + }; + window.toggleMaximize(); + }, + } +} + fn toggleFullscreen( _: *App, target: apprt.Target, @@ -805,6 +832,23 @@ fn setInitialSize( } } +fn setSizeLimit( + _: *App, + target: apprt.Target, + value: apprt.action.SizeLimit, +) !void { + switch (target) { + .app => {}, + .surface => |v| try v.rt_surface.setSizeLimits(.{ + .width = value.min_width, + .height = value.min_height, + }, if (value.max_width > 0) .{ + .width = value.max_width, + .height = value.max_height, + } else null), + } +} + fn showDesktopNotification( self: *App, target: apprt.Target, @@ -847,9 +891,10 @@ fn configChange( new_config: *const Config, ) void { switch (target) { - .surface => |surface| { - if (surface.rt_surface.container.window()) |window| window.syncAppearance(new_config) catch |err| { - log.warn("error syncing appearance changes to window err={}", .{err}); + .surface => |surface| surface: { + const window = surface.rt_surface.container.window() orelse break :surface; + window.updateConfig(new_config) catch |err| { + log.warn("error updating config for window err={}", .{err}); }; }, @@ -868,11 +913,9 @@ fn configChange( // App changes needs to show a toast that our configuration // has reloaded. - if (adwaita.enabled(&self.config)) { - if (self.core_app.focusedSurface()) |core_surface| { - const surface = core_surface.rt_surface; - if (surface.container.window()) |window| window.onConfigReloaded(); - } + if (self.core_app.focusedSurface()) |core_surface| { + const surface = core_surface.rt_surface; + if (surface.container.window()) |window| window.onConfigReloaded(); } }, } @@ -1185,7 +1228,8 @@ pub fn run(self: *App) !void { self.transient_cgroup_base = path; } else log.debug("cgroup isolation disabled config={}", .{self.config.@"linux-cgroup"}); - // Setup our D-Bus connection for listening to settings changes. + // Setup our D-Bus connection for listening to settings changes, + // and asynchronously request the initial color scheme self.initDbus(); // Setup our menu items @@ -1193,9 +1237,6 @@ pub fn run(self: *App) !void { self.initMenu(); self.initContextMenu(); - // Setup our initial color scheme - self.colorSchemeEvent(self.getColorScheme()); - // On startup, we want to check for configuration errors right away // so we can show our error window. We also need to setup other initial // state. @@ -1243,6 +1284,22 @@ fn initDbus(self: *App) void { self, null, ); + + // Request the initial color scheme asynchronously. + c.g_dbus_connection_call( + dbus, + "org.freedesktop.portal.Desktop", + "/org/freedesktop/portal/desktop", + "org.freedesktop.portal.Settings", + "ReadOne", + c.g_variant_new("(ss)", "org.freedesktop.appearance", "color-scheme"), + c.G_VARIANT_TYPE("(v)"), + c.G_DBUS_CALL_FLAGS_NONE, + -1, + null, + dbusColorSchemeCallback, + self, + ); } // This timeout function is started when no surfaces are open. It can be @@ -1480,93 +1537,58 @@ fn gtkWindowIsActive( core_app.focusEvent(false); } -/// Call a D-Bus method to determine the current color scheme. If there -/// is any error at any point we'll log the error and return "light" -pub fn getColorScheme(self: *App) apprt.ColorScheme { - const dbus_connection = c.g_application_get_dbus_connection(@ptrCast(self.app)); +fn dbusColorSchemeCallback( + source_object: [*c]c.GObject, + res: ?*c.GAsyncResult, + ud: ?*anyopaque, +) callconv(.C) void { + const self: *App = @ptrCast(@alignCast(ud.?)); + const dbus: *c.GDBusConnection = @ptrCast(source_object); var err: ?*c.GError = null; defer if (err) |e| c.g_error_free(e); - const value = c.g_dbus_connection_call_sync( - dbus_connection, - "org.freedesktop.portal.Desktop", - "/org/freedesktop/portal/desktop", - "org.freedesktop.portal.Settings", - "ReadOne", - c.g_variant_new("(ss)", "org.freedesktop.appearance", "color-scheme"), - c.G_VARIANT_TYPE("(v)"), - c.G_DBUS_CALL_FLAGS_NONE, - -1, - null, - &err, - ) orelse { - if (err) |e| { - // If ReadOne is not yet implemented, fall back to deprecated "Read" method - // Error code: GDBus.Error:org.freedesktop.DBus.Error.UnknownMethod: No such method “ReadOne” - if (e.code == 19) { - return self.getColorSchemeDeprecated(); + if (c.g_dbus_connection_call_finish(dbus, res, &err)) |value| { + if (c.g_variant_is_of_type(value, c.G_VARIANT_TYPE("(v)")) == 1) { + var inner: ?*c.GVariant = null; + c.g_variant_get(value, "(v)", &inner); + defer c.g_variant_unref(inner); + if (c.g_variant_is_of_type(inner, c.G_VARIANT_TYPE("u")) == 1) { + self.colorSchemeEvent(if (c.g_variant_get_uint32(inner) == 1) + .dark + else + .light); + return; } - // Otherwise, log the error and return .light - log.err("unable to get current color scheme: {s}", .{e.message}); } - return .light; - }; - defer c.g_variant_unref(value); - - if (c.g_variant_is_of_type(value, c.G_VARIANT_TYPE("(v)")) == 1) { - var inner: ?*c.GVariant = null; - c.g_variant_get(value, "(v)", &inner); - defer c.g_variant_unref(inner); - if (c.g_variant_is_of_type(inner, c.G_VARIANT_TYPE("u")) == 1) { - return if (c.g_variant_get_uint32(inner) == 1) .dark else .light; + } else if (err) |e| { + // If ReadOne is not yet implemented, fall back to deprecated "Read" method + // Error code: GDBus.Error:org.freedesktop.DBus.Error.UnknownMethod: No such method “ReadOne” + if (self.dbus_color_scheme_retry and e.code == 19) { + self.dbus_color_scheme_retry = false; + c.g_dbus_connection_call( + dbus, + "org.freedesktop.portal.Desktop", + "/org/freedesktop/portal/desktop", + "org.freedesktop.portal.Settings", + "Read", + c.g_variant_new("(ss)", "org.freedesktop.appearance", "color-scheme"), + c.G_VARIANT_TYPE("(v)"), + c.G_DBUS_CALL_FLAGS_NONE, + -1, + null, + dbusColorSchemeCallback, + self, + ); + return; } - } - return .light; -} - -/// Call the deprecated D-Bus "Read" method to determine the current color scheme. If -/// there is any error at any point we'll log the error and return "light" -fn getColorSchemeDeprecated(self: *App) apprt.ColorScheme { - const dbus_connection = c.g_application_get_dbus_connection(@ptrCast(self.app)); - var err: ?*c.GError = null; - defer if (err) |e| c.g_error_free(e); - - const value = c.g_dbus_connection_call_sync( - dbus_connection, - "org.freedesktop.portal.Desktop", - "/org/freedesktop/portal/desktop", - "org.freedesktop.portal.Settings", - "Read", - c.g_variant_new("(ss)", "org.freedesktop.appearance", "color-scheme"), - c.G_VARIANT_TYPE("(v)"), - c.G_DBUS_CALL_FLAGS_NONE, - -1, - null, - &err, - ) orelse { - if (err) |e| log.err("Read method failed: {s}", .{e.message}); - return .light; - }; - defer c.g_variant_unref(value); - - if (c.g_variant_is_of_type(value, c.G_VARIANT_TYPE("(v)")) == 1) { - var inner: ?*c.GVariant = null; - c.g_variant_get(value, "(v)", &inner); - defer if (inner) |i| c.g_variant_unref(i); - - if (inner) |i| { - const child = c.g_variant_get_child_value(i, 0) orelse { - return .light; - }; - defer c.g_variant_unref(child); - - const val = c.g_variant_get_uint32(child); - return if (val == 1) .dark else .light; - } + // Otherwise, log the error and return .light + log.warn("unable to get current color scheme: {s}", .{e.message}); } - return .light; + + // Fall back + self.colorSchemeEvent(.light); } /// This will be called by D-Bus when the style changes between light & dark. @@ -1731,18 +1753,17 @@ fn initActions(self: *App) void { } } -/// This sets the self.menu property to the application menu that can be -/// shared by all application windows. -fn initMenu(self: *App) void { - const menu = c.g_menu_new(); - errdefer c.g_object_unref(menu); - +/// Initializes and populates the provided GMenu with sections and actions. +/// This function is used to set up the application's menu structure, either for +/// the main menu button or as a context menu when window decorations are disabled. +fn initMenuContent(menu: *c.GMenu) void { { const section = c.g_menu_new(); defer c.g_object_unref(section); c.g_menu_append_section(menu, null, @ptrCast(@alignCast(section))); c.g_menu_append(section, "New Window", "win.new_window"); c.g_menu_append(section, "New Tab", "win.new_tab"); + c.g_menu_append(section, "Close Tab", "win.close_tab"); c.g_menu_append(section, "Split Right", "win.split_right"); c.g_menu_append(section, "Split Down", "win.split_down"); c.g_menu_append(section, "Close Window", "win.close"); @@ -1757,13 +1778,14 @@ fn initMenu(self: *App) void { c.g_menu_append(section, "Reload Configuration", "app.reload-config"); c.g_menu_append(section, "About Ghostty", "win.about"); } +} - // { - // const section = c.g_menu_new(); - // defer c.g_object_unref(section); - // c.g_menu_append_submenu(menu, "File", @ptrCast(@alignCast(section))); - // } - +/// This sets the self.menu property to the application menu that can be +/// shared by all application windows. +fn initMenu(self: *App) void { + const menu = c.g_menu_new(); + errdefer c.g_object_unref(menu); + initMenuContent(@ptrCast(menu)); self.menu = menu; } @@ -1771,7 +1793,13 @@ fn initContextMenu(self: *App) void { const menu = c.g_menu_new(); errdefer c.g_object_unref(menu); - createContextMenuCopyPasteSection(menu, false); + { + const section = c.g_menu_new(); + defer c.g_object_unref(section); + c.g_menu_append_section(menu, null, @ptrCast(@alignCast(section))); + c.g_menu_append(section, "Copy", "win.copy"); + c.g_menu_append(section, "Paste", "win.paste"); + } { const section = c.g_menu_new(); @@ -1789,21 +1817,21 @@ fn initContextMenu(self: *App) void { c.g_menu_append(section, "Terminal Inspector", "win.toggle_inspector"); } - self.context_menu = menu; -} - -fn createContextMenuCopyPasteSection(menu: ?*c.GMenu, has_selection: bool) void { const section = c.g_menu_new(); defer c.g_object_unref(section); - c.g_menu_prepend_section(menu, null, @ptrCast(@alignCast(section))); - // FIXME: Feels really hackish, but disabling sensitivity on this doesn't seems to work(?) - c.g_menu_append(section, "Copy", if (has_selection) "win.copy" else "noop"); - c.g_menu_append(section, "Paste", "win.paste"); + const submenu = c.g_menu_new(); + defer c.g_object_unref(submenu); + + initMenuContent(@ptrCast(submenu)); + c.g_menu_append_submenu(section, "Menu", @ptrCast(@alignCast(submenu))); + c.g_menu_append_section(menu, null, @ptrCast(@alignCast(section))); + + self.context_menu = menu; } -pub fn refreshContextMenu(self: *App, has_selection: bool) void { - c.g_menu_remove(self.context_menu, 0); - createContextMenuCopyPasteSection(self.context_menu, has_selection); +pub fn refreshContextMenu(_: *App, window: ?*c.GtkWindow, has_selection: bool) void { + const action: ?*c.GSimpleAction = @ptrCast(c.g_action_map_lookup_action(@ptrCast(window), "copy")); + c.g_simple_action_set_enabled(action, if (has_selection) 1 else 0); } fn isValidAppId(app_id: [:0]const u8) bool { diff --git a/src/apprt/gtk/Builder.zig b/src/apprt/gtk/Builder.zig new file mode 100644 index 0000000000..ffacd3adff --- /dev/null +++ b/src/apprt/gtk/Builder.zig @@ -0,0 +1,72 @@ +/// Wrapper around GTK's builder APIs that perform some comptime checks. +const Builder = @This(); + +const std = @import("std"); + +const gtk = @import("gtk"); +const gobject = @import("gobject"); + +resource_name: [:0]const u8, +builder: ?*gtk.Builder, + +pub fn init(comptime name: []const u8, comptime kind: enum { blp, ui }) Builder { + comptime { + switch (kind) { + .blp => { + // Use @embedFile to make sure that the file exists at compile + // time. Zig _should_ discard the data so that it doesn't end + // up in the final executable. At runtime we will load the data + // from a GResource. + _ = @embedFile("ui/" ++ name ++ ".blp"); + + // Check to make sure that our file is listed as a + // `blueprint_file` in `gresource.zig`. If it isn't Ghostty + // could crash at runtime when we try and load a nonexistent + // GResource. + const gresource = @import("gresource.zig"); + for (gresource.blueprint_files) |blueprint_file| { + if (std.mem.eql(u8, blueprint_file, name)) break; + } else @compileError("missing blueprint file '" ++ name ++ "' in gresource.zig"); + }, + .ui => { + // Use @embedFile to make sure that the file exists at compile + // time. Zig _should_ discard the data so that it doesn't end + // up in the final executable. At runtime we will load the data + // from a GResource. + _ = @embedFile("ui/" ++ name ++ ".ui"); + + // Check to make sure that our file is listed as a `ui_file` in + // `gresource.zig`. If it isn't Ghostty could crash at runtime + // when we try and load a nonexistent GResource. + const gresource = @import("gresource.zig"); + for (gresource.ui_files) |ui_file| { + if (std.mem.eql(u8, ui_file, name)) break; + } else @compileError("missing ui file '" ++ name ++ "' in gresource.zig"); + }, + } + } + + return .{ + .resource_name = "/com/mitchellh/ghostty/ui/" ++ name ++ ".ui", + .builder = null, + }; +} + +pub fn setWidgetClassTemplate(self: *const Builder, class: *gtk.WidgetClass) void { + class.setTemplateFromResource(self.resource_name); +} + +pub fn getObject(self: *Builder, name: [:0]const u8) ?*gobject.Object { + const builder = builder: { + if (self.builder) |builder| break :builder builder; + const builder = gtk.Builder.newFromResource(self.resource_name); + self.builder = builder; + break :builder builder; + }; + + return builder.getObject(name); +} + +pub fn deinit(self: *const Builder) void { + if (self.builder) |builder| builder.unref(); +} diff --git a/src/apprt/gtk/Split.zig b/src/apprt/gtk/Split.zig index 2d428acb2a..8ddadfd131 100644 --- a/src/apprt/gtk/Split.zig +++ b/src/apprt/gtk/Split.zig @@ -313,11 +313,7 @@ pub fn directionMap(self: *const Split, from: Side) DirectionMap { if (self.directionPrevious(from)) |prev| { result.put(.previous, prev.surface); if (!prev.wrapped) { - // This behavior matches the behavior of macOS at the time of writing - // this. There is an open issue (#524) to make this depend on the - // actual physical location of the current split. result.put(.up, prev.surface); - result.put(.left, prev.surface); } } @@ -325,13 +321,57 @@ pub fn directionMap(self: *const Split, from: Side) DirectionMap { result.put(.next, next.surface); if (!next.wrapped) { result.put(.down, next.surface); - result.put(.right, next.surface); } } + if (self.directionLeft(from)) |left| { + result.put(.left, left); + } + + if (self.directionRight(from)) |right| { + result.put(.right, right); + } + return result; } +fn directionLeft(self: *const Split, from: Side) ?*Surface { + switch (from) { + .bottom_right => { + switch (self.orientation) { + .horizontal => return self.top_left.deepestSurface(.bottom_right), + .vertical => return directionLeft( + self.container.split() orelse return null, + .bottom_right, + ), + } + }, + .top_left => return directionLeft( + self.container.split() orelse return null, + .bottom_right, + ), + } +} + +fn directionRight(self: *const Split, from: Side) ?*Surface { + switch (from) { + .top_left => { + switch (self.orientation) { + .horizontal => return self.bottom_right.deepestSurface(.top_left), + .vertical => return directionRight( + self.container.split() orelse return null, + .top_left, + ), + } + }, + .bottom_right => return directionRight( + self.container.split() orelse return null, + .top_left, + ), + } +} + + fn directionPrevious(self: *const Split, from: Side) ?struct { surface: *Surface, wrapped: bool, diff --git a/src/apprt/gtk/Surface.zig b/src/apprt/gtk/Surface.zig index 056a3f40b8..42c8278a23 100644 --- a/src/apprt/gtk/Surface.zig +++ b/src/apprt/gtk/Surface.zig @@ -25,7 +25,6 @@ const ResizeOverlay = @import("ResizeOverlay.zig"); const inspector = @import("inspector.zig"); const gtk_key = @import("key.zig"); const c = @import("c.zig").c; -const x11 = @import("x11.zig"); const log = std.log.scoped(.gtk_surface); @@ -347,6 +346,11 @@ cursor: ?*c.GdkCursor = null, /// pass it to GTK. title_text: ?[:0]const u8 = null, +/// Our current working directory. We use this value for setting tooltips in +/// the headerbar subtitle if we have focus. When set, the text in this buf +/// will be null-terminated because we need to pass it to GTK. +pwd: ?[:0]const u8 = null, + /// The timer used to delay title updates in order to prevent flickering. update_title_timer: ?c.guint = null, @@ -364,10 +368,9 @@ cursor_pos: apprt.CursorPos, inspector: ?*inspector.Inspector = null, /// Key input states. See gtkKeyPressed for detailed descriptions. -in_keypress: bool = false, +in_keyevent: IMKeyEvent = .false, im_context: *c.GtkIMContext, im_composing: bool = false, -im_commit_buffered: bool = false, im_buf: [128]u8 = undefined, im_len: u7 = 0, @@ -375,6 +378,20 @@ im_len: u7 = 0, /// details on what this is. cgroup_path: ?[]const u8 = null, +/// The state of the key event while we're doing IM composition. +/// See gtkKeyPressed for detailed descriptions. +pub const IMKeyEvent = enum { + /// Not in a key event. + false, + + /// In a key event but im_composing was either true or false + /// prior to the calling IME processing. This is important to + /// work around different input methods calling commit and + /// preedit end in a different order. + composing, + not_composing, +}; + /// Configuration used for initializing the surface. We have to copy some /// data since initialization is delayed with GTK (on realize). pub const InitConfig = struct { @@ -492,6 +509,17 @@ pub fn init(self: *Surface, app: *App, opts: Options) !void { c.gtk_widget_set_focusable(gl_area, 1); c.gtk_widget_set_focus_on_click(gl_area, 1); + // Set up to handle items being dropped on our surface. Files can be dropped + // from Nautilus and strings can be dropped from many programs. + const drop_target = c.gtk_drop_target_new(c.G_TYPE_INVALID, c.GDK_ACTION_COPY); + errdefer c.g_object_unref(drop_target); + var drop_target_types = [_]c.GType{ + c.gdk_file_list_get_type(), + c.G_TYPE_STRING, + }; + c.gtk_drop_target_set_gtypes(drop_target, @ptrCast(&drop_target_types), drop_target_types.len); + c.gtk_widget_add_controller(@ptrCast(overlay), @ptrCast(drop_target)); + // Inherit the parent's font size if we have a parent. const font_size: ?font.face.DesiredSize = font_size: { if (!app.config.@"window-inherit-font-size") break :font_size null; @@ -545,7 +573,7 @@ pub fn init(self: *Surface, app: *App, opts: Options) !void { .font_size = font_size, .init_config = init_config, .size = .{ .width = 800, .height = 600 }, - .cursor_pos = .{ .x = 0, .y = 0 }, + .cursor_pos = .{ .x = -1, .y = -1 }, .im_context = im_context, .cgroup_path = cgroup_path, }; @@ -574,6 +602,7 @@ pub fn init(self: *Surface, app: *App, opts: Options) !void { _ = c.g_signal_connect_data(im_context, "preedit-changed", c.G_CALLBACK(>kInputPreeditChanged), self, null, c.G_CONNECT_DEFAULT); _ = c.g_signal_connect_data(im_context, "preedit-end", c.G_CALLBACK(>kInputPreeditEnd), self, null, c.G_CONNECT_DEFAULT); _ = c.g_signal_connect_data(im_context, "commit", c.G_CALLBACK(>kInputCommit), self, null, c.G_CONNECT_DEFAULT); + _ = c.g_signal_connect_data(drop_target, "drop", c.G_CALLBACK(>kDrop), self, null, c.G_CONNECT_DEFAULT); } fn realize(self: *Surface) !void { @@ -618,9 +647,6 @@ fn realize(self: *Surface) !void { try self.core_surface.setFontSize(size); } - // Set the initial color scheme - try self.core_surface.colorSchemeCallback(self.app.getColorScheme()); - // Note we're realized self.realized = true; } @@ -628,6 +654,7 @@ fn realize(self: *Surface) !void { pub fn deinit(self: *Surface) void { self.init_config.deinit(self.app.core_app.alloc); if (self.title_text) |title| self.app.core_app.alloc.free(title); + if (self.pwd) |pwd| self.app.core_app.alloc.free(pwd); // We don't allocate anything if we aren't realized. if (!self.realized) return; @@ -840,6 +867,28 @@ pub fn setInitialWindowSize(self: *const Surface, width: u32, height: u32) !void ); } +pub fn setSizeLimits(self: *const Surface, min: apprt.SurfaceSize, max_: ?apprt.SurfaceSize) !void { + + // There's no support for setting max size at the moment. + _ = max_; + + // If we are within a split, do not set the size. + if (self.container.split() != null) return; + + // This operation only makes sense if we're within a window view + // hierarchy and we're the first tab in the window. + const window = self.container.window() orelse return; + if (window.notebook.nPages() > 1) return; + + // Note: this doesn't properly take into account the window decorations. + // I'm not currently sure how to do that. + c.gtk_widget_set_size_request( + @ptrCast(window.window), + @intCast(min.width), + @intCast(min.height), + ); +} + pub fn grabFocus(self: *Surface) void { if (self.container.tab()) |tab| { // If any other surface was focused and zoomed in, set it to non zoomed in @@ -876,7 +925,7 @@ fn updateTitleLabels(self: *Surface) void { // I don't know a way around this yet. I've tried re-hiding the // cursor after setting the title but it doesn't work, I think // due to some gtk event loop things... - c.gtk_window_set_title(window.window, title.ptr); + window.setTitle(title); } } } @@ -929,11 +978,27 @@ pub fn getTitle(self: *Surface) ?[:0]const u8 { return null; } +/// Set the current working directory of the surface. +/// +/// In addition, update the tab's tooltip text, and if we are the focused child, +/// update the subtitle of the containing window. pub fn setPwd(self: *Surface, pwd: [:0]const u8) !void { - // If we have a tab and are the focused child, then we have to update the tab if (self.container.tab()) |tab| { tab.setTooltipText(pwd); + + if (tab.focus_child == self) { + if (self.container.window()) |window| { + if (self.app.config.@"window-subtitle" == .@"working-directory") window.setSubtitle(pwd); + } + } } + + const alloc = self.app.core_app.alloc; + + // Failing to set the surface's current working directory is not a big + // deal since we just used our slice parameter which is the same value. + if (self.pwd) |old| alloc.free(old); + self.pwd = alloc.dupeZ(u8, pwd) catch null; } pub fn setMouseShape( @@ -1082,7 +1147,7 @@ pub fn setClipboardString( c.gdk_clipboard_set_text(clipboard, val.ptr); // We only toast if we are copying to the standard clipboard. if (clipboard_type == .standard and - self.app.config.@"adw-toast".@"clipboard-copy") + self.app.config.@"app-notifications".@"clipboard-copy") { if (self.container.window()) |window| window.sendToast("Copied to clipboard"); @@ -1205,10 +1270,12 @@ fn showContextMenu(self: *Surface, x: f32, y: f32) void { return; }; + // Convert surface coordinate into coordinate space of the + // context menu's parent var point: c.graphene_point_t = .{ .x = x, .y = y }; if (c.gtk_widget_compute_point( self.primaryWidget(), - @ptrCast(window.window), + c.gtk_widget_get_parent(@ptrCast(window.context_menu)), &c.GRAPHENE_POINT_INIT(point.x, point.y), @ptrCast(&point), ) == 0) { @@ -1224,7 +1291,7 @@ fn showContextMenu(self: *Surface, x: f32, y: f32) void { }; c.gtk_popover_set_pointing_to(@ptrCast(@alignCast(window.context_menu)), &rect); - self.app.refreshContextMenu(self.core_surface.hasSelection()); + self.app.refreshContextMenu(window.window, self.core_surface.hasSelection()); c.gtk_popover_popup(@ptrCast(@alignCast(window.context_menu))); } @@ -1328,6 +1395,12 @@ fn gtkResize(area: *c.GtkGLArea, width: c.gint, height: c.gint, ud: ?*anyopaque) return; }; + if (self.container.window()) |window| { + window.winproto.resizeEvent() catch |err| { + log.warn("failed to notify window protocol of resize={}", .{err}); + }; + } + self.resize_overlay.maybeShow(); } } @@ -1433,31 +1506,37 @@ fn gtkMouseMotion( .y = @floatCast(scaled.y), }; - // When the GLArea is resized under the mouse, GTK issues a mouse motion - // event. This has the unfortunate side effect of causing focus to potentially - // change when `focus-follows-mouse` is enabled. To prevent this, we check - // if the cursor is still in the same place as the last event and only grab - // focus if it has moved. + // There seem to be at least two cases where GTK issues a mouse motion + // event without the cursor actually moving: + // 1. GLArea is resized under the mouse. This has the unfortunate + // side effect of causing focus to potentially change when + // `focus-follows-mouse` is enabled. + // 2. The window title is updated. This can cause the mouse to unhide + // incorrectly when hide-mouse-when-typing is enabled. + // To prevent incorrect behavior, we'll only grab focus and + // continue with callback logic if the cursor has actually moved. const is_cursor_still = @abs(self.cursor_pos.x - pos.x) < 1 and @abs(self.cursor_pos.y - pos.y) < 1; - // If we don't have focus, and we want it, grab it. - const gl_widget = @as(*c.GtkWidget, @ptrCast(self.gl_area)); - if (!is_cursor_still and c.gtk_widget_has_focus(gl_widget) == 0 and self.app.config.@"focus-follows-mouse") { - self.grabFocus(); - } + if (!is_cursor_still) { + // If we don't have focus, and we want it, grab it. + const gl_widget = @as(*c.GtkWidget, @ptrCast(self.gl_area)); + if (c.gtk_widget_has_focus(gl_widget) == 0 and self.app.config.@"focus-follows-mouse") { + self.grabFocus(); + } - // Our pos changed, update - self.cursor_pos = pos; + // Our pos changed, update + self.cursor_pos = pos; - // Get our modifiers - const gtk_mods = c.gdk_event_get_modifier_state(event); - const mods = gtk_key.translateMods(gtk_mods); + // Get our modifiers + const gtk_mods = c.gdk_event_get_modifier_state(event); + const mods = gtk_key.translateMods(gtk_mods); - self.core_surface.cursorPosCallback(self.cursor_pos, mods) catch |err| { - log.err("error in cursor pos callback err={}", .{err}); - return; - }; + self.core_surface.cursorPosCallback(self.cursor_pos, mods) catch |err| { + log.err("error in cursor pos callback err={}", .{err}); + return; + }; + } } fn gtkMouseLeave( @@ -1537,30 +1616,36 @@ fn gtkKeyReleased( )) 1 else 0; } -/// Key press event. This is where we do ALL of our key handling, -/// translation to keyboard layouts, dead key handling, etc. Key handling -/// is complicated so this comment will explain what's going on. +/// Key press event (press or release). /// /// At a high level, we want to construct an `input.KeyEvent` and /// pass that to `keyCallback`. At a low level, this is more complicated /// than it appears because we need to construct all of this information /// and its not given to us. /// -/// For press events, we run the keypress through the input method context -/// in order to determine if we're in a dead key state, completed unicode -/// char, etc. This all happens through various callbacks: preedit, commit, -/// etc. These inspect "in_keypress" if they have to and set some instance -/// state. +/// For all events, we run the GdkEvent through the input method context. +/// This allows the input method to capture the event and trigger +/// callbacks such as preedit, commit, etc. +/// +/// There are a couple important aspects to the prior paragraph: we must +/// send ALL events through the input method context. This is because +/// input methods use both key press and key release events to determine +/// the state of the input method. For example, fcitx uses key release +/// events on modifiers (i.e. ctrl+shift) to switch the input method. +/// +/// We set some state to note we're in a key event (self.in_keyevent) +/// because some of the input method callbacks change behavior based on +/// this state. For example, we don't want to send character events +/// like "a" via the input "commit" event if we're actively processing +/// a keypress because we'd lose access to the keycode information. +/// However, a "commit" event may still happen outside of a keypress +/// event from e.g. a tablet or on-screen keyboard. /// -/// We then take all of the information in order to determine if we have +/// Finally, we take all of the information in order to determine if we have /// a unicode character or if we have to map the keyval to a code to /// get the underlying logical key, etc. /// -/// Finally, we can emit the keyCallback. -/// -/// Note we ALSO have an IMContext attached directly to the widget -/// which can emit preedit and commit callbacks. But, if we're not -/// in a keypress, we let those automatically work. +/// Then we can emit the keyCallback. pub fn keyEvent( self: *Surface, action: input.Action, @@ -1569,26 +1654,15 @@ pub fn keyEvent( keycode: c.guint, gtk_mods: c.GdkModifierType, ) bool { + // log.warn("GTKIM: keyEvent action={}", .{action}); const event = c.gtk_event_controller_get_current_event( @ptrCast(ec_key), ) orelse return false; - const keyval_unicode = c.gdk_keyval_to_unicode(keyval); - - // Get the unshifted unicode value of the keyval. This is used - // by the Kitty keyboard protocol. - const keyval_unicode_unshifted: u21 = gtk_key.keyvalUnicodeUnshifted( - @ptrCast(self.gl_area), - event, - keycode, - ); - - // We always reset our committed text when ending a keypress so that - // future keypresses don't think we have a commit event. - defer self.im_len = 0; - - // We only want to send the event through the IM context if we're a press - if (action == .press or action == .repeat) { + // The block below is all related to input method handling. See the function + // comment for some high level details and then the comments within + // the block for more specifics. + { // This can trigger an input method so we need to notify the im context // where the cursor is so it can render the dropdowns in the correct // place. @@ -1600,41 +1674,98 @@ pub fn keyEvent( .height = 1, }); - // We mark that we're in a keypress event. We use this in our - // IM commit callback to determine if we need to send a char callback - // to the core surface or not. - self.in_keypress = true; - defer self.in_keypress = false; - - // Pass the event through the IM controller to handle dead key states. - // Filter is true if the event was handled by the IM controller. - const im_handled = c.gtk_im_context_filter_keypress(self.im_context, event) != 0; - // log.warn("im_handled={} im_len={} im_composing={}", .{ im_handled, self.im_len, self.im_composing }); - - // If this is a dead key, then we're composing a character and - // we need to set our proper preedit state. - if (self.im_composing) preedit: { - const text = self.im_buf[0..self.im_len]; - self.core_surface.preeditCallback(text) catch |err| { - log.err("error in preedit callback err={}", .{err}); - break :preedit; - }; - - // If we're composing then we don't want to send the key - // event to the core surface so we always return immediately. - if (im_handled) return true; - } else { - // If we aren't composing, then we set our preedit to - // empty no matter what. - self.core_surface.preeditCallback(null) catch {}; - - // If the IM handled this and we have no text, then we just - // return because this probably just changed the input method - // or something. - if (im_handled and self.im_len == 0) return true; + // We note that we're in a keypress because we want some logic to + // depend on this. For example, we don't want to send character events + // like "a" via the input "commit" event if we're actively processing + // a keypress because we'd lose access to the keycode information. + // + // We have to maintain some additional state here of whether we + // were composing because different input methods call the callbacks + // in different orders. For example, ibus calls commit THEN preedit + // end but simple calls preedit end THEN commit. + self.in_keyevent = if (self.im_composing) .composing else .not_composing; + defer self.in_keyevent = .false; + + // Pass the event through the input method which returns true if handled. + // Confusingly, not all events handled by the input method result + // in this returning true so we have to maintain some additional + // state about whether we were composing or not to determine if + // we should proceed with key encoding. + // + // Cases where the input method does not mark the event as handled: + // + // - If we change the input method via keypress while we have preedit + // text, the input method will commit the pending text but will not + // mark it as handled. We use the `.composing` state to detect + // this case. + // + // - If we switch input methods (i.e. via ctrl+shift with fcitx), + // the input method will handle the key release event but will not + // mark it as handled. I don't know any way to detect this case so + // it will result in a key event being sent to the key callback. + // For Kitty text encoding, this will result in modifiers being + // triggered despite being technically consumed. At the time of + // writing, both Kitty and Alacritty have the same behavior. I + // know of no way to fix this. + const im_handled = c.gtk_im_context_filter_keypress( + self.im_context, + event, + ) != 0; + // log.warn("GTKIM: im_handled={} im_len={} im_composing={}", .{ + // im_handled, + // self.im_len, + // self.im_composing, + // }); + + // If the input method handled the event, you would think we would + // never proceed with key encoding for Ghostty but that is not the + // case. Input methods will handle basic character encoding like + // typing "a" and we want to associate that with the key event. + // So we have to check additional state to determine if we exit. + if (im_handled) { + // If we are composing then we're in a preedit state and do + // not want to encode any keys. For example: type a deadkey + // such as single quote on a US international keyboard layout. + if (self.im_composing) return true; + + // If we were composing and now we're not it means that we committed + // the text. We also don't want to encode a key event for this. + // Example: enable Japanese input method, press "konn" and then + // press enter. The final enter should not be encoded and "konn" + // (in hiragana) should be written as "こん". + if (self.in_keyevent == .composing) return true; + + // Not composing and our input method buffer is empty. This could + // mean that the input method reacted to this event by activating + // an onscreen keyboard or something equivalent. We don't know. + // But the input method handled it and didn't give us text so + // we will just assume we should not encode this. This handles a + // real scenario when ibus starts the emoji input method + // (super+.). + if (self.im_len == 0) return true; } + + // At this point, for the sake of explanation of internal state: + // it is possible that im_len > 0 and im_composing == false. This + // means that we received a commit event from the input method that + // we want associated with the key event. This is common: its how + // basic character translation for simple inputs like "a" work. } + // We always reset the length of the im buffer. There's only one scenario + // we reach this point with im_len > 0 and that's if we received a commit + // event from the input method. We don't want to keep that state around + // since we've handled it here. + defer self.im_len = 0; + + // Get the keyvals for this event. + const keyval_unicode = c.gdk_keyval_to_unicode(keyval); + const keyval_unicode_unshifted: u21 = gtk_key.keyvalUnicodeUnshifted( + @ptrCast(self.gl_area), + event, + keycode, + ); + // We want to get the physical unmapped key to process physical keybinds. // (These are keybinds explicitly marked as requesting physical mapping). const physical_key = keycode: for (input.keycodes.entries) |entry| { @@ -1643,11 +1774,11 @@ pub fn keyEvent( // Get our modifier for the event const mods: input.Mods = gtk_key.eventMods( - @ptrCast(self.gl_area), event, physical_key, gtk_mods, - if (self.app.x11_xkb) |*xkb| xkb else null, + action, + &self.app.winproto, ); // Get our consumed modifiers @@ -1768,12 +1899,11 @@ fn gtkInputPreeditStart( _: *c.GtkIMContext, ud: ?*anyopaque, ) callconv(.C) void { - //log.debug("preedit start", .{}); + // log.warn("GTKIM: preedit start", .{}); const self = userdataSelf(ud.?); - if (!self.in_keypress) return; - // Mark that we are now composing a string with a dead key state. - // We'll record the string in the preedit-changed callback. + // Start our composing state for the input method and reset our + // input buffer to empty. self.im_composing = true; self.im_len = 0; } @@ -1784,52 +1914,33 @@ fn gtkInputPreeditChanged( ) callconv(.C) void { const self = userdataSelf(ud.?); - // If there's buffered character, send the characters directly to the surface. - if (self.im_composing and self.im_commit_buffered) { - defer self.im_commit_buffered = false; - defer self.im_len = 0; - _ = self.core_surface.keyCallback(.{ - .action = .press, - .key = .invalid, - .physical_key = .invalid, - .mods = .{}, - .consumed_mods = .{}, - .composing = false, - .utf8 = self.im_buf[0..self.im_len], - }) catch |err| { - log.err("error in key callback err={}", .{err}); - return; - }; - } - - if (!self.in_keypress) return; - // Get our pre-edit string that we'll use to show the user. var buf: [*c]u8 = undefined; _ = c.gtk_im_context_get_preedit_string(ctx, &buf, null, null); defer c.g_free(buf); const str = std.mem.sliceTo(buf, 0); - // If our string becomes empty we ignore this. This can happen after - // a commit event when the preedit is being cleared and we don't want - // to set im_len to zero. This is safe because preeditstart always sets - // im_len to zero. - if (str.len == 0) return; - - // Copy the preedit string into the im_buf. This is safe because - // commit will always overwrite this. - self.im_len = @intCast(@min(self.im_buf.len, str.len)); - @memcpy(self.im_buf[0..self.im_len], str); + // Update our preedit state in Ghostty core + // log.warn("GTKIM: preedit change str={s}", .{str}); + self.core_surface.preeditCallback(str) catch |err| { + log.err("error in preedit callback err={}", .{err}); + }; } fn gtkInputPreeditEnd( _: *c.GtkIMContext, ud: ?*anyopaque, ) callconv(.C) void { - //log.debug("preedit end", .{}); + // log.warn("GTKIM: preedit end", .{}); const self = userdataSelf(ud.?); - if (!self.in_keypress) return; + + // End our composing state for GTK, allowing us to commit the text. self.im_composing = false; + + // End our preedit state in Ghostty core + self.core_surface.preeditCallback(null) catch |err| { + log.err("error in preedit callback err={}", .{err}); + }; } fn gtkInputCommit( @@ -1840,35 +1951,64 @@ fn gtkInputCommit( const self = userdataSelf(ud.?); const str = std.mem.sliceTo(bytes, 0); - // If we're in a key event, then we want to buffer the commit so - // that we can send the proper keycallback followed by the char - // callback. - if (self.in_keypress) { - if (str.len <= self.im_buf.len) { - @memcpy(self.im_buf[0..str.len], str); - self.im_len = @intCast(str.len); + // log.debug("GTKIM: input commit composing={} keyevent={} str={s}", .{ + // self.im_composing, + // self.in_keyevent, + // str, + // }); - // If composing is done and character should be committed, - // It should be committed in preedit callback. - if (self.im_composing) { - self.im_commit_buffered = true; + // We need to handle commit specially if we're in a key event. + // Specifically, GTK will send us a commit event for basic key + // encodings like "a" (on a US layout keyboard). We don't want + // to treat this as IME committed text because we want to associate + // it with a key event (i.e. "a" key press). + switch (self.in_keyevent) { + // If we're not in a key event then this commit is from + // some other source (i.e. on-screen keyboard, tablet, etc.) + // and we want to commit the text to the core surface. + .false => {}, + + // If we're in a composing state and in a key event then this + // key event is resulting in a commit of multiple keypresses + // and we don't want to encode it alongside the keypress. + .composing => {}, + + // If we're not composing then this commit is just a normal + // key encoding and we want our key event to handle it so + // that Ghostty can be aware of the key event alongside + // the text. + .not_composing => { + if (str.len > self.im_buf.len) { + log.warn("not enough buffer space for input method commit", .{}); + return; } - // log.debug("input commit len={}", .{self.im_len}); - } else { - log.warn("not enough buffer space for input method commit", .{}); - } + // Copy our committed text to the buffer + @memcpy(self.im_buf[0..str.len], str); + self.im_len = @intCast(str.len); - return; + // log.debug("input commit len={}", .{self.im_len}); + return; + }, } - // This prevents staying in composing state after commit even though - // input method has changed. + // If we reach this point from above it means we're composing OR + // not in a keypress. In either case, we want to commit the text + // given to us because that's what GTK is asking us to do. If we're + // not in a keypress it means that this commit came via a non-keyboard + // event (i.e. on-screen keyboard, tablet of some kind, etc.). + + // Committing ends composing state self.im_composing = false; - // We're not in a keypress, so this was sent from an on-screen emoji - // keyboard or something like that. Send the characters directly to - // the surface. + // End our preedit state. Well-behaved input methods do this for us + // by triggering a preedit-end event but some do not (ibus 1.5.29). + self.core_surface.preeditCallback(null) catch |err| { + log.err("error in preedit callback err={}", .{err}); + }; + + // Send the text to the core surface, associated with no key (an + // invalid key, which should produce no PTY encoding). _ = self.core_surface.keyCallback(.{ .action = .press, .key = .invalid, @@ -1878,7 +2018,7 @@ fn gtkInputCommit( .composing = false, .utf8 = str, }) catch |err| { - log.err("error in key callback err={}", .{err}); + log.warn("error in key callback err={}", .{err}); return; }; } @@ -1896,6 +2036,12 @@ fn gtkFocusEnter(_: *c.GtkEventControllerFocus, ud: ?*anyopaque) callconv(.C) vo self.unfocused_widget = null; } + if (self.pwd) |pwd| { + if (self.container.window()) |window| { + if (self.app.config.@"window-subtitle" == .@"working-directory") window.setSubtitle(pwd); + } + } + // Notify our surface self.core_surface.focusCallback(true) catch |err| { log.err("error in focus callback err={}", .{err}); @@ -1980,7 +2126,7 @@ pub fn present(self: *Surface) void { if (self.container.window()) |window| { if (self.container.tab()) |tab| { if (window.notebook.getTabPosition(tab)) |position| - window.notebook.gotoNthTab(position); + _ = window.notebook.gotoNthTab(position); } c.gtk_window_present(window.window); } @@ -2025,3 +2171,131 @@ pub fn setSplitZoom(self: *Surface, new_split_zoom: bool) void { pub fn toggleSplitZoom(self: *Surface) void { self.setSplitZoom(!self.zoomed_in); } + +/// Handle items being dropped on our surface. +fn gtkDrop( + _: *c.GtkDropTarget, + value: *c.GValue, + x: f64, + y: f64, + ud: ?*anyopaque, +) callconv(.C) c.gboolean { + _ = x; + _ = y; + const self = userdataSelf(ud.?); + const alloc = self.app.core_app.alloc; + + if (g_value_holds(value, c.G_TYPE_BOXED)) { + var data = std.ArrayList(u8).init(alloc); + defer data.deinit(); + + var shell_escape_writer: internal_os.ShellEscapeWriter(std.ArrayList(u8).Writer) = .{ + .child_writer = data.writer(), + }; + const writer = shell_escape_writer.writer(); + + const fl: *c.GdkFileList = @ptrCast(c.g_value_get_boxed(value)); + var l = c.gdk_file_list_get_files(fl); + + while (l != null) : (l = l.*.next) { + const file: *c.GFile = @ptrCast(l.*.data); + const path = c.g_file_get_path(file) orelse continue; + + writer.writeAll(std.mem.span(path)) catch |err| { + log.err("unable to write path to buffer: {}", .{err}); + continue; + }; + writer.writeAll("\n") catch |err| { + log.err("unable to write to buffer: {}", .{err}); + continue; + }; + } + + const string = data.toOwnedSliceSentinel(0) catch |err| { + log.err("unable to convert to a slice: {}", .{err}); + return 1; + }; + defer alloc.free(string); + + self.doPaste(string); + + return 1; + } + + if (g_value_holds(value, c.G_TYPE_STRING)) { + if (c.g_value_get_string(value)) |string| { + self.doPaste(std.mem.span(string)); + } + return 1; + } + + return 1; +} + +fn doPaste(self: *Surface, data: [:0]const u8) void { + if (data.len == 0) return; + + self.core_surface.completeClipboardRequest(.paste, data, false) catch |err| switch (err) { + error.UnsafePaste, + error.UnauthorizedPaste, + => { + ClipboardConfirmationWindow.create( + self.app, + data, + &self.core_surface, + .paste, + ) catch |window_err| { + log.err("failed to create clipboard confirmation window err={}", .{window_err}); + }; + }, + error.OutOfMemory, + error.NoSpaceLeft, + => log.err("failed to complete clipboard request err={}", .{err}), + }; +} + +pub fn defaultTermioEnv(self: *Surface) !std.process.EnvMap { + const alloc = self.app.core_app.alloc; + var env = try internal_os.getEnvMap(alloc); + errdefer env.deinit(); + + // Don't leak these GTK environment variables to child processes. + env.remove("GDK_DEBUG"); + env.remove("GDK_DISABLE"); + env.remove("GSK_RENDERER"); + + // Unset environment varies set by snaps if we're running in a snap. + // This allows Ghostty to further launch additional snaps. + if (env.get("SNAP")) |_| { + env.remove("SNAP"); + env.remove("DRIRC_CONFIGDIR"); + env.remove("__EGL_EXTERNAL_PLATFORM_CONFIG_DIRS"); + env.remove("__EGL_VENDOR_LIBRARY_DIRS"); + env.remove("LD_LIBRARY_PATH"); + env.remove("LIBGL_DRIVERS_PATH"); + env.remove("LIBVA_DRIVERS_PATH"); + env.remove("VK_LAYER_PATH"); + env.remove("XLOCALEDIR"); + env.remove("GDK_PIXBUF_MODULEDIR"); + env.remove("GDK_PIXBUF_MODULE_FILE"); + env.remove("GTK_PATH"); + } + + if (self.container.window()) |window| { + // On some window protocols we might want to add specific + // environment variables to subprocesses, such as WINDOWID on X11. + try window.winproto.addSubprocessEnv(&env); + } + + return env; +} + +/// Check a GValue to see what's type its wrapping. This is equivalent to GTK's +/// `G_VALUE_HOLDS` macro but Zig's C translator does not like it. +fn g_value_holds(value_: ?*c.GValue, g_type: c.GType) bool { + if (value_) |value| { + if (value.*.g_type == g_type) return true; + return c.g_type_check_value_holds(value, g_type) != 0; + } + return false; +} diff --git a/src/apprt/gtk/Tab.zig b/src/apprt/gtk/Tab.zig index ed0804fd3d..d320daa7c8 100644 --- a/src/apprt/gtk/Tab.zig +++ b/src/apprt/gtk/Tab.zig @@ -121,10 +121,63 @@ pub fn remove(self: *Tab) void { self.window.closeTab(self); } -pub fn gtkTabCloseClick(_: *c.GtkButton, ud: ?*anyopaque) callconv(.C) void { +/// Helper function to check if any surface in the split hierarchy needs close confirmation +fn needsConfirm(elem: Surface.Container.Elem) bool { + return switch (elem) { + .surface => |s| s.core_surface.needsConfirmQuit(), + .split => |s| needsConfirm(s.top_left) or needsConfirm(s.bottom_right), + }; +} + +/// Close the tab, asking for confirmation if any surface requests it. +pub fn closeWithConfirmation(tab: *Tab) void { + switch (tab.elem) { + .surface => |s| s.close(s.core_surface.needsConfirmQuit()), + .split => |s| { + if (needsConfirm(s.top_left) or needsConfirm(s.bottom_right)) { + const alert = c.gtk_message_dialog_new( + tab.window.window, + c.GTK_DIALOG_MODAL, + c.GTK_MESSAGE_QUESTION, + c.GTK_BUTTONS_YES_NO, + "Close this tab?", + ); + c.gtk_message_dialog_format_secondary_text( + @ptrCast(alert), + "All terminal sessions in this tab will be terminated.", + ); + + // We want the "yes" to appear destructive. + const yes_widget = c.gtk_dialog_get_widget_for_response( + @ptrCast(alert), + c.GTK_RESPONSE_YES, + ); + c.gtk_widget_add_css_class(yes_widget, "destructive-action"); + + // We want the "no" to be the default action + c.gtk_dialog_set_default_response( + @ptrCast(alert), + c.GTK_RESPONSE_NO, + ); + + _ = c.g_signal_connect_data(alert, "response", c.G_CALLBACK(>kTabCloseConfirmation), tab, null, c.G_CONNECT_DEFAULT); + c.gtk_widget_show(alert); + return; + } + tab.remove(); + }, + } +} + +fn gtkTabCloseConfirmation( + alert: *c.GtkMessageDialog, + response: c.gint, + ud: ?*anyopaque, +) callconv(.C) void { const tab: *Tab = @ptrCast(@alignCast(ud)); - const window = tab.window; - window.closeTab(tab); + c.gtk_window_destroy(@ptrCast(alert)); + if (response != c.GTK_RESPONSE_YES) return; + tab.remove(); } fn gtkDestroy(v: *c.GtkWidget, ud: ?*anyopaque) callconv(.C) void { @@ -135,17 +188,3 @@ fn gtkDestroy(v: *c.GtkWidget, ud: ?*anyopaque) callconv(.C) void { const tab: *Tab = @ptrCast(@alignCast(ud)); tab.destroy(tab.window.app.core_app.alloc); } - -pub fn gtkTabClick( - gesture: *c.GtkGestureClick, - _: c.gint, - _: c.gdouble, - _: c.gdouble, - ud: ?*anyopaque, -) callconv(.C) void { - const self: *Tab = @ptrCast(@alignCast(ud)); - const gtk_button = c.gtk_gesture_single_get_current_button(@ptrCast(gesture)); - if (gtk_button == c.GDK_BUTTON_MIDDLE) { - self.remove(); - } -} diff --git a/src/apprt/gtk/Window.zig b/src/apprt/gtk/Window.zig index bf80c4a1a7..ab5e54d9fa 100644 --- a/src/apprt/gtk/Window.zig +++ b/src/apprt/gtk/Window.zig @@ -22,10 +22,10 @@ const Tab = @import("Tab.zig"); const c = @import("c.zig").c; const adwaita = @import("adwaita.zig"); const gtk_key = @import("key.zig"); -const Notebook = @import("notebook.zig").Notebook; -const HeaderBar = @import("headerbar.zig").HeaderBar; +const Notebook = @import("notebook.zig"); +const HeaderBar = @import("headerbar.zig"); const version = @import("version.zig"); -const wayland = @import("wayland.zig"); +const winproto = @import("winproto.zig"); const log = std.log.scoped(.gtk); @@ -34,29 +34,26 @@ app: *App, /// Our window window: *c.GtkWindow, -/// The header bar for the window. This is possibly null since it can be -/// disabled using gtk-titlebar. This is either an AdwHeaderBar or -/// GtkHeaderBar depending on if adw is enabled and linked. -header: ?HeaderBar, +/// The header bar for the window. +headerbar: HeaderBar, /// The tab overview for the window. This is possibly null since there is no /// taboverview without a AdwApplicationWindow (libadwaita >= 1.4.0). tab_overview: ?*c.GtkWidget, /// The notebook (tab grouping) for this window. -/// can be either c.GtkNotebook or c.AdwTabView. notebook: Notebook, context_menu: *c.GtkWidget, -/// The libadwaita widget for receiving toast send requests. If libadwaita is -/// not used, this is null and unused. -toast_overlay: ?*c.GtkWidget, +/// The libadwaita widget for receiving toast send requests. +toast_overlay: *c.GtkWidget, /// See adwTabOverviewOpen for why we have this. adw_tab_overview_focus_timer: ?c.guint = null, -wayland: ?wayland.SurfaceState, +/// State and logic for windowing protocol for a window. +winproto: winproto.Window, pub fn create(alloc: Allocator, app: *App) !*Window { // Allocate a fixed pointer for our window. We try to minimize @@ -77,46 +74,36 @@ pub fn init(self: *Window, app: *App) !void { self.* = .{ .app = app, .window = undefined, - .header = null, + .headerbar = undefined, .tab_overview = null, .notebook = undefined, .context_menu = undefined, .toast_overlay = undefined, - .wayland = null, + .winproto = .none, }; // Create the window - const window: *c.GtkWidget = window: { - if ((comptime adwaita.versionAtLeast(0, 0, 0)) and adwaita.enabled(&self.app.config)) { - const window = c.adw_application_window_new(app.app); - c.gtk_widget_add_css_class(@ptrCast(window), "adw"); - break :window window; - } else { - const window = c.gtk_application_window_new(app.app); - c.gtk_widget_add_css_class(@ptrCast(window), "gtk"); - break :window window; - } - }; - errdefer c.gtk_window_destroy(@ptrCast(window)); + const gtk_widget = c.adw_application_window_new(app.app); + errdefer c.gtk_window_destroy(@ptrCast(gtk_widget)); - const gtk_window: *c.GtkWindow = @ptrCast(window); - self.window = gtk_window; - c.gtk_window_set_title(gtk_window, "Ghostty"); - c.gtk_window_set_default_size(gtk_window, 1000, 600); - c.gtk_widget_add_css_class(@ptrCast(gtk_window), "window"); - c.gtk_widget_add_css_class(@ptrCast(gtk_window), "terminal-window"); + self.window = @ptrCast(@alignCast(gtk_widget)); + + c.gtk_window_set_title(self.window, "Ghostty"); + c.gtk_window_set_default_size(self.window, 1000, 600); + c.gtk_widget_add_css_class(gtk_widget, "window"); + c.gtk_widget_add_css_class(gtk_widget, "terminal-window"); // GTK4 grabs F10 input by default to focus the menubar icon. We want // to disable this so that terminal programs can capture F10 (such as htop) - c.gtk_window_set_handle_menubar_accel(gtk_window, 0); + c.gtk_window_set_handle_menubar_accel(self.window, 0); - c.gtk_window_set_icon_name(gtk_window, build_config.bundle_id); + c.gtk_window_set_icon_name(self.window, build_config.bundle_id); // Apply class to color headerbar if window-theme is set to `ghostty` and // GTK version is before 4.16. The conditional is because above 4.16 // we use GTK CSS color variables. if (!version.atLeast(4, 16, 0) and app.config.@"window-theme" == .ghostty) { - c.gtk_widget_add_css_class(@ptrCast(gtk_window), "window-theme-ghostty"); + c.gtk_widget_add_css_class(gtk_widget, "window-theme-ghostty"); } // Create our box which will hold our widgets in the main content area. @@ -126,9 +113,9 @@ pub fn init(self: *Window, app: *App) !void { self.notebook.init(); // If we are using Adwaita, then we can support the tab overview. - self.tab_overview = if ((comptime adwaita.versionAtLeast(1, 4, 0)) and adwaita.enabled(&self.app.config) and adwaita.versionAtLeast(1, 4, 0)) overview: { + self.tab_overview = if (adwaita.versionAtLeast(1, 4, 0)) overview: { const tab_overview = c.adw_tab_overview_new(); - c.adw_tab_overview_set_view(@ptrCast(tab_overview), self.notebook.adw.tab_view); + c.adw_tab_overview_set_view(@ptrCast(tab_overview), self.notebook.tab_view); c.adw_tab_overview_set_enable_new_tab(@ptrCast(tab_overview), 1); _ = c.g_signal_connect_data( tab_overview, @@ -150,79 +137,65 @@ pub fn init(self: *Window, app: *App) !void { break :overview tab_overview; } else null; - // gtk-titlebar can be used to disable the header bar (but keep - // the window manager's decorations). We create this no matter if we - // are decorated or not because we can have a keybind to toggle the - // decorations. - if (app.config.@"gtk-titlebar") { - const header = HeaderBar.init(self); - - // If we are not decorated then we hide the titlebar. - header.setVisible(app.config.@"window-decoration"); - - { - const btn = c.gtk_menu_button_new(); - c.gtk_widget_set_tooltip_text(btn, "Main Menu"); - c.gtk_menu_button_set_icon_name(@ptrCast(btn), "open-menu-symbolic"); - c.gtk_menu_button_set_menu_model(@ptrCast(btn), @ptrCast(@alignCast(app.menu))); - header.packEnd(btn); - } + // gtk-titlebar can be used to disable the header bar (but keep the window + // manager's decorations). We create this no matter if we are decorated or + // not because we can have a keybind to toggle the decorations. + self.headerbar.init(); - // If we're using an AdwWindow then we can support the tab overview. - if (self.tab_overview) |tab_overview| { - if (comptime !adwaita.versionAtLeast(1, 4, 0)) unreachable; - assert(self.app.config.@"gtk-adwaita" and adwaita.versionAtLeast(1, 4, 0)); - const btn = switch (app.config.@"gtk-tabs-location") { - .top, .bottom, .left, .right => btn: { - const btn = c.gtk_toggle_button_new(); - c.gtk_widget_set_tooltip_text(btn, "View Open Tabs"); - c.gtk_button_set_icon_name(@ptrCast(btn), "view-grid-symbolic"); - _ = c.g_object_bind_property( - btn, - "active", - tab_overview, - "open", - c.G_BINDING_BIDIRECTIONAL | c.G_BINDING_SYNC_CREATE, - ); - - break :btn btn; - }, - - .hidden => btn: { - const btn = c.adw_tab_button_new(); - c.adw_tab_button_set_view(@ptrCast(btn), self.notebook.adw.tab_view); - c.gtk_actionable_set_action_name(@ptrCast(btn), "overview.open"); - break :btn btn; - }, - }; - - c.gtk_widget_set_focus_on_click(btn, c.FALSE); - header.packEnd(btn); - } + { + const btn = c.gtk_menu_button_new(); + c.gtk_widget_set_tooltip_text(btn, "Main Menu"); + c.gtk_menu_button_set_icon_name(@ptrCast(btn), "open-menu-symbolic"); + c.gtk_menu_button_set_menu_model(@ptrCast(btn), @ptrCast(@alignCast(app.menu))); + self.headerbar.packEnd(btn); + } - { - const btn = c.gtk_button_new_from_icon_name("tab-new-symbolic"); - c.gtk_widget_set_tooltip_text(btn, "New Tab"); - _ = c.g_signal_connect_data(btn, "clicked", c.G_CALLBACK(>kTabNewClick), self, null, c.G_CONNECT_DEFAULT); - header.packStart(btn); - } + // If we're using an AdwWindow then we can support the tab overview. + if (self.tab_overview) |tab_overview| { + if (!adwaita.versionAtLeast(1, 4, 0)) unreachable; + const btn = switch (app.config.@"gtk-tabs-location") { + .top, .bottom => btn: { + const btn = c.gtk_toggle_button_new(); + c.gtk_widget_set_tooltip_text(btn, "View Open Tabs"); + c.gtk_button_set_icon_name(@ptrCast(btn), "view-grid-symbolic"); + _ = c.g_object_bind_property( + btn, + "active", + tab_overview, + "open", + c.G_BINDING_BIDIRECTIONAL | c.G_BINDING_SYNC_CREATE, + ); + + break :btn btn; + }, - self.header = header; - } + .hidden => btn: { + const btn = c.adw_tab_button_new(); + c.adw_tab_button_set_view(@ptrCast(btn), self.notebook.tab_view); + c.gtk_actionable_set_action_name(@ptrCast(btn), "overview.open"); + break :btn btn; + }, + }; - _ = c.g_signal_connect_data(gtk_window, "notify::decorated", c.G_CALLBACK(>kWindowNotifyDecorated), self, null, c.G_CONNECT_DEFAULT); + c.gtk_widget_set_focus_on_click(btn, c.FALSE); + self.headerbar.packEnd(btn); + } - // If we are disabling decorations then disable them right away. - if (!app.config.@"window-decoration") { - c.gtk_window_set_decorated(gtk_window, 0); + { + const btn = c.gtk_button_new_from_icon_name("tab-new-symbolic"); + c.gtk_widget_set_tooltip_text(btn, "New Tab"); + _ = c.g_signal_connect_data(btn, "clicked", c.G_CALLBACK(>kTabNewClick), self, null, c.G_CONNECT_DEFAULT); + self.headerbar.packStart(btn); } + _ = c.g_signal_connect_data(self.window, "notify::decorated", c.G_CALLBACK(>kWindowNotifyDecorated), self, null, c.G_CONNECT_DEFAULT); + _ = c.g_signal_connect_data(self.window, "notify::maximized", c.G_CALLBACK(>kWindowNotifyMaximized), self, null, c.G_CONNECT_DEFAULT); + _ = c.g_signal_connect_data(self.window, "notify::fullscreened", c.G_CALLBACK(>kWindowNotifyFullscreened), self, null, c.G_CONNECT_DEFAULT); + // If Adwaita is enabled and is older than 1.4.0 we don't have the tab overview and so we // need to stick the headerbar into the content box. - if (!adwaita.versionAtLeast(1, 4, 0) and adwaita.enabled(&self.app.config)) { - if (self.header) |h| { - c.gtk_box_append(@ptrCast(box), h.asWidget()); - } + if (!adwaita.versionAtLeast(1, 4, 0)) { + c.gtk_box_append(@ptrCast(box), self.headerbar.asWidget()); } // In debug we show a warning and apply the 'devel' class to the window. @@ -230,10 +203,7 @@ pub fn init(self: *Window, app: *App) !void { if (comptime std.debug.runtime_safety) { const warning_box = c.gtk_box_new(c.GTK_ORIENTATION_VERTICAL, 0); const warning_text = "⚠️ You're running a debug build of Ghostty! Performance will be degraded."; - if ((comptime adwaita.versionAtLeast(1, 3, 0)) and - adwaita.enabled(&app.config) and - adwaita.versionAtLeast(1, 3, 0)) - { + if (adwaita.versionAtLeast(1, 3, 0)) { const banner = c.adw_banner_new(warning_text); c.adw_banner_set_revealed(@ptrCast(banner), 1); c.gtk_box_append(@ptrCast(warning_box), @ptrCast(banner)); @@ -243,37 +213,33 @@ pub fn init(self: *Window, app: *App) !void { c.gtk_widget_set_margin_bottom(warning, 10); c.gtk_box_append(@ptrCast(warning_box), warning); } - c.gtk_widget_add_css_class(@ptrCast(gtk_window), "devel"); + c.gtk_widget_add_css_class(gtk_widget, "devel"); c.gtk_widget_add_css_class(@ptrCast(warning_box), "background"); c.gtk_box_append(@ptrCast(box), warning_box); } // Setup our toast overlay if we have one - self.toast_overlay = if (adwaita.enabled(&self.app.config)) toast: { - const toast_overlay = c.adw_toast_overlay_new(); - c.adw_toast_overlay_set_child( - @ptrCast(toast_overlay), - @ptrCast(@alignCast(self.notebook.asWidget())), - ); - c.gtk_box_append(@ptrCast(box), toast_overlay); - break :toast toast_overlay; - } else toast: { - c.gtk_box_append(@ptrCast(box), self.notebook.asWidget()); - break :toast null; - }; + self.toast_overlay = c.adw_toast_overlay_new(); + c.adw_toast_overlay_set_child( + @ptrCast(self.toast_overlay), + @ptrCast(@alignCast(self.notebook.asWidget())), + ); + c.gtk_box_append(@ptrCast(box), self.toast_overlay); // If we have a tab overview then we can set it on our notebook. if (self.tab_overview) |tab_overview| { - if (comptime !adwaita.versionAtLeast(1, 3, 0)) unreachable; - assert(self.notebook == .adw); - c.adw_tab_overview_set_view(@ptrCast(tab_overview), self.notebook.adw.tab_view); + if (!adwaita.versionAtLeast(1, 4, 0)) unreachable; + c.adw_tab_overview_set_view(@ptrCast(tab_overview), self.notebook.tab_view); } self.context_menu = c.gtk_popover_menu_new_from_model(@ptrCast(@alignCast(self.app.context_menu))); - c.gtk_widget_set_parent(self.context_menu, window); + c.gtk_widget_set_parent(self.context_menu, box); c.gtk_popover_set_has_arrow(@ptrCast(@alignCast(self.context_menu)), 0); c.gtk_widget_set_halign(self.context_menu, c.GTK_ALIGN_START); + // If we want the window to be maximized, we do that here. + if (app.config.maximize) c.gtk_window_maximize(self.window); + // If we are in fullscreen mode, new windows start fullscreen. if (app.config.fullscreen) c.gtk_window_fullscreen(self.window); @@ -282,43 +248,39 @@ pub fn init(self: *Window, app: *App) !void { // focused (i.e. when the libadw tab overview is shown). const ec_key_press = c.gtk_event_controller_key_new(); errdefer c.g_object_unref(ec_key_press); - c.gtk_widget_add_controller(window, ec_key_press); + c.gtk_widget_add_controller(gtk_widget, ec_key_press); // All of our events _ = c.g_signal_connect_data(self.context_menu, "closed", c.G_CALLBACK(>kRefocusTerm), self, null, c.G_CONNECT_DEFAULT); - _ = c.g_signal_connect_data(window, "realize", c.G_CALLBACK(>kRealize), self, null, c.G_CONNECT_DEFAULT); - _ = c.g_signal_connect_data(window, "close-request", c.G_CALLBACK(>kCloseRequest), self, null, c.G_CONNECT_DEFAULT); - _ = c.g_signal_connect_data(window, "destroy", c.G_CALLBACK(>kDestroy), self, null, c.G_CONNECT_DEFAULT); + _ = c.g_signal_connect_data(self.window, "realize", c.G_CALLBACK(>kRealize), self, null, c.G_CONNECT_DEFAULT); + _ = c.g_signal_connect_data(self.window, "close-request", c.G_CALLBACK(>kCloseRequest), self, null, c.G_CONNECT_DEFAULT); + _ = c.g_signal_connect_data(self.window, "destroy", c.G_CALLBACK(>kDestroy), self, null, c.G_CONNECT_DEFAULT); _ = c.g_signal_connect_data(ec_key_press, "key-pressed", c.G_CALLBACK(>kKeyPressed), self, null, c.G_CONNECT_DEFAULT); // Our actions for the menu initActions(self); - if ((comptime adwaita.versionAtLeast(1, 4, 0)) and adwaita.versionAtLeast(1, 4, 0) and adwaita.enabled(&self.app.config)) { + if (adwaita.versionAtLeast(1, 4, 0)) { const toolbar_view: *c.AdwToolbarView = @ptrCast(c.adw_toolbar_view_new()); - if (self.header) |header| { - const header_widget = header.asWidget(); - c.adw_toolbar_view_add_top_bar(toolbar_view, header_widget); - } + c.adw_toolbar_view_add_top_bar(toolbar_view, self.headerbar.asWidget()); if (self.app.config.@"gtk-tabs-location" != .hidden) { const tab_bar = c.adw_tab_bar_new(); - c.adw_tab_bar_set_view(tab_bar, self.notebook.adw.tab_view); + c.adw_tab_bar_set_view(tab_bar, self.notebook.tab_view); if (!app.config.@"gtk-wide-tabs") c.adw_tab_bar_set_expand_tabs(tab_bar, 0); const tab_bar_widget: *c.GtkWidget = @ptrCast(@alignCast(tab_bar)); switch (self.app.config.@"gtk-tabs-location") { - // left and right are not supported in libadwaita. - .top, .left, .right => c.adw_toolbar_view_add_top_bar(toolbar_view, tab_bar_widget), + .top => c.adw_toolbar_view_add_top_bar(toolbar_view, tab_bar_widget), .bottom => c.adw_toolbar_view_add_bottom_bar(toolbar_view, tab_bar_widget), .hidden => unreachable, } } c.adw_toolbar_view_set_content(toolbar_view, box); - const toolbar_style: c.AdwToolbarStyle = switch (self.app.config.@"adw-toolbar-style") { + const toolbar_style: c.AdwToolbarStyle = switch (self.app.config.@"gtk-toolbar-style") { .flat => c.ADW_TOOLBAR_FLAT, .raised => c.ADW_TOOLBAR_RAISED, .@"raised-border" => c.ADW_TOOLBAR_RAISED_BORDER, @@ -332,56 +294,48 @@ pub fn init(self: *Window, app: *App) !void { @ptrCast(@alignCast(toolbar_view)), ); c.adw_application_window_set_content( - @ptrCast(gtk_window), + @ptrCast(gtk_widget), @ptrCast(@alignCast(self.tab_overview)), ); } else tab_bar: { - switch (self.notebook) { - .adw => |*adw| if (comptime adwaita.versionAtLeast(0, 0, 0)) { - if (app.config.@"gtk-tabs-location" == .hidden) break :tab_bar; - // In earlier adwaita versions, we need to add the tabbar manually since we do not use - // an AdwToolbarView. - const tab_bar: *c.AdwTabBar = c.adw_tab_bar_new().?; - c.gtk_widget_add_css_class(@ptrCast(@alignCast(tab_bar)), "inline"); - switch (app.config.@"gtk-tabs-location") { - .top, - .left, - .right, - => c.gtk_box_prepend( - @ptrCast(box), - @ptrCast(@alignCast(tab_bar)), - ), - - .bottom => c.gtk_box_append( - @ptrCast(box), - @ptrCast(@alignCast(tab_bar)), - ), - .hidden => unreachable, - } - c.adw_tab_bar_set_view(tab_bar, adw.tab_view); - - if (!app.config.@"gtk-wide-tabs") c.adw_tab_bar_set_expand_tabs(tab_bar, 0); - }, - - .gtk => {}, + if (app.config.@"gtk-tabs-location" == .hidden) break :tab_bar; + // In earlier adwaita versions, we need to add the tabbar manually since we do not use + // an AdwToolbarView. + const tab_bar: *c.AdwTabBar = c.adw_tab_bar_new().?; + c.gtk_widget_add_css_class(@ptrCast(@alignCast(tab_bar)), "inline"); + switch (app.config.@"gtk-tabs-location") { + .top => c.gtk_box_insert_child_after( + @ptrCast(box), + @ptrCast(@alignCast(tab_bar)), + @ptrCast(@alignCast(self.headerbar.asWidget())), + ), + .bottom => c.gtk_box_append( + @ptrCast(box), + @ptrCast(@alignCast(tab_bar)), + ), + .hidden => unreachable, } + c.adw_tab_bar_set_view(tab_bar, self.notebook.tab_view); - // The box is our main child - if (!adwaita.versionAtLeast(1, 4, 0) and adwaita.enabled(&self.app.config)) { - c.adw_application_window_set_content( - @ptrCast(gtk_window), - box, - ); - } else { - c.gtk_window_set_child(gtk_window, box); - if (self.header) |h| { - c.gtk_window_set_titlebar(gtk_window, h.asWidget()); - } - } + if (!app.config.@"gtk-wide-tabs") c.adw_tab_bar_set_expand_tabs(tab_bar, 0); } // Show the window - c.gtk_widget_show(window); + c.gtk_widget_show(gtk_widget); +} + +pub fn updateConfig( + self: *Window, + config: *const configpkg.Config, +) !void { + self.winproto.updateConfigEvent(config) catch |err| { + // We want to continue attempting to make the other config + // changes necessary so we just log the error and continue. + log.warn("failed to update window protocol config error={}", .{err}); + }; + + // We always resync our appearance whenever the config changes. + try self.syncAppearance(config); } /// Updates appearance based on config settings. Will be called once upon window @@ -390,19 +344,48 @@ pub fn init(self: *Window, app: *App) !void { /// TODO: Many of the initial style settings in `create` could possibly be made /// reactive by moving them here. pub fn syncAppearance(self: *Window, config: *const configpkg.Config) !void { - if (config.@"background-opacity" < 1) { - c.gtk_widget_remove_css_class(@ptrCast(self.window), "background"); - } else { - c.gtk_widget_add_css_class(@ptrCast(self.window), "background"); + self.winproto.syncAppearance() catch |err| { + log.warn("failed to sync winproto appearance error={}", .{err}); + }; + + toggleCssClass( + @ptrCast(self.window), + "background", + config.@"background-opacity" >= 1, + ); + + // If we are disabling CSDs then disable them right away. + const csd_enabled = self.winproto.clientSideDecorationEnabled(); + c.gtk_window_set_decorated(self.window, @intFromBool(csd_enabled)); + + // If we are not decorated then we hide the titlebar. + self.headerbar.setVisible(config.@"gtk-titlebar" and csd_enabled); + + // Disable the title buttons (close, maximize, minimize, ...) + // *inside* the tab overview if CSDs are disabled. + // We do spare the search button, though. + if (self.tab_overview) |tab_overview| { + if (!adwaita.versionAtLeast(1, 4, 0)) unreachable; + c.adw_tab_overview_set_show_start_title_buttons( + @ptrCast(tab_overview), + @intFromBool(csd_enabled), + ); + c.adw_tab_overview_set_show_end_title_buttons( + @ptrCast(tab_overview), + @intFromBool(csd_enabled), + ); } +} - if (self.wayland) |*wl| { - const blurred = switch (config.@"background-blur-radius") { - .false => false, - .true => true, - .radius => |v| v > 0, - }; - try wl.setBlur(blurred); +fn toggleCssClass( + widget: *c.GtkWidget, + class: [:0]const u8, + v: bool, +) void { + if (v) { + c.gtk_widget_add_css_class(widget, class); + } else { + c.gtk_widget_remove_css_class(widget, class); } } @@ -443,13 +426,23 @@ fn initActions(self: *Window) void { pub fn deinit(self: *Window) void { c.gtk_widget_unparent(@ptrCast(self.context_menu)); - if (self.wayland) |*wl| wl.deinit(); + self.winproto.deinit(self.app.core_app.alloc); if (self.adw_tab_overview_focus_timer) |timer| { _ = c.g_source_remove(timer); } } +/// Set the title of the window. +pub fn setTitle(self: *Window, title: [:0]const u8) void { + self.headerbar.setTitle(title); +} + +/// Set the subtitle of the window if it has one. +pub fn setSubtitle(self: *Window, subtitle: [:0]const u8) void { + self.headerbar.setSubtitle(subtitle); +} + /// Add a new tab to this window. pub fn newTab(self: *Window, parent: ?*CoreSurface) !void { const alloc = self.app.core_app.alloc; @@ -467,23 +460,25 @@ pub fn closeTab(self: *Window, tab: *Tab) void { } /// Go to the previous tab for a surface. -pub fn gotoPreviousTab(self: *Window, surface: *Surface) void { +pub fn gotoPreviousTab(self: *Window, surface: *Surface) bool { const tab = surface.container.tab() orelse { log.info("surface is not attached to a tab bar, cannot navigate", .{}); - return; + return false; }; - self.notebook.gotoPreviousTab(tab); + if (!self.notebook.gotoPreviousTab(tab)) return false; self.focusCurrentTab(); + return true; } /// Go to the next tab for a surface. -pub fn gotoNextTab(self: *Window, surface: *Surface) void { +pub fn gotoNextTab(self: *Window, surface: *Surface) bool { const tab = surface.container.tab() orelse { log.info("surface is not attached to a tab bar, cannot navigate", .{}); - return; + return false; }; - self.notebook.gotoNextTab(tab); + if (!self.notebook.gotoNextTab(tab)) return false; self.focusCurrentTab(); + return true; } /// Move the current tab for a surface. @@ -495,31 +490,41 @@ pub fn moveTab(self: *Window, surface: *Surface, position: c_int) void { self.notebook.moveTab(tab, position); } -/// Go to the next tab for a surface. -pub fn gotoLastTab(self: *Window) void { - const max = self.notebook.nPages() -| 1; - self.gotoTab(@intCast(max)); +/// Go to the last tab for a surface. +pub fn gotoLastTab(self: *Window) bool { + const max = self.notebook.nPages(); + return self.gotoTab(@intCast(max)); } /// Go to the specific tab index. -pub fn gotoTab(self: *Window, n: usize) void { - if (n == 0) return; +pub fn gotoTab(self: *Window, n: usize) bool { + if (n == 0) return false; const max = self.notebook.nPages(); - if (max == 0) return; - const page_idx = std.math.cast(c_int, n - 1) orelse return; - self.notebook.gotoNthTab(@min(page_idx, max - 1)); + if (max == 0) return false; + const page_idx = std.math.cast(c_int, n - 1) orelse return false; + if (!self.notebook.gotoNthTab(@min(page_idx, max - 1))) return false; self.focusCurrentTab(); + return true; } /// Toggle tab overview (if present) pub fn toggleTabOverview(self: *Window) void { if (self.tab_overview) |tab_overview_widget| { - if (comptime !adwaita.versionAtLeast(1, 4, 0)) unreachable; + if (!adwaita.versionAtLeast(1, 4, 0)) unreachable; const tab_overview: *c.AdwTabOverview = @ptrCast(@alignCast(tab_overview_widget)); c.adw_tab_overview_set_open(tab_overview, 1 - c.adw_tab_overview_get_open(tab_overview)); } } +/// Toggle the maximized state for this window. +pub fn toggleMaximize(self: *Window) void { + if (c.gtk_window_is_maximized(self.window) == 0) { + c.gtk_window_maximize(self.window); + } else { + c.gtk_window_unmaximize(self.window); + } +} + /// Toggle fullscreen for this window. pub fn toggleFullscreen(self: *Window) void { const is_fullscreen = c.gtk_window_is_fullscreen(self.window); @@ -532,17 +537,11 @@ pub fn toggleFullscreen(self: *Window) void { /// Toggle the window decorations for this window. pub fn toggleWindowDecorations(self: *Window) void { - const old_decorated = c.gtk_window_get_decorated(self.window) == 1; - const new_decorated = !old_decorated; - c.gtk_window_set_decorated(self.window, @intFromBool(new_decorated)); - - // If we have a titlebar, then we also show/hide it depending on the - // decorated state. GTK tends to consider the titlebar part of the frame - // and hides it with decorations, but libadwaita doesn't. This makes it - // explicit. - if (self.header) |headerbar| { - headerbar.setVisible(new_decorated); - } + self.app.config.@"window-decoration" = switch (self.app.config.@"window-decoration") { + .auto, .client, .server => .none, + .none => .client, + }; + self.updateConfig(&self.app.config) catch {}; } /// Grabs focus on the currently selected tab. @@ -558,20 +557,27 @@ pub fn onConfigReloaded(self: *Window) void { } pub fn sendToast(self: *Window, title: [:0]const u8) void { - if (comptime !adwaita.versionAtLeast(0, 0, 0)) return; - const toast_overlay = self.toast_overlay orelse return; const toast = c.adw_toast_new(title); c.adw_toast_set_timeout(toast, 3); - c.adw_toast_overlay_add_toast(@ptrCast(toast_overlay), toast); + c.adw_toast_overlay_add_toast(@ptrCast(self.toast_overlay), toast); } fn gtkRealize(v: *c.GtkWindow, ud: ?*anyopaque) callconv(.C) bool { const self = userdataSelf(ud.?); - if (self.app.wayland) |*wl| { - self.wayland = wayland.SurfaceState.init(v, wl); + // Initialize our window protocol logic + if (winproto.Window.init( + self.app.core_app.alloc, + &self.app.winproto, + v, + &self.app.config, + )) |winproto_win| { + self.winproto = winproto_win; + } else |err| { + log.warn("failed to initialize window protocol error={}", .{err}); } + // When we are realized we always setup our appearance self.syncAppearance(&self.app.config) catch |err| { log.err("failed to initialize appearance={}", .{err}); }; @@ -579,22 +585,67 @@ fn gtkRealize(v: *c.GtkWindow, ud: ?*anyopaque) callconv(.C) bool { return true; } +fn gtkWindowNotifyMaximized( + _: *c.GObject, + _: *c.GParamSpec, + ud: ?*anyopaque, +) callconv(.C) void { + const self = userdataSelf(ud orelse return); + + // Only toggle visibility of the header bar when we're using CSDs, + // and actually intend on displaying the header bar + if (!self.winproto.clientSideDecorationEnabled()) return; + + // If we aren't maximized, we should show the headerbar again + // if it was originally visible. + const maximized = c.gtk_window_is_maximized(self.window) != 0; + if (!maximized) { + self.headerbar.setVisible(self.app.config.@"gtk-titlebar"); + return; + } + + // If we are maximized, we should hide the headerbar if requested. + if (self.app.config.@"gtk-titlebar-hide-when-maximized") { + self.headerbar.setVisible(false); + } +} + fn gtkWindowNotifyDecorated( object: *c.GObject, _: *c.GParamSpec, - _: ?*anyopaque, + ud: ?*anyopaque, ) callconv(.C) void { - if (c.gtk_window_get_decorated(@ptrCast(object)) == 1) { - c.gtk_widget_remove_css_class(@ptrCast(object), "ssd"); - c.gtk_widget_remove_css_class(@ptrCast(object), "no-border-radius"); - } else { - // Fix any artifacting that may occur in window corners. The .ssd CSS - // class is defined in the GtkWindow documentation: - // https://docs.gtk.org/gtk4/class.Window.html#css-nodes. A definition - // for .ssd is provided by GTK and Adwaita. - c.gtk_widget_add_css_class(@ptrCast(object), "ssd"); - c.gtk_widget_add_css_class(@ptrCast(object), "no-border-radius"); + const self = userdataSelf(ud orelse return); + const is_decorated = c.gtk_window_get_decorated(@ptrCast(object)) == 1; + + // Fix any artifacting that may occur in window corners. The .ssd CSS + // class is defined in the GtkWindow documentation: + // https://docs.gtk.org/gtk4/class.Window.html#css-nodes. A definition + // for .ssd is provided by GTK and Adwaita. + toggleCssClass(@ptrCast(object), "csd", is_decorated); + toggleCssClass(@ptrCast(object), "ssd", !is_decorated); + toggleCssClass(@ptrCast(object), "no-border-radius", !is_decorated); + + // FIXME: This is to update the blur region offset on X11. + // Remove this when we move everything related to window appearance + // to `syncAppearance` for Ghostty 1.2. + self.winproto.syncAppearance() catch {}; +} + +fn gtkWindowNotifyFullscreened( + object: *c.GObject, + _: *c.GParamSpec, + ud: ?*anyopaque, +) callconv(.C) void { + const self = userdataSelf(ud orelse return); + const fullscreened = c.gtk_window_is_fullscreen(@ptrCast(object)) != 0; + if (!fullscreened) { + const csd_enabled = self.winproto.clientSideDecorationEnabled(); + self.headerbar.setVisible(self.app.config.@"gtk-titlebar" and csd_enabled); + return; } + + self.headerbar.setVisible(false); } // Note: we MUST NOT use the GtkButton parameter because gtkActionNewTab @@ -612,12 +663,12 @@ fn gtkTabNewClick(_: *c.GtkButton, ud: ?*anyopaque) callconv(.C) void { /// because we need to return an AdwTabPage from this function. fn gtkNewTabFromOverview(_: *c.GtkWidget, ud: ?*anyopaque) callconv(.C) ?*c.AdwTabPage { const self: *Window = userdataSelf(ud.?); - assert((comptime adwaita.versionAtLeast(1, 4, 0)) and adwaita.versionAtLeast(1, 4, 0) and adwaita.enabled(&self.app.config)); + assert(adwaita.versionAtLeast(1, 4, 0)); const alloc = self.app.core_app.alloc; const surface = self.actionSurface(); const tab = Tab.create(alloc, self, surface) catch return null; - return c.adw_tab_view_get_page(self.notebook.adw.tab_view, @ptrCast(@alignCast(tab.box))); + return c.adw_tab_view_get_page(self.notebook.tab_view, @ptrCast(@alignCast(tab.box))); } fn adwTabOverviewOpen( @@ -764,7 +815,7 @@ fn gtkKeyPressed( // // If someone can confidently show or explain that this is not // necessary, please remove this check. - if (comptime adwaita.versionAtLeast(1, 4, 0)) { + if (adwaita.versionAtLeast(1, 4, 0)) { if (self.tab_overview) |tab_overview_widget| { const tab_overview: *c.AdwTabOverview = @ptrCast(@alignCast(tab_overview_widget)); if (c.adw_tab_overview_get_open(tab_overview) == 0) return 0; @@ -792,10 +843,7 @@ fn gtkActionAbout( const icon = "com.mitchellh.ghostty"; const website = "https://ghostty.org"; - if ((comptime adwaita.versionAtLeast(1, 5, 0)) and - adwaita.versionAtLeast(1, 5, 0) and - adwaita.enabled(&self.app.config)) - { + if (adwaita.versionAtLeast(1, 5, 0)) { c.adw_show_about_dialog( @ptrCast(self.window), "application-name", diff --git a/src/apprt/gtk/adwaita.zig b/src/apprt/gtk/adwaita.zig index 0750555867..885627fa47 100644 --- a/src/apprt/gtk/adwaita.zig +++ b/src/apprt/gtk/adwaita.zig @@ -1,20 +1,5 @@ const std = @import("std"); const c = @import("c.zig").c; -const build_options = @import("build_options"); -const Config = @import("../../config.zig").Config; - -/// Returns true if Ghostty is configured to build with libadwaita and -/// the configuration has enabled adwaita. -/// -/// For a comptime version of this function, use `versionAtLeast` in -/// a comptime context with all the version numbers set to 0. -/// -/// This must be `inline` so that the comptime check noops conditional -/// paths that are not enabled. -pub inline fn enabled(config: *const Config) bool { - return build_options.adwaita and - config.@"gtk-adwaita"; -} /// Verifies that the running libadwaita version is at least the given /// version. This will return false if Ghostty is configured to @@ -33,8 +18,6 @@ pub inline fn versionAtLeast( comptime minor: u16, comptime micro: u16, ) bool { - if (comptime !build_options.adwaita) return false; - // If our header has lower versions than the given version, // we can return false immediately. This prevents us from // compiling against unknown symbols and makes runtime checks diff --git a/src/apprt/gtk/builder_check.zig b/src/apprt/gtk/builder_check.zig new file mode 100644 index 0000000000..015c6310d7 --- /dev/null +++ b/src/apprt/gtk/builder_check.zig @@ -0,0 +1,32 @@ +const std = @import("std"); +const build_options = @import("build_options"); + +const gtk = @import("gtk"); +const adw = @import("adw"); + +pub fn main() !void { + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + const alloc = gpa.allocator(); + + const filename = filename: { + var it = try std.process.argsWithAllocator(alloc); + defer it.deinit(); + + _ = it.next() orelse return error.NoFilename; + break :filename try alloc.dupeZ(u8, it.next() orelse return error.NoFilename); + }; + defer alloc.free(filename); + + const data = try std.fs.cwd().readFileAllocOptions(alloc, filename, std.math.maxInt(u16), null, 1, 0); + defer alloc.free(data); + + if (gtk.initCheck() == 0) { + std.debug.print("{s}: skipping builder check because we can't connect to display!\n", .{filename}); + return; + } + + adw.init(); + + const builder = gtk.Builder.newFromString(data.ptr, @intCast(data.len)); + defer builder.unref(); +} diff --git a/src/apprt/gtk/c.zig b/src/apprt/gtk/c.zig index dde99c78ee..c42c35d465 100644 --- a/src/apprt/gtk/c.zig +++ b/src/apprt/gtk/c.zig @@ -3,14 +3,14 @@ const build_options = @import("build_options"); /// Imported C API directly from header files pub const c = @cImport({ @cInclude("gtk/gtk.h"); - if (build_options.adwaita) { - @cInclude("libadwaita-1/adwaita.h"); - } + @cInclude("adwaita.h"); if (build_options.x11) { // Add in X11-specific GDK backend which we use for specific things // (e.g. X11 window class). @cInclude("gdk/x11/gdkx.h"); + @cInclude("X11/Xlib.h"); + @cInclude("X11/Xatom.h"); // Xkb for X11 state handling @cInclude("X11/XKBlib.h"); } diff --git a/src/apprt/gtk/gresource.zig b/src/apprt/gtk/gresource.zig index 327680993d..050605b00f 100644 --- a/src/apprt/gtk/gresource.zig +++ b/src/apprt/gtk/gresource.zig @@ -53,29 +53,33 @@ const icons = [_]struct { }, }; -pub const gresource_xml = comptimeGenerateGResourceXML(); +pub const ui_files = [_][]const u8{}; +pub const blueprint_files = [_][]const u8{}; -fn comptimeGenerateGResourceXML() []const u8 { - comptime { - @setEvalBranchQuota(13000); - var counter = std.io.countingWriter(std.io.null_writer); - try writeGResourceXML(&counter.writer()); +pub fn main() !void { + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + const alloc = gpa.allocator(); - var buf: [counter.bytes_written]u8 = undefined; - var stream = std.io.fixedBufferStream(&buf); - try writeGResourceXML(stream.writer()); - const final = buf; - return final[0..stream.getWritten().len]; + var extra_ui_files = std.ArrayList([]const u8).init(alloc); + defer { + for (extra_ui_files.items) |item| alloc.free(item); + extra_ui_files.deinit(); + } + + var it = try std.process.argsWithAllocator(alloc); + defer it.deinit(); + + while (it.next()) |filename| { + if (std.mem.eql(u8, std.fs.path.extension(filename), ".ui")) { + try extra_ui_files.append(try alloc.dupe(u8, filename)); + } } -} -fn writeGResourceXML(writer: anytype) !void { + const writer = std.io.getStdOut().writer(); + try writer.writeAll( \\ \\ - \\ - ); - try writer.writeAll( \\ \\ ); @@ -87,9 +91,6 @@ fn writeGResourceXML(writer: anytype) !void { } try writer.writeAll( \\ - \\ - ); - try writer.writeAll( \\ \\ ); @@ -99,6 +100,23 @@ fn writeGResourceXML(writer: anytype) !void { .{ icon.alias, icon.source }, ); } + try writer.writeAll( + \\ + \\ + \\ + ); + for (ui_files) |ui_file| { + try writer.print( + " src/apprt/gtk/ui/{0s}.ui\n", + .{ui_file}, + ); + } + for (extra_ui_files.items) |ui_file| { + try writer.print( + " {s}\n", + .{ std.fs.path.basename(ui_file), ui_file }, + ); + } try writer.writeAll( \\ \\ @@ -107,12 +125,24 @@ fn writeGResourceXML(writer: anytype) !void { } pub const dependencies = deps: { - var deps: [css_files.len + icons.len][]const u8 = undefined; - for (css_files, 0..) |css_file, i| { - deps[i] = std.fmt.comptimePrint("src/apprt/gtk/{s}", .{css_file}); + const total = css_files.len + icons.len + ui_files.len + blueprint_files.len; + var deps: [total][]const u8 = undefined; + var index: usize = 0; + for (css_files) |css_file| { + deps[index] = std.fmt.comptimePrint("src/apprt/gtk/{s}", .{css_file}); + index += 1; + } + for (icons) |icon| { + deps[index] = std.fmt.comptimePrint("images/icons/icon_{s}.png", .{icon.source}); + index += 1; + } + for (ui_files) |ui_file| { + deps[index] = std.fmt.comptimePrint("src/apprt/gtk/ui/{s}.ui", .{ui_file}); + index += 1; } - for (icons, css_files.len..) |icon, i| { - deps[i] = std.fmt.comptimePrint("images/icons/icon_{s}.png", .{icon.source}); + for (blueprint_files) |blueprint_file| { + deps[index] = std.fmt.comptimePrint("src/apprt/gtk/ui/{s}.blp", .{blueprint_file}); + index += 1; } break :deps deps; }; diff --git a/src/apprt/gtk/headerbar.zig b/src/apprt/gtk/headerbar.zig index 5bb92aca2e..8f4c58fc4b 100644 --- a/src/apprt/gtk/headerbar.zig +++ b/src/apprt/gtk/headerbar.zig @@ -1,73 +1,59 @@ +const HeaderBar = @This(); + const std = @import("std"); const c = @import("c.zig").c; const Window = @import("Window.zig"); -const adwaita = @import("adwaita.zig"); - -const AdwHeaderBar = if (adwaita.versionAtLeast(0, 0, 0)) c.AdwHeaderBar else void; - -pub const HeaderBar = union(enum) { - adw: *AdwHeaderBar, - gtk: *c.GtkHeaderBar, - - pub fn init(window: *Window) HeaderBar { - if ((comptime adwaita.versionAtLeast(1, 4, 0)) and - adwaita.enabled(&window.app.config)) - { - return initAdw(); - } - - return initGtk(); - } - - fn initAdw() HeaderBar { - const headerbar = c.adw_header_bar_new(); - return .{ .adw = @ptrCast(headerbar) }; - } - - fn initGtk() HeaderBar { - const headerbar = c.gtk_header_bar_new(); - return .{ .gtk = @ptrCast(headerbar) }; - } - - pub fn setVisible(self: HeaderBar, visible: bool) void { - c.gtk_widget_set_visible(self.asWidget(), @intFromBool(visible)); - } - - pub fn asWidget(self: HeaderBar) *c.GtkWidget { - return switch (self) { - .adw => |headerbar| @ptrCast(@alignCast(headerbar)), - .gtk => |headerbar| @ptrCast(@alignCast(headerbar)), - }; - } - - pub fn packEnd(self: HeaderBar, widget: *c.GtkWidget) void { - switch (self) { - .adw => |headerbar| if (comptime adwaita.versionAtLeast(0, 0, 0)) { - c.adw_header_bar_pack_end( - @ptrCast(@alignCast(headerbar)), - widget, - ); - }, - .gtk => |headerbar| c.gtk_header_bar_pack_end( - @ptrCast(@alignCast(headerbar)), - widget, - ), - } - } - pub fn packStart(self: HeaderBar, widget: *c.GtkWidget) void { - switch (self) { - .adw => |headerbar| if (comptime adwaita.versionAtLeast(0, 0, 0)) { - c.adw_header_bar_pack_start( - @ptrCast(@alignCast(headerbar)), - widget, - ); - }, - .gtk => |headerbar| c.gtk_header_bar_pack_start( - @ptrCast(@alignCast(headerbar)), - widget, - ), - } - } -}; +/// the Adwaita headerbar widget +headerbar: *c.AdwHeaderBar, + +/// the Adwaita window title widget +title: *c.AdwWindowTitle, + +pub fn init(self: *HeaderBar) void { + const window: *Window = @fieldParentPtr("headerbar", self); + self.* = .{ + .headerbar = @ptrCast(@alignCast(c.adw_header_bar_new())), + .title = @ptrCast(@alignCast(c.adw_window_title_new( + c.gtk_window_get_title(window.window) orelse "Ghostty", + null, + ))), + }; + c.adw_header_bar_set_title_widget( + self.headerbar, + @ptrCast(@alignCast(self.title)), + ); +} + +pub fn setVisible(self: *const HeaderBar, visible: bool) void { + c.gtk_widget_set_visible(self.asWidget(), @intFromBool(visible)); +} + +pub fn asWidget(self: *const HeaderBar) *c.GtkWidget { + return @ptrCast(@alignCast(self.headerbar)); +} + +pub fn packEnd(self: *const HeaderBar, widget: *c.GtkWidget) void { + c.adw_header_bar_pack_end( + @ptrCast(@alignCast(self.headerbar)), + widget, + ); +} + +pub fn packStart(self: *const HeaderBar, widget: *c.GtkWidget) void { + c.adw_header_bar_pack_start( + @ptrCast(@alignCast(self.headerbar)), + widget, + ); +} + +pub fn setTitle(self: *const HeaderBar, title: [:0]const u8) void { + const window: *const Window = @fieldParentPtr("headerbar", self); + c.gtk_window_set_title(window.window, title); + c.adw_window_title_set_title(self.title, title); +} + +pub fn setSubtitle(self: *const HeaderBar, subtitle: [:0]const u8) void { + c.adw_window_title_set_subtitle(self.title, subtitle); +} diff --git a/src/apprt/gtk/key.zig b/src/apprt/gtk/key.zig index 311bff0da4..60f12edca0 100644 --- a/src/apprt/gtk/key.zig +++ b/src/apprt/gtk/key.zig @@ -2,7 +2,7 @@ const std = @import("std"); const build_options = @import("build_options"); const input = @import("../../input.zig"); const c = @import("c.zig").c; -const x11 = @import("x11.zig"); +const winproto = @import("winproto.zig"); /// Returns a GTK accelerator string from a trigger. pub fn accelFromTrigger(buf: []u8, trigger: input.Binding.Trigger) !?[:0]const u8 { @@ -105,45 +105,66 @@ pub fn keyvalUnicodeUnshifted( /// This requires a lot of context because the GdkEvent /// doesn't contain enough on its own. pub fn eventMods( - widget: *c.GtkWidget, event: *c.GdkEvent, physical_key: input.Key, gtk_mods: c.GdkModifierType, - x11_xkb: ?*x11.Xkb, + action: input.Action, + app_winproto: *winproto.App, ) input.Mods { const device = c.gdk_event_get_device(event); - var mods = mods: { - // Add any modifier state events from Xkb if we have them (X11 - // only). Null back from the Xkb call means there was no modifier - // event to read. This likely means that the key event did not - // result in a modifier change and we can safely rely on the GDK - // state. - if (comptime build_options.x11) { - const display = c.gtk_widget_get_display(widget); - if (x11_xkb) |xkb| { - if (xkb.modifier_state_from_notify(display)) |x11_mods| break :mods x11_mods; - break :mods translateMods(gtk_mods); - } - } - - // On Wayland, we have to use the GDK device because the mods sent - // to this event do not have the modifier key applied if it was - // pressed (i.e. left control). - break :mods translateMods(c.gdk_device_get_modifier_state(device)); - }; - + var mods = app_winproto.eventMods(device, gtk_mods); mods.num_lock = c.gdk_device_get_num_lock_state(device) == 1; + // We use the physical key to determine sided modifiers. As + // far as I can tell there's no other way to reliably determine + // this. + // + // We also set the main modifier to true if either side is true, + // since on both X11/Wayland, GTK doesn't set the main modifier + // if only the modifier key is pressed, but our core logic + // relies on it. switch (physical_key) { - .left_shift => mods.sides.shift = .left, - .right_shift => mods.sides.shift = .right, - .left_control => mods.sides.ctrl = .left, - .right_control => mods.sides.ctrl = .right, - .left_alt => mods.sides.alt = .left, - .right_alt => mods.sides.alt = .right, - .left_super => mods.sides.super = .left, - .right_super => mods.sides.super = .right, + .left_shift => { + mods.shift = action != .release; + mods.sides.shift = .left; + }, + + .right_shift => { + mods.shift = action != .release; + mods.sides.shift = .right; + }, + + .left_control => { + mods.ctrl = action != .release; + mods.sides.ctrl = .left; + }, + + .right_control => { + mods.ctrl = action != .release; + mods.sides.ctrl = .right; + }, + + .left_alt => { + mods.alt = action != .release; + mods.sides.alt = .left; + }, + + .right_alt => { + mods.alt = action != .release; + mods.sides.alt = .right; + }, + + .left_super => { + mods.super = action != .release; + mods.sides.super = .left; + }, + + .right_super => { + mods.super = action != .release; + mods.sides.super = .right; + }, + else => {}, } diff --git a/src/apprt/gtk/notebook.zig b/src/apprt/gtk/notebook.zig index 4676c2529f..e411ba9ad9 100644 --- a/src/apprt/gtk/notebook.zig +++ b/src/apprt/gtk/notebook.zig @@ -1,164 +1,193 @@ +/// An abstraction over the GTK notebook and Adwaita tab view to manage +/// all the terminal tabs in a window. +const Notebook = @This(); + const std = @import("std"); const assert = std.debug.assert; const c = @import("c.zig").c; const Window = @import("Window.zig"); const Tab = @import("Tab.zig"); -const NotebookAdw = @import("notebook_adw.zig").NotebookAdw; -const NotebookGtk = @import("notebook_gtk.zig").NotebookGtk; const adwaita = @import("adwaita.zig"); const log = std.log.scoped(.gtk); -const AdwTabView = if (adwaita.versionAtLeast(0, 0, 0)) c.AdwTabView else anyopaque; +/// the tab view +tab_view: *c.AdwTabView, -/// An abstraction over the GTK notebook and Adwaita tab view to manage -/// all the terminal tabs in a window. -/// An abstraction over the GTK notebook and Adwaita tab view to manage -/// all the terminal tabs in a window. -pub const Notebook = union(enum) { - adw: NotebookAdw, - gtk: NotebookGtk, +/// Set to true so that the adw close-page handler knows we're forcing +/// and to allow a close to happen with no confirm. This is a bit of a hack +/// because we currently use GTK alerts to confirm tab close and they +/// don't carry with them the ADW state that we are confirming or not. +/// Long term we should move to ADW alerts so we can know if we are +/// confirming or not. +forcing_close: bool = false, - pub fn init(self: *Notebook) void { - const window: *Window = @fieldParentPtr("notebook", self); - const app = window.app; - if (adwaita.enabled(&app.config)) return NotebookAdw.init(self); +pub fn init(self: *Notebook) void { + const window: *Window = @fieldParentPtr("notebook", self); - return NotebookGtk.init(self); - } + const tab_view: *c.AdwTabView = c.adw_tab_view_new() orelse unreachable; + c.gtk_widget_add_css_class(@ptrCast(@alignCast(tab_view)), "notebook"); - pub fn asWidget(self: *Notebook) *c.GtkWidget { - return switch (self.*) { - .adw => |*adw| adw.asWidget(), - .gtk => |*gtk| gtk.asWidget(), - }; + if (adwaita.versionAtLeast(1, 2, 0)) { + // Adwaita enables all of the shortcuts by default. + // We want to manage keybindings ourselves. + c.adw_tab_view_remove_shortcuts(tab_view, c.ADW_TAB_VIEW_SHORTCUT_ALL_SHORTCUTS); } - pub fn nPages(self: *Notebook) c_int { - return switch (self.*) { - .adw => |*adw| adw.nPages(), - .gtk => |*gtk| gtk.nPages(), - }; - } + self.* = .{ + .tab_view = tab_view, + }; - /// Returns the index of the currently selected page. - /// Returns null if the notebook has no pages. - fn currentPage(self: *Notebook) ?c_int { - return switch (self.*) { - .adw => |*adw| adw.currentPage(), - .gtk => |*gtk| gtk.currentPage(), - }; - } + _ = c.g_signal_connect_data(tab_view, "page-attached", c.G_CALLBACK(&adwPageAttached), window, null, c.G_CONNECT_DEFAULT); + _ = c.g_signal_connect_data(tab_view, "close-page", c.G_CALLBACK(&adwClosePage), window, null, c.G_CONNECT_DEFAULT); + _ = c.g_signal_connect_data(tab_view, "create-window", c.G_CALLBACK(&adwTabViewCreateWindow), window, null, c.G_CONNECT_DEFAULT); + _ = c.g_signal_connect_data(tab_view, "notify::selected-page", c.G_CALLBACK(&adwSelectPage), window, null, c.G_CONNECT_DEFAULT); +} - /// Returns the currently selected tab or null if there are none. - pub fn currentTab(self: *Notebook) ?*Tab { - return switch (self.*) { - .adw => |*adw| adw.currentTab(), - .gtk => |*gtk| gtk.currentTab(), - }; - } +pub fn asWidget(self: *Notebook) *c.GtkWidget { + return @ptrCast(@alignCast(self.tab_view)); +} - pub fn gotoNthTab(self: *Notebook, position: c_int) void { - switch (self.*) { - .adw => |*adw| adw.gotoNthTab(position), - .gtk => |*gtk| gtk.gotoNthTab(position), - } - } +pub fn nPages(self: *Notebook) c_int { + return c.adw_tab_view_get_n_pages(self.tab_view); +} - pub fn getTabPosition(self: *Notebook, tab: *Tab) ?c_int { - return switch (self.*) { - .adw => |*adw| adw.getTabPosition(tab), - .gtk => |*gtk| gtk.getTabPosition(tab), - }; - } +/// Returns the index of the currently selected page. +/// Returns null if the notebook has no pages. +fn currentPage(self: *Notebook) ?c_int { + const page = c.adw_tab_view_get_selected_page(self.tab_view) orelse return null; + return c.adw_tab_view_get_page_position(self.tab_view, page); +} - pub fn gotoPreviousTab(self: *Notebook, tab: *Tab) void { - const page_idx = self.getTabPosition(tab) orelse return; +/// Returns the currently selected tab or null if there are none. +pub fn currentTab(self: *Notebook) ?*Tab { + const page = c.adw_tab_view_get_selected_page(self.tab_view) orelse return null; + const child = c.adw_tab_page_get_child(page); + return @ptrCast(@alignCast( + c.g_object_get_data(@ptrCast(child), Tab.GHOSTTY_TAB) orelse return null, + )); +} - // The next index is the previous or we wrap around. - const next_idx = if (page_idx > 0) page_idx - 1 else next_idx: { - const max = self.nPages(); - break :next_idx max -| 1; - }; +pub fn gotoNthTab(self: *Notebook, position: c_int) bool { + const page_to_select = c.adw_tab_view_get_nth_page(self.tab_view, position) orelse return false; + c.adw_tab_view_set_selected_page(self.tab_view, page_to_select); + return true; +} - // Do nothing if we have one tab - if (next_idx == page_idx) return; +pub fn getTabPosition(self: *Notebook, tab: *Tab) ?c_int { + const page = c.adw_tab_view_get_page(self.tab_view, @ptrCast(tab.box)) orelse return null; + return c.adw_tab_view_get_page_position(self.tab_view, page); +} - self.gotoNthTab(next_idx); - } +pub fn gotoPreviousTab(self: *Notebook, tab: *Tab) bool { + const page_idx = self.getTabPosition(tab) orelse return false; - pub fn gotoNextTab(self: *Notebook, tab: *Tab) void { - const page_idx = self.getTabPosition(tab) orelse return; + // The next index is the previous or we wrap around. + const next_idx = if (page_idx > 0) page_idx - 1 else next_idx: { + const max = self.nPages(); + break :next_idx max -| 1; + }; - const max = self.nPages() -| 1; - const next_idx = if (page_idx < max) page_idx + 1 else 0; - if (next_idx == page_idx) return; + // Do nothing if we have one tab + if (next_idx == page_idx) return false; - self.gotoNthTab(next_idx); - } + return self.gotoNthTab(next_idx); +} - pub fn moveTab(self: *Notebook, tab: *Tab, position: c_int) void { - const page_idx = self.getTabPosition(tab) orelse return; +pub fn gotoNextTab(self: *Notebook, tab: *Tab) bool { + const page_idx = self.getTabPosition(tab) orelse return false; - const max = self.nPages() -| 1; - var new_position: c_int = page_idx + position; + const max = self.nPages() -| 1; + const next_idx = if (page_idx < max) page_idx + 1 else 0; + if (next_idx == page_idx) return false; - if (new_position < 0) { - new_position = max + new_position + 1; - } else if (new_position > max) { - new_position = new_position - max - 1; - } + return self.gotoNthTab(next_idx); +} - if (new_position == page_idx) return; - self.reorderPage(tab, new_position); - } +pub fn moveTab(self: *Notebook, tab: *Tab, position: c_int) void { + const page_idx = self.getTabPosition(tab) orelse return; - pub fn reorderPage(self: *Notebook, tab: *Tab, position: c_int) void { - switch (self.*) { - .adw => |*adw| adw.reorderPage(tab, position), - .gtk => |*gtk| gtk.reorderPage(tab, position), - } - } + const max = self.nPages() -| 1; + var new_position: c_int = page_idx + position; - pub fn setTabLabel(self: *Notebook, tab: *Tab, title: [:0]const u8) void { - switch (self.*) { - .adw => |*adw| adw.setTabLabel(tab, title), - .gtk => |*gtk| gtk.setTabLabel(tab, title), - } + if (new_position < 0) { + new_position = max + new_position + 1; + } else if (new_position > max) { + new_position = new_position - max - 1; } - pub fn setTabTooltip(self: *Notebook, tab: *Tab, tooltip: [:0]const u8) void { - switch (self.*) { - .adw => |*adw| adw.setTabTooltip(tab, tooltip), - .gtk => |*gtk| gtk.setTabTooltip(tab, tooltip), - } - } + if (new_position == page_idx) return; + self.reorderPage(tab, new_position); +} - fn newTabInsertPosition(self: *Notebook, tab: *Tab) c_int { - const numPages = self.nPages(); - return switch (tab.window.app.config.@"window-new-tab-position") { - .current => if (self.currentPage()) |page| page + 1 else numPages, - .end => numPages, - }; - } +pub fn reorderPage(self: *Notebook, tab: *Tab, position: c_int) void { + const page = c.adw_tab_view_get_page(self.tab_view, @ptrCast(tab.box)); + _ = c.adw_tab_view_reorder_page(self.tab_view, page, position); +} - /// Adds a new tab with the given title to the notebook. - pub fn addTab(self: *Notebook, tab: *Tab, title: [:0]const u8) void { - const position = self.newTabInsertPosition(tab); - switch (self.*) { - .adw => |*adw| adw.addTab(tab, position, title), - .gtk => |*gtk| gtk.addTab(tab, position, title), - } - } +pub fn setTabLabel(self: *Notebook, tab: *Tab, title: [:0]const u8) void { + const page = c.adw_tab_view_get_page(self.tab_view, @ptrCast(tab.box)); + c.adw_tab_page_set_title(page, title.ptr); +} + +pub fn setTabTooltip(self: *Notebook, tab: *Tab, tooltip: [:0]const u8) void { + const page = c.adw_tab_view_get_page(self.tab_view, @ptrCast(tab.box)); + c.adw_tab_page_set_tooltip(page, tooltip.ptr); +} + +fn newTabInsertPosition(self: *Notebook, tab: *Tab) c_int { + const numPages = self.nPages(); + return switch (tab.window.app.config.@"window-new-tab-position") { + .current => if (self.currentPage()) |page| page + 1 else numPages, + .end => numPages, + }; +} - pub fn closeTab(self: *Notebook, tab: *Tab) void { - switch (self.*) { - .adw => |*adw| adw.closeTab(tab), - .gtk => |*gtk| gtk.closeTab(tab), +/// Adds a new tab with the given title to the notebook. +pub fn addTab(self: *Notebook, tab: *Tab, title: [:0]const u8) void { + const position = self.newTabInsertPosition(tab); + const box_widget: *c.GtkWidget = @ptrCast(tab.box); + const page = c.adw_tab_view_insert(self.tab_view, box_widget, position); + c.adw_tab_page_set_title(page, title.ptr); + c.adw_tab_view_set_selected_page(self.tab_view, page); +} + +pub fn closeTab(self: *Notebook, tab: *Tab) void { + // closeTab always expects to close unconditionally so we mark this + // as true so that the close_page call below doesn't request + // confirmation. + self.forcing_close = true; + const n = self.nPages(); + defer { + // self becomes invalid if we close the last page because we close + // the whole window + if (n > 1) self.forcing_close = false; + } + + const page = c.adw_tab_view_get_page(self.tab_view, @ptrCast(tab.box)) orelse return; + c.adw_tab_view_close_page(self.tab_view, page); + + // If we have no more tabs we close the window + if (self.nPages() == 0) { + const window = tab.window.window; + + // libadw versions <= 1.3.x leak the final page view + // which causes our surface to not properly cleanup. We + // unref to force the cleanup. This will trigger a critical + // warning from GTK, but I don't know any other workaround. + // Note: I'm not actually sure if 1.4.0 contains the fix, + // I just know that 1.3.x is broken and 1.5.1 is fixed. + // If we know that 1.4.0 is fixed, we can change this. + if (!adwaita.versionAtLeast(1, 4, 0)) { + c.g_object_unref(tab.box); } + + // `self` will become invalid after this call because it will have + // been freed up as part of the process of closing the window. + c.gtk_window_destroy(window); } -}; +} pub fn createWindow(currentWindow: *Window) !*Window { const alloc = currentWindow.app.core_app.alloc; @@ -167,3 +196,54 @@ pub fn createWindow(currentWindow: *Window) !*Window { // Create a new window return Window.create(alloc, app); } + +fn adwPageAttached(_: *c.AdwTabView, page: *c.AdwTabPage, _: c_int, ud: ?*anyopaque) callconv(.C) void { + const window: *Window = @ptrCast(@alignCast(ud.?)); + + const child = c.adw_tab_page_get_child(page); + const tab: *Tab = @ptrCast(@alignCast(c.g_object_get_data(@ptrCast(child), Tab.GHOSTTY_TAB) orelse return)); + tab.window = window; + + window.focusCurrentTab(); +} + +fn adwClosePage( + _: *c.AdwTabView, + page: *c.AdwTabPage, + ud: ?*anyopaque, +) callconv(.C) c.gboolean { + const child = c.adw_tab_page_get_child(page); + const tab: *Tab = @ptrCast(@alignCast(c.g_object_get_data( + @ptrCast(child), + Tab.GHOSTTY_TAB, + ) orelse return 0)); + + const window: *Window = @ptrCast(@alignCast(ud.?)); + const notebook = window.notebook; + c.adw_tab_view_close_page_finish( + notebook.tab_view, + page, + @intFromBool(notebook.forcing_close), + ); + if (!notebook.forcing_close) tab.closeWithConfirmation(); + return 1; +} + +fn adwTabViewCreateWindow( + _: *c.AdwTabView, + ud: ?*anyopaque, +) callconv(.C) ?*c.AdwTabView { + const currentWindow: *Window = @ptrCast(@alignCast(ud.?)); + const window = createWindow(currentWindow) catch |err| { + log.warn("error creating new window error={}", .{err}); + return null; + }; + return window.notebook.tab_view; +} + +fn adwSelectPage(_: *c.GObject, _: *c.GParamSpec, ud: ?*anyopaque) void { + const window: *Window = @ptrCast(@alignCast(ud.?)); + const page = c.adw_tab_view_get_selected_page(window.notebook.tab_view) orelse return; + const title = c.adw_tab_page_get_title(page); + window.setTitle(std.mem.span(title)); +} diff --git a/src/apprt/gtk/notebook_adw.zig b/src/apprt/gtk/notebook_adw.zig deleted file mode 100644 index 85083a97eb..0000000000 --- a/src/apprt/gtk/notebook_adw.zig +++ /dev/null @@ -1,163 +0,0 @@ -const std = @import("std"); -const assert = std.debug.assert; -const c = @import("c.zig").c; - -const Window = @import("Window.zig"); -const Tab = @import("Tab.zig"); -const Notebook = @import("notebook.zig").Notebook; -const createWindow = @import("notebook.zig").createWindow; -const adwaita = @import("adwaita.zig"); - -const log = std.log.scoped(.gtk); - -const AdwTabView = if (adwaita.versionAtLeast(0, 0, 0)) c.AdwTabView else anyopaque; -const AdwTabPage = if (adwaita.versionAtLeast(0, 0, 0)) c.AdwTabPage else anyopaque; - -pub const NotebookAdw = struct { - /// the tab view - tab_view: *AdwTabView, - - pub fn init(notebook: *Notebook) void { - const window: *Window = @fieldParentPtr("notebook", notebook); - const app = window.app; - assert(adwaita.enabled(&app.config)); - - const tab_view: *c.AdwTabView = c.adw_tab_view_new().?; - c.gtk_widget_add_css_class(@ptrCast(@alignCast(tab_view)), "notebook"); - - if (comptime adwaita.versionAtLeast(1, 2, 0) and adwaita.versionAtLeast(1, 2, 0)) { - // Adwaita enables all of the shortcuts by default. - // We want to manage keybindings ourselves. - c.adw_tab_view_remove_shortcuts(tab_view, c.ADW_TAB_VIEW_SHORTCUT_ALL_SHORTCUTS); - } - - notebook.* = .{ - .adw = .{ - .tab_view = tab_view, - }, - }; - - _ = c.g_signal_connect_data(tab_view, "page-attached", c.G_CALLBACK(&adwPageAttached), window, null, c.G_CONNECT_DEFAULT); - _ = c.g_signal_connect_data(tab_view, "create-window", c.G_CALLBACK(&adwTabViewCreateWindow), window, null, c.G_CONNECT_DEFAULT); - _ = c.g_signal_connect_data(tab_view, "notify::selected-page", c.G_CALLBACK(&adwSelectPage), window, null, c.G_CONNECT_DEFAULT); - } - - pub fn asWidget(self: *NotebookAdw) *c.GtkWidget { - return @ptrCast(@alignCast(self.tab_view)); - } - - pub fn nPages(self: *NotebookAdw) c_int { - if (comptime adwaita.versionAtLeast(0, 0, 0)) - return c.adw_tab_view_get_n_pages(self.tab_view) - else - unreachable; - } - - /// Returns the index of the currently selected page. - /// Returns null if the notebook has no pages. - pub fn currentPage(self: *NotebookAdw) ?c_int { - if (comptime !adwaita.versionAtLeast(0, 0, 0)) unreachable; - const page = c.adw_tab_view_get_selected_page(self.tab_view) orelse return null; - return c.adw_tab_view_get_page_position(self.tab_view, page); - } - - /// Returns the currently selected tab or null if there are none. - pub fn currentTab(self: *NotebookAdw) ?*Tab { - if (comptime !adwaita.versionAtLeast(0, 0, 0)) unreachable; - const page = c.adw_tab_view_get_selected_page(self.tab_view) orelse return null; - const child = c.adw_tab_page_get_child(page); - return @ptrCast(@alignCast( - c.g_object_get_data(@ptrCast(child), Tab.GHOSTTY_TAB) orelse return null, - )); - } - - pub fn gotoNthTab(self: *NotebookAdw, position: c_int) void { - if (comptime !adwaita.versionAtLeast(0, 0, 0)) unreachable; - const page_to_select = c.adw_tab_view_get_nth_page(self.tab_view, position); - c.adw_tab_view_set_selected_page(self.tab_view, page_to_select); - } - - pub fn getTabPosition(self: *NotebookAdw, tab: *Tab) ?c_int { - if (comptime !adwaita.versionAtLeast(0, 0, 0)) unreachable; - const page = c.adw_tab_view_get_page(self.tab_view, @ptrCast(tab.box)) orelse return null; - return c.adw_tab_view_get_page_position(self.tab_view, page); - } - - pub fn reorderPage(self: *NotebookAdw, tab: *Tab, position: c_int) void { - if (comptime !adwaita.versionAtLeast(0, 0, 0)) unreachable; - const page = c.adw_tab_view_get_page(self.tab_view, @ptrCast(tab.box)); - _ = c.adw_tab_view_reorder_page(self.tab_view, page, position); - } - - pub fn setTabLabel(self: *NotebookAdw, tab: *Tab, title: [:0]const u8) void { - if (comptime !adwaita.versionAtLeast(0, 0, 0)) unreachable; - const page = c.adw_tab_view_get_page(self.tab_view, @ptrCast(tab.box)); - c.adw_tab_page_set_title(page, title.ptr); - } - - pub fn setTabTooltip(self: *NotebookAdw, tab: *Tab, tooltip: [:0]const u8) void { - if (comptime !adwaita.versionAtLeast(0, 0, 0)) unreachable; - const page = c.adw_tab_view_get_page(self.tab_view, @ptrCast(tab.box)); - c.adw_tab_page_set_tooltip(page, tooltip.ptr); - } - - pub fn addTab(self: *NotebookAdw, tab: *Tab, position: c_int, title: [:0]const u8) void { - if (comptime !adwaita.versionAtLeast(0, 0, 0)) unreachable; - const box_widget: *c.GtkWidget = @ptrCast(tab.box); - const page = c.adw_tab_view_insert(self.tab_view, box_widget, position); - c.adw_tab_page_set_title(page, title.ptr); - c.adw_tab_view_set_selected_page(self.tab_view, page); - } - - pub fn closeTab(self: *NotebookAdw, tab: *Tab) void { - if (comptime !adwaita.versionAtLeast(0, 0, 0)) unreachable; - - const page = c.adw_tab_view_get_page(self.tab_view, @ptrCast(tab.box)) orelse return; - c.adw_tab_view_close_page(self.tab_view, page); - - // If we have no more tabs we close the window - if (self.nPages() == 0) { - // libadw versions <= 1.3.x leak the final page view - // which causes our surface to not properly cleanup. We - // unref to force the cleanup. This will trigger a critical - // warning from GTK, but I don't know any other workaround. - // Note: I'm not actually sure if 1.4.0 contains the fix, - // I just know that 1.3.x is broken and 1.5.1 is fixed. - // If we know that 1.4.0 is fixed, we can change this. - if (!adwaita.versionAtLeast(1, 4, 0)) { - c.g_object_unref(tab.box); - } - - c.gtk_window_destroy(tab.window.window); - } - } -}; - -fn adwPageAttached(_: *AdwTabView, page: *c.AdwTabPage, _: c_int, ud: ?*anyopaque) callconv(.C) void { - const window: *Window = @ptrCast(@alignCast(ud.?)); - - const child = c.adw_tab_page_get_child(page); - const tab: *Tab = @ptrCast(@alignCast(c.g_object_get_data(@ptrCast(child), Tab.GHOSTTY_TAB) orelse return)); - tab.window = window; - - window.focusCurrentTab(); -} - -fn adwTabViewCreateWindow( - _: *AdwTabView, - ud: ?*anyopaque, -) callconv(.C) ?*AdwTabView { - const currentWindow: *Window = @ptrCast(@alignCast(ud.?)); - const window = createWindow(currentWindow) catch |err| { - log.warn("error creating new window error={}", .{err}); - return null; - }; - return window.notebook.adw.tab_view; -} - -fn adwSelectPage(_: *c.GObject, _: *c.GParamSpec, ud: ?*anyopaque) void { - const window: *Window = @ptrCast(@alignCast(ud.?)); - const page = c.adw_tab_view_get_selected_page(window.notebook.adw.tab_view) orelse return; - const title = c.adw_tab_page_get_title(page); - c.gtk_window_set_title(window.window, title); -} diff --git a/src/apprt/gtk/notebook_gtk.zig b/src/apprt/gtk/notebook_gtk.zig deleted file mode 100644 index 6e8b016ba4..0000000000 --- a/src/apprt/gtk/notebook_gtk.zig +++ /dev/null @@ -1,285 +0,0 @@ -const std = @import("std"); -const assert = std.debug.assert; -const c = @import("c.zig").c; - -const Window = @import("Window.zig"); -const Tab = @import("Tab.zig"); -const Notebook = @import("notebook.zig").Notebook; -const createWindow = @import("notebook.zig").createWindow; - -const log = std.log.scoped(.gtk); - -/// An abstraction over the GTK notebook and Adwaita tab view to manage -/// all the terminal tabs in a window. -pub const NotebookGtk = struct { - notebook: *c.GtkNotebook, - - pub fn init(notebook: *Notebook) void { - const window: *Window = @fieldParentPtr("notebook", notebook); - const app = window.app; - - // Create a notebook to hold our tabs. - const notebook_widget: *c.GtkWidget = c.gtk_notebook_new(); - c.gtk_widget_add_css_class(notebook_widget, "notebook"); - - const gtk_notebook: *c.GtkNotebook = @ptrCast(notebook_widget); - const notebook_tab_pos: c_uint = switch (app.config.@"gtk-tabs-location") { - .top, .hidden => c.GTK_POS_TOP, - .bottom => c.GTK_POS_BOTTOM, - .left => c.GTK_POS_LEFT, - .right => c.GTK_POS_RIGHT, - }; - c.gtk_notebook_set_tab_pos(gtk_notebook, notebook_tab_pos); - c.gtk_notebook_set_scrollable(gtk_notebook, 1); - c.gtk_notebook_set_show_tabs(gtk_notebook, 0); - c.gtk_notebook_set_show_border(gtk_notebook, 0); - - // This enables all Ghostty terminal tabs to be exchanged across windows. - c.gtk_notebook_set_group_name(gtk_notebook, "ghostty-terminal-tabs"); - - // This is important so the notebook expands to fit available space. - // Otherwise, it will be zero/zero in the box below. - c.gtk_widget_set_vexpand(notebook_widget, 1); - c.gtk_widget_set_hexpand(notebook_widget, 1); - - // Remove the background from the stack widget - const stack = c.gtk_widget_get_last_child(notebook_widget); - c.gtk_widget_add_css_class(stack, "transparent"); - - notebook.* = .{ - .gtk = .{ - .notebook = gtk_notebook, - }, - }; - - // All of our events - _ = c.g_signal_connect_data(gtk_notebook, "page-added", c.G_CALLBACK(>kPageAdded), window, null, c.G_CONNECT_DEFAULT); - _ = c.g_signal_connect_data(gtk_notebook, "page-removed", c.G_CALLBACK(>kPageRemoved), window, null, c.G_CONNECT_DEFAULT); - _ = c.g_signal_connect_data(gtk_notebook, "switch-page", c.G_CALLBACK(>kSwitchPage), window, null, c.G_CONNECT_DEFAULT); - _ = c.g_signal_connect_data(gtk_notebook, "create-window", c.G_CALLBACK(>kNotebookCreateWindow), window, null, c.G_CONNECT_DEFAULT); - } - - /// return the underlying widget as a generic GtkWidget - pub fn asWidget(self: *NotebookGtk) *c.GtkWidget { - return @ptrCast(@alignCast(self.notebook)); - } - - /// returns the number of pages in the notebook - pub fn nPages(self: *NotebookGtk) c_int { - return c.gtk_notebook_get_n_pages(self.notebook); - } - - /// Returns the index of the currently selected page. - /// Returns null if the notebook has no pages. - pub fn currentPage(self: *NotebookGtk) ?c_int { - const current = c.gtk_notebook_get_current_page(self.notebook); - return if (current == -1) null else current; - } - - /// Returns the currently selected tab or null if there are none. - pub fn currentTab(self: *NotebookGtk) ?*Tab { - log.warn("currentTab", .{}); - const page = self.currentPage() orelse return null; - const child = c.gtk_notebook_get_nth_page(self.notebook, page); - return @ptrCast(@alignCast( - c.g_object_get_data(@ptrCast(child), Tab.GHOSTTY_TAB) orelse return null, - )); - } - - /// focus the nth tab - pub fn gotoNthTab(self: *NotebookGtk, position: c_int) void { - c.gtk_notebook_set_current_page(self.notebook, position); - } - - /// get the position of the current tab - pub fn getTabPosition(self: *NotebookGtk, tab: *Tab) ?c_int { - const page = c.gtk_notebook_get_page(self.notebook, @ptrCast(tab.box)) orelse return null; - return getNotebookPageIndex(page); - } - - pub fn reorderPage(self: *NotebookGtk, tab: *Tab, position: c_int) void { - c.gtk_notebook_reorder_child(self.notebook, @ptrCast(tab.box), position); - } - - pub fn setTabLabel(_: *NotebookGtk, tab: *Tab, title: [:0]const u8) void { - c.gtk_label_set_text(tab.label_text, title.ptr); - } - - pub fn setTabTooltip(_: *NotebookGtk, tab: *Tab, tooltip: [:0]const u8) void { - c.gtk_widget_set_tooltip_text(@ptrCast(@alignCast(tab.label_text)), tooltip.ptr); - } - - /// Adds a new tab with the given title to the notebook. - pub fn addTab(self: *NotebookGtk, tab: *Tab, position: c_int, title: [:0]const u8) void { - const box_widget: *c.GtkWidget = @ptrCast(tab.box); - - // Build the tab label - const label_box_widget = c.gtk_box_new(c.GTK_ORIENTATION_HORIZONTAL, 0); - const label_box = @as(*c.GtkBox, @ptrCast(label_box_widget)); - const label_text_widget = c.gtk_label_new(title.ptr); - const label_text: *c.GtkLabel = @ptrCast(label_text_widget); - c.gtk_box_append(label_box, label_text_widget); - tab.label_text = label_text; - - const window = tab.window; - if (window.app.config.@"gtk-wide-tabs") { - c.gtk_widget_set_hexpand(label_box_widget, 1); - c.gtk_widget_set_halign(label_box_widget, c.GTK_ALIGN_FILL); - c.gtk_widget_set_hexpand(label_text_widget, 1); - c.gtk_widget_set_halign(label_text_widget, c.GTK_ALIGN_FILL); - - // This ensures that tabs are always equal width. If they're too - // long, they'll be truncated with an ellipsis. - c.gtk_label_set_max_width_chars(label_text, 1); - c.gtk_label_set_ellipsize(label_text, c.PANGO_ELLIPSIZE_END); - - // We need to set a minimum width so that at a certain point - // the notebook will have an arrow button rather than shrinking tabs - // to an unreadably small size. - c.gtk_widget_set_size_request(label_text_widget, 100, 1); - } - - // Build the close button for the tab - const label_close_widget = c.gtk_button_new_from_icon_name("window-close-symbolic"); - const label_close: *c.GtkButton = @ptrCast(label_close_widget); - c.gtk_button_set_has_frame(label_close, 0); - c.gtk_box_append(label_box, label_close_widget); - - const page_idx = c.gtk_notebook_insert_page( - self.notebook, - box_widget, - label_box_widget, - position, - ); - - // Clicks - const gesture_tab_click = c.gtk_gesture_click_new(); - c.gtk_gesture_single_set_button(@ptrCast(gesture_tab_click), 0); - c.gtk_widget_add_controller(label_box_widget, @ptrCast(gesture_tab_click)); - - _ = c.g_signal_connect_data(label_close, "clicked", c.G_CALLBACK(&Tab.gtkTabCloseClick), tab, null, c.G_CONNECT_DEFAULT); - _ = c.g_signal_connect_data(gesture_tab_click, "pressed", c.G_CALLBACK(&Tab.gtkTabClick), tab, null, c.G_CONNECT_DEFAULT); - - // Tab settings - c.gtk_notebook_set_tab_reorderable(self.notebook, box_widget, 1); - c.gtk_notebook_set_tab_detachable(self.notebook, box_widget, 1); - - if (self.nPages() > 1) { - c.gtk_notebook_set_show_tabs(self.notebook, 1); - } - - // Switch to the new tab - c.gtk_notebook_set_current_page(self.notebook, page_idx); - } - - pub fn closeTab(self: *NotebookGtk, tab: *Tab) void { - const page = c.gtk_notebook_get_page(self.notebook, @ptrCast(tab.box)) orelse return; - - // Find page and tab which we're closing - const page_idx = getNotebookPageIndex(page); - - // Remove the page. This will destroy the GTK widgets in the page which - // will trigger Tab cleanup. The `tab` variable is therefore unusable past that point. - c.gtk_notebook_remove_page(self.notebook, page_idx); - - const remaining = self.nPages(); - switch (remaining) { - // If we have no more tabs we close the window - 0 => c.gtk_window_destroy(tab.window.window), - - // If we have one more tab we hide the tab bar - 1 => c.gtk_notebook_set_show_tabs(self.notebook, 0), - - else => {}, - } - - // If we have remaining tabs, we need to make sure we grab focus. - if (remaining > 0) - (self.currentTab() orelse return).window.focusCurrentTab(); - } -}; - -fn getNotebookPageIndex(page: *c.GtkNotebookPage) c_int { - var value: c.GValue = std.mem.zeroes(c.GValue); - defer c.g_value_unset(&value); - _ = c.g_value_init(&value, c.G_TYPE_INT); - c.g_object_get_property( - @ptrCast(@alignCast(page)), - "position", - &value, - ); - - return c.g_value_get_int(&value); -} - -fn gtkPageAdded( - notebook: *c.GtkNotebook, - _: *c.GtkWidget, - page_idx: c.guint, - ud: ?*anyopaque, -) callconv(.C) void { - const self: *Window = @ptrCast(@alignCast(ud.?)); - - // The added page can come from another window with drag and drop, thus we migrate the tab - // window to be self. - const page = c.gtk_notebook_get_nth_page(notebook, @intCast(page_idx)); - const tab: *Tab = @ptrCast(@alignCast( - c.g_object_get_data(@ptrCast(page), Tab.GHOSTTY_TAB) orelse return, - )); - tab.window = self; - - // Whenever a new page is added, we always grab focus of the - // currently selected page. This was added specifically so that when - // we drag a tab out to create a new window ("create-window" event) - // we grab focus in the new window. Without this, the terminal didn't - // have focus. - self.focusCurrentTab(); -} - -fn gtkPageRemoved( - _: *c.GtkNotebook, - _: *c.GtkWidget, - _: c.guint, - ud: ?*anyopaque, -) callconv(.C) void { - log.warn("gtkPageRemoved", .{}); - const window: *Window = @ptrCast(@alignCast(ud.?)); - - // Hide the tab bar if we only have one tab after removal - const remaining = c.gtk_notebook_get_n_pages(window.notebook.gtk.notebook); - - if (remaining == 1) { - c.gtk_notebook_set_show_tabs(window.notebook.gtk.notebook, 0); - } -} - -fn gtkSwitchPage(_: *c.GtkNotebook, page: *c.GtkWidget, _: usize, ud: ?*anyopaque) callconv(.C) void { - const window: *Window = @ptrCast(@alignCast(ud.?)); - const self = &window.notebook.gtk; - const gtk_label_box = @as(*c.GtkWidget, @ptrCast(c.gtk_notebook_get_tab_label(self.notebook, page))); - const gtk_label = @as(*c.GtkLabel, @ptrCast(c.gtk_widget_get_first_child(gtk_label_box))); - const label_text = c.gtk_label_get_text(gtk_label); - c.gtk_window_set_title(window.window, label_text); -} - -fn gtkNotebookCreateWindow( - _: *c.GtkNotebook, - page: *c.GtkWidget, - ud: ?*anyopaque, -) callconv(.C) ?*c.GtkNotebook { - // The tab for the page is stored in the widget data. - const tab: *Tab = @ptrCast(@alignCast( - c.g_object_get_data(@ptrCast(page), Tab.GHOSTTY_TAB) orelse return null, - )); - - const currentWindow: *Window = @ptrCast(@alignCast(ud.?)); - const newWindow = createWindow(currentWindow) catch |err| { - log.warn("error creating new window error={}", .{err}); - return null; - }; - - // And add it to the new window. - tab.window = newWindow; - - return newWindow.notebook.gtk.notebook; -} diff --git a/src/apprt/gtk/wayland.zig b/src/apprt/gtk/wayland.zig deleted file mode 100644 index 92446cc46a..0000000000 --- a/src/apprt/gtk/wayland.zig +++ /dev/null @@ -1,125 +0,0 @@ -const std = @import("std"); -const c = @import("c.zig").c; -const wayland = @import("wayland"); -const wl = wayland.client.wl; -const org = wayland.client.org; -const build_options = @import("build_options"); - -const log = std.log.scoped(.gtk_wayland); - -/// Wayland state that contains application-wide Wayland objects (e.g. wl_display). -pub const AppState = struct { - display: *wl.Display, - blur_manager: ?*org.KdeKwinBlurManager = null, - - pub fn init(display: ?*c.GdkDisplay) ?AppState { - if (comptime !build_options.wayland) return null; - - // It should really never be null - const display_ = display orelse return null; - - // Check if we're actually on Wayland - if (c.g_type_check_instance_is_a( - @ptrCast(@alignCast(display_)), - c.gdk_wayland_display_get_type(), - ) == 0) - return null; - - const wl_display: *wl.Display = @ptrCast(c.gdk_wayland_display_get_wl_display(display_) orelse return null); - - return .{ - .display = wl_display, - }; - } - - pub fn register(self: *AppState) !void { - const registry = try self.display.getRegistry(); - - registry.setListener(*AppState, registryListener, self); - if (self.display.roundtrip() != .SUCCESS) return error.RoundtripFailed; - - log.debug("app wayland init={}", .{self}); - } -}; - -/// Wayland state that contains Wayland objects associated with a window (e.g. wl_surface). -pub const SurfaceState = struct { - app_state: *AppState, - surface: *wl.Surface, - - /// A token that, when present, indicates that the window is blurred. - blur_token: ?*org.KdeKwinBlur = null, - - pub fn init(window: *c.GtkWindow, app_state: *AppState) ?SurfaceState { - if (comptime !build_options.wayland) return null; - - const surface = c.gtk_native_get_surface(@ptrCast(window)) orelse return null; - - // Check if we're actually on Wayland - if (c.g_type_check_instance_is_a( - @ptrCast(@alignCast(surface)), - c.gdk_wayland_surface_get_type(), - ) == 0) - return null; - - const wl_surface: *wl.Surface = @ptrCast(c.gdk_wayland_surface_get_wl_surface(surface) orelse return null); - - return .{ - .app_state = app_state, - .surface = wl_surface, - }; - } - - pub fn deinit(self: *SurfaceState) void { - if (self.blur_token) |blur| blur.release(); - } - - pub fn setBlur(self: *SurfaceState, blurred: bool) !void { - log.debug("setting blur={}", .{blurred}); - - const mgr = self.app_state.blur_manager orelse { - log.warn("can't set blur: org_kde_kwin_blur_manager protocol unavailable", .{}); - return; - }; - - if (self.blur_token) |blur| { - // Only release token when transitioning from blurred -> not blurred - if (!blurred) { - mgr.unset(self.surface); - blur.release(); - self.blur_token = null; - } - } else { - // Only acquire token when transitioning from not blurred -> blurred - if (blurred) { - const blur_token = try mgr.create(self.surface); - blur_token.commit(); - self.blur_token = blur_token; - } - } - } -}; - -fn registryListener(registry: *wl.Registry, event: wl.Registry.Event, state: *AppState) void { - switch (event) { - .global => |global| { - log.debug("got global interface={s}", .{global.interface}); - if (bindInterface(org.KdeKwinBlurManager, registry, global, 1)) |iface| { - state.blur_manager = iface; - return; - } - }, - .global_remove => {}, - } -} - -fn bindInterface(comptime T: type, registry: *wl.Registry, global: anytype, version: u32) ?*T { - if (std.mem.orderZ(u8, global.interface, T.interface.name) == .eq) { - return registry.bind(global.name, T, version) catch |err| { - log.warn("encountered error={} while binding interface {s}", .{ err, global.interface }); - return null; - }; - } else { - return null; - } -} diff --git a/src/apprt/gtk/winproto.zig b/src/apprt/gtk/winproto.zig new file mode 100644 index 0000000000..c752ee6927 --- /dev/null +++ b/src/apprt/gtk/winproto.zig @@ -0,0 +1,140 @@ +const std = @import("std"); +const build_options = @import("build_options"); +const Allocator = std.mem.Allocator; +const c = @import("c.zig").c; +const Config = @import("../../config.zig").Config; +const input = @import("../../input.zig"); +const key = @import("key.zig"); + +pub const noop = @import("winproto/noop.zig"); +pub const x11 = @import("winproto/x11.zig"); +pub const wayland = @import("winproto/wayland.zig"); + +pub const Protocol = enum { + none, + wayland, + x11, +}; + +/// App-state for the underlying windowing protocol. There should be one +/// instance of this struct per application. +pub const App = union(Protocol) { + none: noop.App, + wayland: if (build_options.wayland) wayland.App else noop.App, + x11: if (build_options.x11) x11.App else noop.App, + + pub fn init( + alloc: Allocator, + gdk_display: *c.GdkDisplay, + app_id: [:0]const u8, + config: *const Config, + ) !App { + inline for (@typeInfo(App).Union.fields) |field| { + if (try field.type.init( + alloc, + gdk_display, + app_id, + config, + )) |v| { + return @unionInit(App, field.name, v); + } + } + + return .{ .none = .{} }; + } + + pub fn deinit(self: *App, alloc: Allocator) void { + switch (self.*) { + inline else => |*v| v.deinit(alloc), + } + } + + pub fn eventMods( + self: *App, + device: ?*c.GdkDevice, + gtk_mods: c.GdkModifierType, + ) input.Mods { + return switch (self.*) { + inline else => |*v| v.eventMods(device, gtk_mods), + } orelse key.translateMods(gtk_mods); + } +}; + +/// Per-Window state for the underlying windowing protocol. +/// +/// In Wayland, the terminology used is "Surface" and for it, this is +/// really "Surface"-specific state. But Ghostty uses the term "Surface" +/// heavily to mean something completely different, so we use "Window" here +/// to better match what it generally maps to in the Ghostty codebase. +pub const Window = union(Protocol) { + none: noop.Window, + wayland: if (build_options.wayland) wayland.Window else noop.Window, + x11: if (build_options.x11) x11.Window else noop.Window, + + pub fn init( + alloc: Allocator, + app: *App, + window: *c.GtkWindow, + config: *const Config, + ) !Window { + return switch (app.*) { + inline else => |*v, tag| { + inline for (@typeInfo(Window).Union.fields) |field| { + if (comptime std.mem.eql( + u8, + field.name, + @tagName(tag), + )) return @unionInit( + Window, + field.name, + try field.type.init( + alloc, + v, + window, + config, + ), + ); + } + }, + }; + } + + pub fn deinit(self: *Window, alloc: Allocator) void { + switch (self.*) { + inline else => |*v| v.deinit(alloc), + } + } + + pub fn resizeEvent(self: *Window) !void { + switch (self.*) { + inline else => |*v| try v.resizeEvent(), + } + } + + pub fn updateConfigEvent( + self: *Window, + config: *const Config, + ) !void { + switch (self.*) { + inline else => |*v| try v.updateConfigEvent(config), + } + } + + pub fn syncAppearance(self: *Window) !void { + switch (self.*) { + inline else => |*v| try v.syncAppearance(), + } + } + + pub fn clientSideDecorationEnabled(self: Window) bool { + return switch (self) { + inline else => |v| v.clientSideDecorationEnabled(), + }; + } + + pub fn addSubprocessEnv(self: *Window, env: *std.process.EnvMap) !void { + switch (self.*) { + inline else => |*v| try v.addSubprocessEnv(env), + } + } +}; diff --git a/src/apprt/gtk/winproto/noop.zig b/src/apprt/gtk/winproto/noop.zig new file mode 100644 index 0000000000..cb1c0e9ebb --- /dev/null +++ b/src/apprt/gtk/winproto/noop.zig @@ -0,0 +1,66 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; +const c = @import("../c.zig").c; +const Config = @import("../../../config.zig").Config; +const input = @import("../../../input.zig"); + +const log = std.log.scoped(.winproto_noop); + +pub const App = struct { + pub fn init( + _: Allocator, + _: *c.GdkDisplay, + _: [:0]const u8, + _: *const Config, + ) !?App { + return null; + } + + pub fn deinit(self: *App, alloc: Allocator) void { + _ = self; + _ = alloc; + } + + pub fn eventMods( + _: *App, + _: ?*c.GdkDevice, + _: c.GdkModifierType, + ) ?input.Mods { + return null; + } +}; + +pub const Window = struct { + pub fn init( + _: Allocator, + _: *App, + _: *c.GtkWindow, + _: *const Config, + ) !Window { + return .{}; + } + + pub fn deinit(self: Window, alloc: Allocator) void { + _ = self; + _ = alloc; + } + + pub fn updateConfigEvent( + _: *Window, + _: *const Config, + ) !void {} + + pub fn resizeEvent(_: *Window) !void {} + + pub fn syncAppearance(_: *Window) !void {} + + /// This returns true if CSD is enabled for this window. This + /// should be the actual present state of the window, not the + /// desired state. + pub fn clientSideDecorationEnabled(self: Window) bool { + _ = self; + return true; + } + + pub fn addSubprocessEnv(_: *Window, _: *std.process.EnvMap) !void {} +}; diff --git a/src/apprt/gtk/winproto/wayland.zig b/src/apprt/gtk/winproto/wayland.zig new file mode 100644 index 0000000000..f2ef17d73a --- /dev/null +++ b/src/apprt/gtk/winproto/wayland.zig @@ -0,0 +1,308 @@ +//! Wayland protocol implementation for the Ghostty GTK apprt. +const std = @import("std"); +const wayland = @import("wayland"); +const Allocator = std.mem.Allocator; +const c = @import("../c.zig").c; +const Config = @import("../../../config.zig").Config; +const input = @import("../../../input.zig"); + +const wl = wayland.client.wl; +const org = wayland.client.org; + +const log = std.log.scoped(.winproto_wayland); + +/// Wayland state that contains application-wide Wayland objects (e.g. wl_display). +pub const App = struct { + display: *wl.Display, + context: *Context, + + const Context = struct { + kde_blur_manager: ?*org.KdeKwinBlurManager = null, + + // FIXME: replace with `zxdg_decoration_v1` once GTK merges + // https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/6398 + kde_decoration_manager: ?*org.KdeKwinServerDecorationManager = null, + + default_deco_mode: ?org.KdeKwinServerDecorationManager.Mode = null, + }; + + pub fn init( + alloc: Allocator, + gdk_display: *c.GdkDisplay, + app_id: [:0]const u8, + config: *const Config, + ) !?App { + _ = config; + _ = app_id; + + // Check if we're actually on Wayland + if (c.g_type_check_instance_is_a( + @ptrCast(@alignCast(gdk_display)), + c.gdk_wayland_display_get_type(), + ) == 0) return null; + + const display: *wl.Display = @ptrCast(c.gdk_wayland_display_get_wl_display( + gdk_display, + ) orelse return error.NoWaylandDisplay); + + // Create our context for our callbacks so we have a stable pointer. + // Note: at the time of writing this comment, we don't really need + // a stable pointer, but it's too scary that we'd need one in the future + // and not have it and corrupt memory or something so let's just do it. + const context = try alloc.create(Context); + errdefer alloc.destroy(context); + context.* = .{}; + + // Get our display registry so we can get all the available interfaces + // and bind to what we need. + const registry = try display.getRegistry(); + registry.setListener(*Context, registryListener, context); + if (display.roundtrip() != .SUCCESS) return error.RoundtripFailed; + + if (context.kde_decoration_manager != null) { + // FIXME: Roundtrip again because we have to wait for the decoration + // manager to respond with the preferred default mode. Ew. + if (display.roundtrip() != .SUCCESS) return error.RoundtripFailed; + } + + return .{ + .display = display, + .context = context, + }; + } + + pub fn deinit(self: *App, alloc: Allocator) void { + alloc.destroy(self.context); + } + + pub fn eventMods( + _: *App, + _: ?*c.GdkDevice, + _: c.GdkModifierType, + ) ?input.Mods { + return null; + } + + fn registryListener( + registry: *wl.Registry, + event: wl.Registry.Event, + context: *Context, + ) void { + switch (event) { + // https://wayland.app/protocols/wayland#wl_registry:event:global + .global => |global| { + log.debug("wl_registry.global: interface={s}", .{global.interface}); + + if (registryBind( + org.KdeKwinBlurManager, + registry, + global, + )) |blur_manager| { + context.kde_blur_manager = blur_manager; + } else if (registryBind( + org.KdeKwinServerDecorationManager, + registry, + global, + )) |deco_manager| { + context.kde_decoration_manager = deco_manager; + deco_manager.setListener(*Context, decoManagerListener, context); + } + }, + + // We don't handle removal events + .global_remove => {}, + } + } + + /// Bind a Wayland interface to a global object. Returns non-null + /// if the binding was successful, otherwise null. + /// + /// The type T is the Wayland interface type that we're requesting. + /// This function will verify that the global object is the correct + /// interface and version before binding. + fn registryBind( + comptime T: type, + registry: *wl.Registry, + global: anytype, + ) ?*T { + if (std.mem.orderZ( + u8, + global.interface, + T.interface.name, + ) != .eq) return null; + + return registry.bind(global.name, T, T.generated_version) catch |err| { + log.warn("error binding interface {s} error={}", .{ + global.interface, + err, + }); + return null; + }; + } + + fn decoManagerListener( + _: *org.KdeKwinServerDecorationManager, + event: org.KdeKwinServerDecorationManager.Event, + context: *Context, + ) void { + switch (event) { + .default_mode => |mode| { + context.default_deco_mode = @enumFromInt(mode.mode); + }, + } + } +}; + +/// Per-window (wl_surface) state for the Wayland protocol. +pub const Window = struct { + config: DerivedConfig, + + /// The Wayland surface for this window. + surface: *wl.Surface, + + /// The context from the app where we can load our Wayland interfaces. + app_context: *App.Context, + + /// A token that, when present, indicates that the window is blurred. + blur_token: ?*org.KdeKwinBlur, + + /// Object that controls the decoration mode (client/server/auto) + /// of the window. + decoration: ?*org.KdeKwinServerDecoration, + + const DerivedConfig = struct { + blur: bool, + window_decoration: Config.WindowDecoration, + + pub fn init(config: *const Config) DerivedConfig { + return .{ + .blur = config.@"background-blur".enabled(), + .window_decoration = config.@"window-decoration", + }; + } + }; + + pub fn init( + alloc: Allocator, + app: *App, + gtk_window: *c.GtkWindow, + config: *const Config, + ) !Window { + _ = alloc; + + const gdk_surface = c.gtk_native_get_surface( + @ptrCast(gtk_window), + ) orelse return error.NotWaylandSurface; + + // This should never fail, because if we're being called at this point + // then we've already asserted that our app state is Wayland. + if (c.g_type_check_instance_is_a( + @ptrCast(@alignCast(gdk_surface)), + c.gdk_wayland_surface_get_type(), + ) == 0) return error.NotWaylandSurface; + + const wl_surface: *wl.Surface = @ptrCast(c.gdk_wayland_surface_get_wl_surface( + gdk_surface, + ) orelse return error.NoWaylandSurface); + + // Get our decoration object so we can control the + // CSD vs SSD status of this surface. + const deco: ?*org.KdeKwinServerDecoration = deco: { + const mgr = app.context.kde_decoration_manager orelse + break :deco null; + + const deco: *org.KdeKwinServerDecoration = mgr.create( + wl_surface, + ) catch |err| { + log.warn("could not create decoration object={}", .{err}); + break :deco null; + }; + + break :deco deco; + }; + + return .{ + .config = DerivedConfig.init(config), + .surface = wl_surface, + .app_context = app.context, + .blur_token = null, + .decoration = deco, + }; + } + + pub fn deinit(self: Window, alloc: Allocator) void { + _ = alloc; + if (self.blur_token) |blur| blur.release(); + if (self.decoration) |deco| deco.release(); + } + + pub fn updateConfigEvent( + self: *Window, + config: *const Config, + ) !void { + self.config = DerivedConfig.init(config); + } + + pub fn resizeEvent(_: *Window) !void {} + + pub fn syncAppearance(self: *Window) !void { + try self.syncBlur(); + try self.syncDecoration(); + } + + pub fn clientSideDecorationEnabled(self: Window) bool { + return switch (self.getDecorationMode()) { + .Client => true, + // If we support SSDs, then we should *not* enable CSDs if we prefer SSDs. + // However, if we do not support SSDs (e.g. GNOME) then we should enable + // CSDs even if the user prefers SSDs. + .Server => if (self.app_context.kde_decoration_manager) |_| false else true, + .None => false, + else => unreachable, + }; + } + + pub fn addSubprocessEnv(self: *Window, env: *std.process.EnvMap) !void { + _ = self; + _ = env; + } + + /// Update the blur state of the window. + fn syncBlur(self: *Window) !void { + const manager = self.app_context.kde_blur_manager orelse return; + const blur = self.config.blur; + + if (self.blur_token) |tok| { + // Only release token when transitioning from blurred -> not blurred + if (!blur) { + manager.unset(self.surface); + tok.release(); + self.blur_token = null; + } + } else { + // Only acquire token when transitioning from not blurred -> blurred + if (blur) { + const tok = try manager.create(self.surface); + tok.commit(); + self.blur_token = tok; + } + } + } + + fn syncDecoration(self: *Window) !void { + const deco = self.decoration orelse return; + + // The protocol requests uint instead of enum so we have + // to convert it. + deco.requestMode(@intCast(@intFromEnum(self.getDecorationMode()))); + } + + fn getDecorationMode(self: Window) org.KdeKwinServerDecorationManager.Mode { + return switch (self.config.window_decoration) { + .auto => self.app_context.default_deco_mode orelse .Client, + .client => .Client, + .server => .Server, + .none => .None, + }; + } +}; diff --git a/src/apprt/gtk/winproto/x11.zig b/src/apprt/gtk/winproto/x11.zig new file mode 100644 index 0000000000..6b60b0edf8 --- /dev/null +++ b/src/apprt/gtk/winproto/x11.zig @@ -0,0 +1,488 @@ +//! X11 window protocol implementation for the Ghostty GTK apprt. +const std = @import("std"); +const builtin = @import("builtin"); +const build_options = @import("build_options"); +const Allocator = std.mem.Allocator; +const c = @import("../c.zig").c; +const input = @import("../../../input.zig"); +const Config = @import("../../../config.zig").Config; +const adwaita = @import("../adwaita.zig"); + +const log = std.log.scoped(.gtk_x11); + +pub const App = struct { + display: *c.Display, + base_event_code: c_int, + atoms: Atoms, + + pub fn init( + alloc: Allocator, + gdk_display: *c.GdkDisplay, + app_id: [:0]const u8, + config: *const Config, + ) !?App { + _ = alloc; + + // If the display isn't X11, then we don't need to do anything. + if (c.g_type_check_instance_is_a( + @ptrCast(@alignCast(gdk_display)), + c.gdk_x11_display_get_type(), + ) == 0) return null; + + // Get our X11 display + const display: *c.Display = c.gdk_x11_display_get_xdisplay( + gdk_display, + ) orelse return error.NoX11Display; + + const x11_program_name: [:0]const u8 = if (config.@"x11-instance-name") |pn| + pn + else if (builtin.mode == .Debug) + "ghostty-debug" + else + "ghostty"; + + // Set the X11 window class property (WM_CLASS) if are are on an X11 + // display. + // + // Note that we also set the program name here using g_set_prgname. + // This is how the instance name field for WM_CLASS is derived when + // calling gdk_x11_display_set_program_class; there does not seem to be + // a way to set it directly. It does not look like this is being set by + // our other app initialization routines currently, but since we're + // currently deriving its value from x11-instance-name effectively, I + // feel like gating it behind an X11 check is better intent. + // + // This makes the property show up like so when using xprop: + // + // WM_CLASS(STRING) = "ghostty", "com.mitchellh.ghostty" + // + // Append "-debug" on both when using the debug build. + c.g_set_prgname(x11_program_name); + c.gdk_x11_display_set_program_class(gdk_display, app_id); + + // XKB + log.debug("Xkb.init: initializing Xkb", .{}); + log.debug("Xkb.init: running XkbQueryExtension", .{}); + var opcode: c_int = 0; + var base_event_code: c_int = 0; + var base_error_code: c_int = 0; + var major = c.XkbMajorVersion; + var minor = c.XkbMinorVersion; + if (c.XkbQueryExtension( + display, + &opcode, + &base_event_code, + &base_error_code, + &major, + &minor, + ) == 0) { + log.err("Fatal: error initializing Xkb extension: error executing XkbQueryExtension", .{}); + return error.XkbInitializationError; + } + + log.debug("Xkb.init: running XkbSelectEventDetails", .{}); + if (c.XkbSelectEventDetails( + display, + c.XkbUseCoreKbd, + c.XkbStateNotify, + c.XkbModifierStateMask, + c.XkbModifierStateMask, + ) == 0) { + log.err("Fatal: error initializing Xkb extension: error executing XkbSelectEventDetails", .{}); + return error.XkbInitializationError; + } + + return .{ + .display = display, + .base_event_code = base_event_code, + .atoms = Atoms.init(gdk_display), + }; + } + + pub fn deinit(self: *App, alloc: Allocator) void { + _ = self; + _ = alloc; + } + + /// Checks for an immediate pending XKB state update event, and returns the + /// keyboard state based on if it finds any. This is necessary as the + /// standard GTK X11 API (and X11 in general) does not include the current + /// key pressed in any modifier state snapshot for that event (e.g. if the + /// pressed key is a modifier, that is not necessarily reflected in the + /// modifiers). + /// + /// Returns null if there is no event. In this case, the caller should fall + /// back to the standard GDK modifier state (this likely means the key + /// event did not result in a modifier change). + pub fn eventMods( + self: App, + device: ?*c.GdkDevice, + gtk_mods: c.GdkModifierType, + ) ?input.Mods { + _ = device; + _ = gtk_mods; + + // Shoutout to Mozilla for figuring out a clean way to do this, this is + // paraphrased from Firefox/Gecko in widget/gtk/nsGtkKeyUtils.cpp. + if (c.XEventsQueued(self.display, c.QueuedAfterReading) == 0) return null; + + var nextEvent: c.XEvent = undefined; + _ = c.XPeekEvent(self.display, &nextEvent); + if (nextEvent.type != self.base_event_code) return null; + + const xkb_event: *c.XkbEvent = @ptrCast(&nextEvent); + if (xkb_event.any.xkb_type != c.XkbStateNotify) return null; + + const xkb_state_notify_event: *c.XkbStateNotifyEvent = @ptrCast(xkb_event); + // Check the state according to XKB masks. + const lookup_mods = xkb_state_notify_event.lookup_mods; + var mods: input.Mods = .{}; + + log.debug("X11: found extra XkbStateNotify event w/lookup_mods: {b}", .{lookup_mods}); + if (lookup_mods & c.ShiftMask != 0) mods.shift = true; + if (lookup_mods & c.ControlMask != 0) mods.ctrl = true; + if (lookup_mods & c.Mod1Mask != 0) mods.alt = true; + if (lookup_mods & c.Mod4Mask != 0) mods.super = true; + if (lookup_mods & c.LockMask != 0) mods.caps_lock = true; + + return mods; + } +}; + +pub const Window = struct { + app: *App, + alloc: Allocator, + config: DerivedConfig, + window: c.Window, + gtk_window: *c.GtkWindow, + + blur_region: Region = .{}, + + const DerivedConfig = struct { + blur: bool, + window_decoration: Config.WindowDecoration, + + pub fn init(config: *const Config) DerivedConfig { + return .{ + .blur = config.@"background-blur".enabled(), + .window_decoration = config.@"window-decoration", + }; + } + }; + + pub fn init( + alloc: Allocator, + app: *App, + gtk_window: *c.GtkWindow, + config: *const Config, + ) !Window { + const surface = c.gtk_native_get_surface( + @ptrCast(gtk_window), + ) orelse return error.NotX11Surface; + + // Check if we're actually on X11 + if (c.g_type_check_instance_is_a( + @ptrCast(@alignCast(surface)), + c.gdk_x11_surface_get_type(), + ) == 0) return error.NotX11Surface; + + return .{ + .app = app, + .alloc = alloc, + .config = DerivedConfig.init(config), + .window = c.gdk_x11_surface_get_xid(surface), + .gtk_window = gtk_window, + }; + } + + pub fn deinit(self: Window, alloc: Allocator) void { + _ = self; + _ = alloc; + } + + pub fn updateConfigEvent( + self: *Window, + config: *const Config, + ) !void { + self.config = DerivedConfig.init(config); + } + + pub fn resizeEvent(self: *Window) !void { + // The blur region must update with window resizes + self.blur_region.width = c.gtk_widget_get_width(@ptrCast(self.gtk_window)); + self.blur_region.height = c.gtk_widget_get_height(@ptrCast(self.gtk_window)); + try self.syncBlur(); + } + + pub fn syncAppearance(self: *Window) !void { + self.blur_region = blur: { + // NOTE(pluiedev): CSDs are a f--king mistake. + // Please, GNOME, stop this nonsense of making a window ~30% bigger + // internally than how they really are just for your shadows and + // rounded corners and all that fluff. Please. I beg of you. + var x: f64 = 0; + var y: f64 = 0; + c.gtk_native_get_surface_transform( + @ptrCast(self.gtk_window), + &x, + &y, + ); + + break :blur .{ + .x = @intFromFloat(x), + .y = @intFromFloat(y), + }; + }; + self.syncBlur() catch |err| { + log.err("failed to synchronize blur={}", .{err}); + }; + self.syncDecorations() catch |err| { + log.err("failed to synchronize decorations={}", .{err}); + }; + } + + pub fn clientSideDecorationEnabled(self: Window) bool { + return switch (self.config.window_decoration) { + .auto, .client => true, + .server, .none => false, + }; + } + + fn syncBlur(self: *Window) !void { + // FIXME: This doesn't currently factor in rounded corners on Adwaita, + // which means that the blur region will grow slightly outside of the + // window borders. Unfortunately, actually calculating the rounded + // region can be quite complex without having access to existing APIs + // (cf. https://github.com/cutefishos/fishui/blob/41d4ba194063a3c7fff4675619b57e6ac0504f06/src/platforms/linux/blurhelper/windowblur.cpp#L134) + // and I think it's not really noticeable enough to justify the effort. + // (Wayland also has this visual artifact anyway...) + + const blur = self.config.blur; + log.debug("set blur={}, window xid={}, region={}", .{ + blur, + self.window, + self.blur_region, + }); + + if (blur) { + try self.changeProperty( + Region, + self.app.atoms.kde_blur, + c.XA_CARDINAL, + ._32, + .{ .mode = .replace }, + &self.blur_region, + ); + } else { + try self.deleteProperty(self.app.atoms.kde_blur); + } + } + + fn syncDecorations(self: *Window) !void { + var hints: MotifWMHints = .{}; + + self.getWindowProperty( + MotifWMHints, + self.app.atoms.motif_wm_hints, + self.app.atoms.motif_wm_hints, + ._32, + .{}, + &hints, + ) catch |err| switch (err) { + // motif_wm_hints is already initialized, so this is fine + error.PropertyNotFound => {}, + + error.RequestFailed, + error.PropertyTypeMismatch, + error.PropertyFormatMismatch, + => return err, + }; + + hints.flags.decorations = true; + hints.decorations.all = switch (self.config.window_decoration) { + .server => true, + .auto, .client, .none => false, + }; + + try self.changeProperty( + MotifWMHints, + self.app.atoms.motif_wm_hints, + self.app.atoms.motif_wm_hints, + ._32, + .{ .mode = .replace }, + &hints, + ); + } + + pub fn addSubprocessEnv(self: *Window, env: *std.process.EnvMap) !void { + var buf: [64]u8 = undefined; + const window_id = try std.fmt.bufPrint(&buf, "{}", .{self.window}); + + try env.put("WINDOWID", window_id); + } + + fn getWindowProperty( + self: *Window, + comptime T: type, + name: c.Atom, + typ: c.Atom, + comptime format: PropertyFormat, + options: struct { + offset: c_long = 0, + length: c_long = std.math.maxInt(c_long), + delete: bool = false, + }, + result: *T, + ) GetWindowPropertyError!void { + // FIXME: Maybe we should switch to libxcb one day. + // Sounds like a much better idea than whatever this is + var actual_type_return: c.Atom = undefined; + var actual_format_return: c_int = undefined; + var nitems_return: c_ulong = undefined; + var bytes_after_return: c_ulong = undefined; + var prop_return: ?format.bufferType() = null; + + const code = c.XGetWindowProperty( + self.app.display, + self.window, + name, + options.offset, + options.length, + @intFromBool(options.delete), + typ, + &actual_type_return, + &actual_format_return, + &nitems_return, + &bytes_after_return, + &prop_return, + ); + if (code != c.Success) return error.RequestFailed; + + if (actual_type_return == c.None) return error.PropertyNotFound; + if (typ != actual_type_return) return error.PropertyTypeMismatch; + if (@intFromEnum(format) != actual_format_return) return error.PropertyFormatMismatch; + + const data_ptr: *T = @ptrCast(prop_return); + result.* = data_ptr.*; + _ = c.XFree(prop_return); + } + + fn changeProperty( + self: *Window, + comptime T: type, + name: c.Atom, + typ: c.Atom, + comptime format: PropertyFormat, + options: struct { + mode: PropertyChangeMode, + }, + value: *T, + ) X11Error!void { + const data: format.bufferType() = @ptrCast(value); + + const status = c.XChangeProperty( + self.app.display, + self.window, + name, + typ, + @intFromEnum(format), + @intFromEnum(options.mode), + data, + @divExact(@sizeOf(T), @sizeOf(format.elemType())), + ); + + // For some godforsaken reason Xlib alternates between + // error values (0 = success) and booleans (1 = success), and they look exactly + // the same in the signature (just `int`, since Xlib is written in C89)... + if (status == 0) return error.RequestFailed; + } + + fn deleteProperty(self: *Window, name: c.Atom) X11Error!void { + const status = c.XDeleteProperty(self.app.display, self.window, name); + if (status == 0) return error.RequestFailed; + } +}; + +const X11Error = error{ + RequestFailed, +}; + +const GetWindowPropertyError = X11Error || error{ + PropertyNotFound, + PropertyTypeMismatch, + PropertyFormatMismatch, +}; + +const Atoms = struct { + kde_blur: c.Atom, + motif_wm_hints: c.Atom, + + fn init(display: *c.GdkDisplay) Atoms { + return .{ + .kde_blur = c.gdk_x11_get_xatom_by_name_for_display( + display, + "_KDE_NET_WM_BLUR_BEHIND_REGION", + ), + .motif_wm_hints = c.gdk_x11_get_xatom_by_name_for_display( + display, + "_MOTIF_WM_HINTS", + ), + }; + } +}; + +const PropertyChangeMode = enum(c_int) { + replace = c.PropModeReplace, + prepend = c.PropModePrepend, + append = c.PropModeAppend, +}; + +const PropertyFormat = enum(c_int) { + _8 = 8, + _16 = 16, + _32 = 32, + + fn elemType(comptime self: PropertyFormat) type { + return switch (self) { + ._8 => c_char, + ._16 => c_int, + ._32 => c_long, + }; + } + + fn bufferType(comptime self: PropertyFormat) type { + // The buffer type has to be a multi-pointer to bytes + // *aligned to the element type* (very important, + // otherwise you'll read garbage!) + // + // I know this is really ugly. X11 is ugly. I consider it apropos. + return [*]align(@alignOf(self.elemType())) u8; + } +}; + +const Region = extern struct { + x: c_long = 0, + y: c_long = 0, + width: c_long = 0, + height: c_long = 0, +}; + +// See Xm/MwmUtil.h, packaged with the Motif Window Manager +const MotifWMHints = extern struct { + flags: packed struct(c_ulong) { + _pad: u1 = 0, + decorations: bool = false, + + // We don't really care about the other flags + _rest: std.meta.Int(.unsigned, @bitSizeOf(c_ulong) - 2) = 0, + } = .{}, + functions: c_ulong = 0, + decorations: packed struct(c_ulong) { + all: bool = false, + + // We don't really care about the other flags + _rest: std.meta.Int(.unsigned, @bitSizeOf(c_ulong) - 1) = 0, + } = .{}, + input_mode: c_long = 0, + status: c_ulong = 0, +}; diff --git a/src/apprt/gtk/x11.zig b/src/apprt/gtk/x11.zig deleted file mode 100644 index 21ff87b34f..0000000000 --- a/src/apprt/gtk/x11.zig +++ /dev/null @@ -1,119 +0,0 @@ -/// Utility functions for X11 handling. -const std = @import("std"); -const build_options = @import("build_options"); -const c = @import("c.zig").c; -const input = @import("../../input.zig"); - -const log = std.log.scoped(.gtk_x11); - -/// Returns true if the passed in display is an X11 display. -pub fn is_display(display: ?*c.GdkDisplay) bool { - if (comptime !build_options.x11) return false; - return c.g_type_check_instance_is_a( - @ptrCast(@alignCast(display orelse return false)), - c.gdk_x11_display_get_type(), - ) != 0; -} - -/// Returns true if the app is running on X11 -pub fn is_current_display_server() bool { - if (comptime !build_options.x11) return false; - const display = c.gdk_display_get_default(); - return is_display(display); -} - -pub const Xkb = struct { - base_event_code: c_int, - - /// Initialize an Xkb struct for the given GDK display. If the display isn't - /// backed by X then this will return null. - pub fn init(display_: ?*c.GdkDisplay) !?Xkb { - if (comptime !build_options.x11) return null; - - // Display should never be null but we just treat that as a non-X11 - // display so that the caller can just ignore it and not unwrap it. - const display = display_ orelse return null; - - // If the display isn't X11, then we don't need to do anything. - if (!is_display(display)) return null; - - log.debug("Xkb.init: initializing Xkb", .{}); - const xdisplay = c.gdk_x11_display_get_xdisplay(display); - var result: Xkb = .{ - .base_event_code = 0, - }; - - log.debug("Xkb.init: running XkbQueryExtension", .{}); - var opcode: c_int = 0; - var base_error_code: c_int = 0; - var major = c.XkbMajorVersion; - var minor = c.XkbMinorVersion; - if (c.XkbQueryExtension( - xdisplay, - &opcode, - &result.base_event_code, - &base_error_code, - &major, - &minor, - ) == 0) { - log.err("Fatal: error initializing Xkb extension: error executing XkbQueryExtension", .{}); - return error.XkbInitializationError; - } - - log.debug("Xkb.init: running XkbSelectEventDetails", .{}); - if (c.XkbSelectEventDetails( - xdisplay, - c.XkbUseCoreKbd, - c.XkbStateNotify, - c.XkbModifierStateMask, - c.XkbModifierStateMask, - ) == 0) { - log.err("Fatal: error initializing Xkb extension: error executing XkbSelectEventDetails", .{}); - return error.XkbInitializationError; - } - - return result; - } - - /// Checks for an immediate pending XKB state update event, and returns the - /// keyboard state based on if it finds any. This is necessary as the - /// standard GTK X11 API (and X11 in general) does not include the current - /// key pressed in any modifier state snapshot for that event (e.g. if the - /// pressed key is a modifier, that is not necessarily reflected in the - /// modifiers). - /// - /// Returns null if there is no event. In this case, the caller should fall - /// back to the standard GDK modifier state (this likely means the key - /// event did not result in a modifier change). - pub fn modifier_state_from_notify(self: Xkb, display_: ?*c.GdkDisplay) ?input.Mods { - if (comptime !build_options.x11) return null; - - const display = display_ orelse return null; - - // Shoutout to Mozilla for figuring out a clean way to do this, this is - // paraphrased from Firefox/Gecko in widget/gtk/nsGtkKeyUtils.cpp. - const xdisplay = c.gdk_x11_display_get_xdisplay(display); - if (c.XEventsQueued(xdisplay, c.QueuedAfterReading) == 0) return null; - - var nextEvent: c.XEvent = undefined; - _ = c.XPeekEvent(xdisplay, &nextEvent); - if (nextEvent.type != self.base_event_code) return null; - - const xkb_event: *c.XkbEvent = @ptrCast(&nextEvent); - if (xkb_event.any.xkb_type != c.XkbStateNotify) return null; - - const xkb_state_notify_event: *c.XkbStateNotifyEvent = @ptrCast(xkb_event); - // Check the state according to XKB masks. - const lookup_mods = xkb_state_notify_event.lookup_mods; - var mods: input.Mods = .{}; - - log.debug("X11: found extra XkbStateNotify event w/lookup_mods: {b}", .{lookup_mods}); - if (lookup_mods & c.ShiftMask != 0) mods.shift = true; - if (lookup_mods & c.ControlMask != 0) mods.ctrl = true; - if (lookup_mods & c.Mod1Mask != 0) mods.alt = true; - if (lookup_mods & c.Mod4Mask != 0) mods.super = true; - if (lookup_mods & c.LockMask != 0) mods.caps_lock = true; - - return mods; - } -}; diff --git a/src/build/Config.zig b/src/build/Config.zig index 71dffce4ab..f7bf96d368 100644 --- a/src/build/Config.zig +++ b/src/build/Config.zig @@ -19,7 +19,7 @@ const GitVersion = @import("GitVersion.zig"); /// TODO: When Zig 0.14 is released, derive this from build.zig.zon directly. /// Until then this MUST match build.zig.zon and should always be the /// _next_ version to release. -const app_version: std.SemanticVersion = .{ .major = 1, .minor = 0, .patch = 2 }; +const app_version: std.SemanticVersion = .{ .major = 1, .minor = 1, .patch = 3 }; /// Standard build configuration options. optimize: std.builtin.OptimizeMode, @@ -32,7 +32,6 @@ renderer: renderer.Impl = .opengl, font_backend: font.Backend = .freetype, /// Feature flags -adwaita: bool = false, x11: bool = false, wayland: bool = false, sentry: bool = true, @@ -55,6 +54,8 @@ emit_helpgen: bool = false, emit_docs: bool = false, emit_webdata: bool = false, emit_xcframework: bool = false, +emit_terminfo: bool = false, +emit_termcap: bool = false, /// Environmental properties env: std.process.EnvMap, @@ -130,12 +131,6 @@ pub fn init(b: *std.Build) !Config { //--------------------------------------------------------------- // Feature Flags - config.adwaita = b.option( - bool, - "gtk-adwaita", - "Enables the use of Adwaita when using the GTK rendering backend.", - ) orelse true; - config.flatpak = b.option( bool, "flatpak", @@ -306,6 +301,27 @@ pub fn init(b: *std.Build) !Config { break :emit_docs path != null; }; + config.emit_terminfo = b.option( + bool, + "emit-terminfo", + "Install Ghostty terminfo source file", + ) orelse switch (target.result.os.tag) { + .windows => true, + else => switch (optimize) { + .Debug => true, + .ReleaseSafe, .ReleaseFast, .ReleaseSmall => false, + }, + }; + + config.emit_termcap = b.option( + bool, + "emit-termcap", + "Install Ghostty termcap file", + ) orelse switch (optimize) { + .Debug => true, + .ReleaseSafe, .ReleaseFast, .ReleaseSmall => false, + }; + config.emit_webdata = b.option( bool, "emit-webdata", @@ -374,7 +390,6 @@ pub fn addOptions(self: *const Config, step: *std.Build.Step.Options) !void { // We need to break these down individual because addOption doesn't // support all types. step.addOption(bool, "flatpak", self.flatpak); - step.addOption(bool, "adwaita", self.adwaita); step.addOption(bool, "x11", self.x11); step.addOption(bool, "wayland", self.wayland); step.addOption(bool, "sentry", self.sentry); @@ -419,7 +434,6 @@ pub fn fromOptions() Config { .version = options.app_version, .flatpak = options.flatpak, - .adwaita = options.adwaita, .app_runtime = std.meta.stringToEnum(apprt.Runtime, @tagName(options.app_runtime)).?, .font_backend = std.meta.stringToEnum(font.Backend, @tagName(options.font_backend)).?, .renderer = std.meta.stringToEnum(renderer.Impl, @tagName(options.renderer)).?, @@ -486,6 +500,7 @@ pub const ExeEntrypoint = enum { mdgen_ghostty_5, webgen_config, webgen_actions, + webgen_commands, bench_parser, bench_stream, bench_codepoint_width, diff --git a/src/build/GhosttyFrameData.zig b/src/build/GhosttyFrameData.zig new file mode 100644 index 0000000000..7c22115b38 --- /dev/null +++ b/src/build/GhosttyFrameData.zig @@ -0,0 +1,37 @@ +//! GhosttyFrameData generates a compressed file and zig module which contains (and exposes) the +//! Ghostty animation frames for use in `ghostty +boo` +const GhosttyFrameData = @This(); + +const std = @import("std"); +const Config = @import("Config.zig"); +const SharedDeps = @import("SharedDeps.zig"); + +/// The exe. +exe: *std.Build.Step.Compile, + +/// The output path for the compressed framedata zig file +output: std.Build.LazyPath, + +pub fn init(b: *std.Build) !GhosttyFrameData { + const exe = b.addExecutable(.{ + .name = "framegen", + .root_source_file = b.path("src/build/framegen/main.zig"), + .target = b.host, + }); + + const run = b.addRunArtifact(exe); + + _ = run.addOutputFileArg("framedata.compressed"); + return .{ + .exe = exe, + .output = run.captureStdOut(), + }; +} + +/// Add the "framedata" import. +pub fn addImport(self: *const GhosttyFrameData, step: *std.Build.Step.Compile) void { + self.output.addStepDependencies(&step.step); + step.root_module.addAnonymousImport("framedata", .{ + .root_source_file = self.output, + }); +} diff --git a/src/build/GhosttyResources.zig b/src/build/GhosttyResources.zig index 9c5f7f8094..912308e46c 100644 --- a/src/build/GhosttyResources.zig +++ b/src/build/GhosttyResources.zig @@ -23,9 +23,12 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyResources { // Write it var wf = b.addWriteFiles(); - const src_source = wf.add("share/terminfo/ghostty.terminfo", str.items); - const src_install = b.addInstallFile(src_source, "share/terminfo/ghostty.terminfo"); - try steps.append(&src_install.step); + const source = wf.add("ghostty.terminfo", str.items); + + if (cfg.emit_terminfo) { + const source_install = b.addInstallFile(source, "share/terminfo/ghostty.terminfo"); + try steps.append(&source_install.step); + } // Windows doesn't have the binaries below. if (cfg.target.result.os.tag == .windows) break :terminfo; @@ -33,10 +36,10 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyResources { // Convert to termcap source format if thats helpful to people and // install it. The resulting value here is the termcap source in case // that is used for other commands. - { + if (cfg.emit_termcap) { const run_step = RunStep.create(b, "infotocap"); run_step.addArg("infotocap"); - run_step.addFileArg(src_source); + run_step.addFileArg(source); const out_source = run_step.captureStdOut(); _ = run_step.captureStdErr(); // so we don't see stderr @@ -49,23 +52,29 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyResources { const run_step = RunStep.create(b, "tic"); run_step.addArgs(&.{ "tic", "-x", "-o" }); const path = run_step.addOutputFileArg("terminfo"); - run_step.addFileArg(src_source); + run_step.addFileArg(source); _ = run_step.captureStdErr(); // so we don't see stderr - // Depend on the terminfo source install step so that Zig build - // creates the "share" directory for us. - run_step.step.dependOn(&src_install.step); - - { - // Use cp -R instead of Step.InstallDir because we need to preserve - // symlinks in the terminfo database. Zig's InstallDir step doesn't - // handle symlinks correctly yet. - const copy_step = RunStep.create(b, "copy terminfo db"); - copy_step.addArgs(&.{ "cp", "-R" }); - copy_step.addFileArg(path); - copy_step.addArg(b.fmt("{s}/share", .{b.install_path})); - try steps.append(©_step.step); + // Ensure that `share/terminfo` is a directory, otherwise the `cp + // -R` will create a file named `share/terminfo` + const mkdir_step = RunStep.create(b, "make share/terminfo directory"); + switch (cfg.target.result.os.tag) { + // windows mkdir shouldn't need "-p" + .windows => mkdir_step.addArgs(&.{"mkdir"}), + else => mkdir_step.addArgs(&.{ "mkdir", "-p" }), } + mkdir_step.addArg(b.fmt("{s}/share/terminfo", .{b.install_path})); + try steps.append(&mkdir_step.step); + + // Use cp -R instead of Step.InstallDir because we need to preserve + // symlinks in the terminfo database. Zig's InstallDir step doesn't + // handle symlinks correctly yet. + const copy_step = RunStep.create(b, "copy terminfo db"); + copy_step.addArgs(&.{ "cp", "-R" }); + copy_step.addFileArg(path); + copy_step.addArg(b.fmt("{s}/share", .{b.install_path})); + copy_step.step.dependOn(&mkdir_step.step); + try steps.append(©_step.step); } } @@ -200,6 +209,13 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyResources { "share/kio/servicemenus/com.mitchellh.ghostty.desktop", ).step); + // Right click menu action for Nautilus. Note that this _must_ be named + // `ghostty.py`. Using the full app id causes problems (see #5468). + try steps.append(&b.addInstallFile( + b.path("dist/linux/ghostty_nautilus.py"), + "share/nautilus-python/extensions/ghostty.py", + ).step); + // Various icons that our application can use, including the icon // that will be used for the desktop. try steps.append(&b.addInstallFile( diff --git a/src/build/GhosttyWebdata.zig b/src/build/GhosttyWebdata.zig index 6e0acaf173..860feb705a 100644 --- a/src/build/GhosttyWebdata.zig +++ b/src/build/GhosttyWebdata.zig @@ -73,6 +73,35 @@ pub fn init( ).step); } + { + const webgen_commands = b.addExecutable(.{ + .name = "webgen_commands", + .root_source_file = b.path("src/main.zig"), + .target = b.host, + }); + deps.help_strings.addImport(webgen_commands); + + { + const buildconfig = config: { + var copy = deps.config.*; + copy.exe_entrypoint = .webgen_commands; + break :config copy; + }; + + const options = b.addOptions(); + try buildconfig.addOptions(options); + webgen_commands.root_module.addOptions("build_options", options); + } + + const webgen_commands_step = b.addRunArtifact(webgen_commands); + const webgen_commands_out = webgen_commands_step.captureStdOut(); + + try steps.append(&b.addInstallFile( + webgen_commands_out, + "share/ghostty/webdata/commands.mdx", + ).step); + } + return .{ .steps = steps.items }; } diff --git a/src/build/SharedDeps.zig b/src/build/SharedDeps.zig index 077da96a62..a90fc330ab 100644 --- a/src/build/SharedDeps.zig +++ b/src/build/SharedDeps.zig @@ -6,6 +6,7 @@ const Config = @import("Config.zig"); const HelpStrings = @import("HelpStrings.zig"); const MetallibStep = @import("MetallibStep.zig"); const UnicodeTables = @import("UnicodeTables.zig"); +const GhosttyFrameData = @import("GhosttyFrameData.zig"); config: *const Config, @@ -13,6 +14,7 @@ options: *std.Build.Step.Options, help_strings: HelpStrings, metallib: ?*MetallibStep, unicode_tables: UnicodeTables, +framedata: GhosttyFrameData, /// Used to keep track of a list of file sources. pub const LazyPathList = std.ArrayList(std.Build.LazyPath); @@ -22,6 +24,7 @@ pub fn init(b: *std.Build, cfg: *const Config) !SharedDeps { .config = cfg, .help_strings = try HelpStrings.init(b, cfg), .unicode_tables = try UnicodeTables.init(b), + .framedata = try GhosttyFrameData.init(b), // Setup by retarget .options = undefined, @@ -430,20 +433,39 @@ pub fn add( }, .gtk => { + const gobject = b.dependency("gobject", .{ + .target = target, + .optimize = optimize, + }); + const gobject_imports = .{ + .{ "gobject", "gobject2" }, + .{ "gio", "gio2" }, + .{ "glib", "glib2" }, + .{ "gtk", "gtk4" }, + .{ "gdk", "gdk4" }, + }; + inline for (gobject_imports) |import| { + const name, const module = import; + step.root_module.addImport(name, gobject.module(module)); + } + step.linkSystemLibrary2("gtk4", dynamic_link_opts); - if (self.config.adwaita) step.linkSystemLibrary2("adwaita-1", dynamic_link_opts); - if (self.config.x11) step.linkSystemLibrary2("X11", dynamic_link_opts); + step.linkSystemLibrary2("libadwaita-1", dynamic_link_opts); + step.root_module.addImport("adw", gobject.module("adw1")); + + if (self.config.x11) { + step.linkSystemLibrary2("X11", dynamic_link_opts); + step.root_module.addImport("gdk_x11", gobject.module("gdkx114")); + } if (self.config.wayland) { - const scanner = Scanner.create(b, .{ + const scanner = Scanner.create(b.dependency("zig_wayland", .{}), .{ // We shouldn't be using getPath but we need to for now // https://codeberg.org/ifreund/zig-wayland/issues/66 - .wayland_xml_path = b.dependency("wayland", .{}) - .path("protocol/wayland.xml") - .getPath(b), - .wayland_protocols_path = b.dependency("wayland_protocols", .{}) - .path("") - .getPath(b), + .wayland_xml = b.dependency("wayland", .{}) + .path("protocol/wayland.xml"), + .wayland_protocols = b.dependency("wayland_protocols", .{}) + .path(""), }); const wayland = b.createModule(.{ .root_source_file = scanner.result }); @@ -452,20 +474,64 @@ pub fn add( .target = target, .optimize = optimize, }); + + // FIXME: replace with `zxdg_decoration_v1` once GTK merges https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/6398 scanner.addCustomProtocol(plasma_wayland_protocols.path("src/protocols/blur.xml")); + scanner.addCustomProtocol(plasma_wayland_protocols.path("src/protocols/server-decoration.xml")); scanner.generate("wl_compositor", 1); scanner.generate("org_kde_kwin_blur_manager", 1); + scanner.generate("org_kde_kwin_server_decoration_manager", 1); step.root_module.addImport("wayland", wayland); + step.root_module.addImport("gdk_wayland", gobject.module("gdkwayland4")); step.linkSystemLibrary2("wayland-client", dynamic_link_opts); } { const gresource = @import("../apprt/gtk/gresource.zig"); - const wf = b.addWriteFiles(); - const gresource_xml = wf.add("gresource.xml", gresource.gresource_xml); + const gresource_xml = gresource_xml: { + const generate_gresource_xml = b.addExecutable(.{ + .name = "generate_gresource_xml", + .root_source_file = b.path("src/apprt/gtk/gresource.zig"), + .target = b.host, + }); + + const generate = b.addRunArtifact(generate_gresource_xml); + + for (gresource.blueprint_files) |blueprint_file| { + const blueprint_compiler = b.addSystemCommand(&.{ + "blueprint-compiler", + "compile", + "--output", + }); + const ui_file = blueprint_compiler.addOutputFileArg(b.fmt("{s}.ui", .{blueprint_file})); + blueprint_compiler.addFileArg(b.path(b.fmt("src/apprt/gtk/ui/{s}.blp", .{blueprint_file}))); + generate.addFileArg(ui_file); + } + + break :gresource_xml generate.captureStdOut(); + }; + + { + const gtk_builder_check = b.addExecutable(.{ + .name = "gtk_builder_check", + .root_source_file = b.path("src/apprt/gtk/builder_check.zig"), + .target = b.host, + }); + gtk_builder_check.root_module.addOptions("build_options", self.options); + gtk_builder_check.root_module.addImport("gtk", gobject.module("gtk4")); + gtk_builder_check.root_module.addImport("adw", gobject.module("adw1")); + + for (gresource.dependencies) |pathname| { + const extension = std.fs.path.extension(pathname); + if (!std.mem.eql(u8, extension, ".ui")) continue; + const check = b.addRunArtifact(gtk_builder_check); + check.addFileArg(b.path(pathname)); + step.step.dependOn(&check.step); + } + } const generate_resources_c = b.addSystemCommand(&.{ "glib-compile-resources", @@ -497,6 +563,7 @@ pub fn add( self.help_strings.addImport(step); self.unicode_tables.addImport(step); + self.framedata.addImport(step); return static_libs; } diff --git a/src/build/UnicodeTables.zig b/src/build/UnicodeTables.zig index 0159de442c..7a4b0a5a2e 100644 --- a/src/build/UnicodeTables.zig +++ b/src/build/UnicodeTables.zig @@ -15,7 +15,6 @@ pub fn init(b: *std.Build) !UnicodeTables { .root_source_file = b.path("src/unicode/props.zig"), .target = b.host, }); - exe.linkLibC(); const ziglyph_dep = b.dependency("ziglyph", .{ .target = b.host, diff --git a/src/build/docker/debian/Dockerfile b/src/build/docker/debian/Dockerfile new file mode 100644 index 0000000000..61e9e75c14 --- /dev/null +++ b/src/build/docker/debian/Dockerfile @@ -0,0 +1,49 @@ +ARG DISTRO_VERSION="12" +FROM docker.io/library/debian:${DISTRO_VERSION} + +# Install Dependencies +RUN DEBIAN_FRONTEND="noninteractive" apt-get -qq update && \ + apt-get -qq -y --no-install-recommends install \ + # Build Tools + build-essential \ + libbz2-dev \ + libonig-dev \ + lintian \ + lsb-release \ + pandoc \ + wget \ + # Ghostty Dependencies + libadwaita-1-dev \ + libgtk-4-dev && \ + # Clean up for better caching + rm -rf /var/lib/apt/lists/* + +# work around the fact that Debian 12 doesn't ship a pkg-config file for bzip2 +RUN . /etc/os-release; if [ $VERSION_ID -le 12 ]; then ln -s libbz2.so /usr/lib/$(gcc -dumpmachine)/libbzip2.so; fi + +# Install zig +# https://ziglang.org/download/ +ARG ZIG_VERSION="0.13.0" +RUN wget -q "https://ziglang.org/download/$ZIG_VERSION/zig-linux-$(uname -m)-$ZIG_VERSION.tar.xz" && \ + tar -xf "zig-linux-$(uname -m)-$ZIG_VERSION.tar.xz" -C /opt && \ + rm zig-linux-* && \ + ln -s "/opt/zig-linux-$(uname -m)-$ZIG_VERSION/zig" /usr/local/bin/zig + +WORKDIR /src + +COPY ./dist/linux /src/dist/linux +COPY ./images /src/images +COPY ./include /src/include +COPY ./pkg /src/pkg +COPY ./nix /src/nix +COPY ./vendor /src/vendor +COPY ./build.zig /src/build.zig +COPY ./build.zig.zon /src/build.zig.zon +COPY ./build.zig.zon.txt /src/build.zig.zon.txt + +RUN ZIG_GLOBAL_CACHE_DIR=/zig/global-cache ./nix/build-support/fetch-zig-cache.sh + +COPY ./src /src/src + +RUN zig build -Doptimize=Debug -Dcpu=baseline -Dapp-runtime=gtk --system /zig/global-cache/p + diff --git a/src/build/framegen/frames/frame_001.txt b/src/build/framegen/frames/frame_001.txt new file mode 100644 index 0000000000..416e87931d --- /dev/null +++ b/src/build/framegen/frames/frame_001.txt @@ -0,0 +1,41 @@ + + + +++==*%%%%%%%%%%%%*==+++ + ++****++ ++****++ + ++**++ ++**++ + xx**+= o+*%$@@@@@@$%*+o =+**xx + xx**oo ·=$@@@@@@@$$$$$$$$@@@@@@@$=· oo**xx + xx** x$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$x **xx + ox** ·$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$· **xo + ==+~ ~@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@~ ~+== + x+++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ +++x + == ·@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@· == + ox++ ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ++xo + +++~ @$$$$$@@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + == $$$$$@@%%%%%%$$$$$$$@@@@@$$$@@@@@@@@@@@@@@@@@@@@$$$$$$ == + == @$$$$* $$$$% =$$$$$@ == + == ·$$$$@ x@$@ @$$$$$· == + == ·@$$$$% ·$$$$% *$$$$$@· == + == ·@$$$$@@$%%$$$$$$@@@@@@@@$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@· == + == ·@$$$$$$$@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==x· $@@$$$$$$$$$@@@@@@@@@@$$$$$$$$@@@@@@@@@@$$$$$$$$$@@$ ·+== + ++++ =@@@@@@@@@* x$@@@@@@@@$x *@@@@@@@@@= ++++ + xx==++ ++==oo + ++===+ ++%%+o o+%%++ +===++ + ++=====%+=++++*=*========***++++***========*=*++++=+%=====++ + xx++==******====++ ++==********==++ ++====******==++xx + ++++ ++++ ++++ + diff --git a/src/build/framegen/frames/frame_002.txt b/src/build/framegen/frames/frame_002.txt new file mode 100644 index 0000000000..e74134e3ed --- /dev/null +++ b/src/build/framegen/frames/frame_002.txt @@ -0,0 +1,41 @@ + + ++++++++++++ + ++==*%%%**=++++++=**%%%*==++ + ++**=* *=**++ + x+**+= =+**+x + ++== o=%@@@@@@@@@@@@@@@@%=o ==++ + ===+ +$@@@@@$$$$$$$$$$$$$$$$@@@@@$+ +=== + ++=+ =@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@= +=++ + ++== $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ ==++ + ** $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ ** + +++o +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ o+++ + == %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% == + xx++ %@$$$$@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xx + ==+· x@$$$@@%=*%$$@@@@@@@@@@@$$$$$@@@@@@@@@@@@@@@@@@$$$$$@x ·+== + == @$$$$% ~$@$$$@= x@$$$$$@ == + == $$$$@* %$$@· @$$$$$ == + == ·@$$$@* $$$$+ $$$$$@· == + == ·@$$$$$~ ~ox+=%@@$$$@@*==============*$@$$$$$@· == + == ·@$$$$$@@@@@@@@@@@@@@@@@$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == $@$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$@$ == + ==+o +@@@$$$$$$$@@@$***%@@@@$$$$$$@@@@%***$@@@$$$$$$$@@@+ o+== + ++== ·=$@@@@@$*~ +%@@@@@@%+ ~*$@@@@@$=· ==++ + ===+ +=== + ++====x+ *=**== ==**=* +x====++ + ++=====**%******=========**%****%**=========******%**=====++ + ++++========++xx ++++========++++ xx++========++++ + + diff --git a/src/build/framegen/frames/frame_003.txt b/src/build/framegen/frames/frame_003.txt new file mode 100644 index 0000000000..6da88ffbf3 --- /dev/null +++ b/src/build/framegen/frames/frame_003.txt @@ -0,0 +1,41 @@ + + ++====****====++ + ==***%==xo ox==%***== + ===*++ ++*=== + ++**x+ ·oxx++xxo· +x**++ + ===+ o=$@@@@@@@@@@@@@@@@@@$=o +=== + **+x o%@@@@$$$$$$$$$$$$$$$$$$$$@@@@%o x+** + ==+o ~$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$~ o+== + ++++ +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ++++ + ox== o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ==xo + ==+~ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ~+== + ox== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + +++x @$$$$@+ ·+*$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + ==+ *@$$$@ o=%@@@@$$$$@@@@@@@@@@@@@@@@@@@@$$$$@* +== + == @$$$$$o %$$$$$ *$$$$$@ == + == $$$$$$@@=· @$$@ @$$$$$ == + == ·@$$$$$x %$$$$% =$$$$$@· == + == ·@$$$$@ ·x*$@@@$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@· == + == ·@$$$$$@x o=%@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == %@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@% == + ==+x ~$@@@@$$$$@@@@%+xx=$@@@@$$$$@@@@$=xx+*@@@@$$$$@@@@$~ x+== + ++== x*$@@@$*x ·=%$@@$%=· x*$@@@$*x ==++ + ==== =+== + x+====+= x+*===+= ======+x =+====xx + ++====*%%%%***====++====*%*%%*%*====++=====**%%%%*====++ + xx++++====++++ ++++====++++ ++++====++++xx + + diff --git a/src/build/framegen/frames/frame_004.txt b/src/build/framegen/frames/frame_004.txt new file mode 100644 index 0000000000..5ad3a81122 --- /dev/null +++ b/src/build/framegen/frames/frame_004.txt @@ -0,0 +1,41 @@ + + ++==************==++ + ++***%=*x~ ~x*=%***++ + ++**+= ==**++ + ==== ~x=*%%%%%%*=x~ ==== + xx**++ o*$@@@@@@@@@@@@@@@@@@@@$*o ++**xo + oo**o~ *@@@@$$$$$$$$$$$$$$$$$$$$$$@@@@* ~o**oo + **o· =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= ·o** + ==+x %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% x+== + xx== *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ==xx + ==x· @@$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ·x== + xx== @$$$@% x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==xx + +++o @$$$@* ~=@@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$@ o+++ + == %$$$$$% +$@@$$$$@@%**************%@@$$$$$% == + == @$$$$$@@%+ $$$$$+ ~@$$$$@ == + == $$$$$$$@@@@= *@$$@· @$$$$$ == + == ~@$$$$$@= %@$$$$@+ o$$$$$$@~ == + == ·@$$$$@= ~*@@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$@@@$@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == +@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@+ == + ==++ *@@@@@@@@@@@%x ~=@@@@@@@@@@@@=~ x%@@@@@@@@@@@* ++== + x+==x· o=***=x ~+****+~ x=***=o ·x==xx + ====xo xx x+ ox==== + ======+x =+=*====++ ++====*=+= x+====== + ++==***%********++++==************==++++********%***==++ + ++++++++++ +x++++++++xx ++++++++++ + + diff --git a/src/build/framegen/frames/frame_005.txt b/src/build/framegen/frames/frame_005.txt new file mode 100644 index 0000000000..e93b295d5d --- /dev/null +++ b/src/build/framegen/frames/frame_005.txt @@ -0,0 +1,41 @@ + + +==*****%%%%*****==+ + ++***%=+ +=%***++ + ++**+= =+**++ + **== ·x=*%$$$$$$%*=x· ==** + xx**xx x%@@@@@@@@@$$$$@@@@@@@@@%x xx**xx + xx** ~%@@@@$$$$$$$$$$$$$$$$$$$$$$@@@@%~ **xx + **o· %@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@% ·o** + ==+o $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ o+== + ++++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% +++x + == @@$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + xx== @$$$$o o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==xx + +++o @$$$@% o*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$@ o+++ + == $$$$$$$o o*@$$$$$@@=+++++++++++++++$@$$$$$$ == + == @$$$$$$@@$=· $$$$@x @$$$$@ == + == $$$$$$$@@@$= $$$$@~ @$$$$$ == + == ·@$$$$$@* x@@$$$$@*~ ~=@$$$$$@· == + == ·@$$$$@% x%@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@%$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ~@@$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@~ == + ++++ +@@@@@@@@@@@*~ x$@@@@@@@@@@$x ·*@@@@@@@@@@@+ ++++ + xx==+~ x+=+x· o+==+o ·x+=+x ~+==xo + ++==+x == == x+==++ + ++====++ ox=+==**==+= =+=***==+*+x ++====++ + x+==**********==++++++************++++++==**********==++ + ++++++xx xx++++xx ++++++++ + + diff --git a/src/build/framegen/frames/frame_006.txt b/src/build/framegen/frames/frame_006.txt new file mode 100644 index 0000000000..f9ad605202 --- /dev/null +++ b/src/build/framegen/frames/frame_006.txt @@ -0,0 +1,41 @@ + + +++==*%%*%%%%%%*%%*===++ + ++**=*++ ++*=**++ + ++**=+ +=**++ + xx**+= ~+*%$$@@@@$$%*+~ =+**+x + xx**ox +$@@@@@@@@$$$$$$@@@@@@@@$+ xo**xx + x+** x$@@@@$$$$$$$$$$$$$$$$$$$$$$@@@@$x **+x + oo** ·$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$· **oo + ==+~ ·@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@· ~+== + ++++ %@$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++++ + == @@$$@$%$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ox++ ~@$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ++xo + +++~ @$$$@% +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$@ ~+++ + == $$$$$$@% =@$$$$$@%xooooooooooooooo*@$$$$$$ == + == @$$$$$$@@@$+ $$$$@o @$$$$@ == + == ·$$$$$$$@@@%+ $$$$@o @$$$$$· == + == ·@$$$$$$* =@@$$$$@$xoooooooooooooox*@$$$$$@· == + == ·@$$$$@% +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$ x$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@%$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==x· @@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@ ·x== + ++++ ~%@@@@@@@@@$x ·*@@@@@@@@@@*· x$@@@@@@@@@%~ ++++ + xx==+x ooo· ~oo~ ·oo~ x+==xo + x+==++ +%% %%+o ++==+x + ++=*==+*xx ++*=**==**+*++ ++*+**==**=*++o~ xx*+==**++ + ++==********=+xx ++==********==++ xx++********==++ + + + diff --git a/src/build/framegen/frames/frame_007.txt b/src/build/framegen/frames/frame_007.txt new file mode 100644 index 0000000000..7825600824 --- /dev/null +++ b/src/build/framegen/frames/frame_007.txt @@ -0,0 +1,41 @@ + + ++++=**%**%%%%**%**=++++ + ==****++ ++*=**== + ++**=+ +=**++ + x+**+= ~+*%$$$@@$$$%*+~ =+**+x + ++**ox +$@@@@@@@@$$$$$$@@@@@@@@$+ xo**++ + x+** x$@@@@$$$$$$$$$$$$$$$$$$$$$$@@@@$x **+x + xx** $@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$ **xx + ==+~ ·@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@· ~+== + x+++ %@$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% +++x + == @@$$@$%%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + xx++ ~@$$$$ o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ +++x + +++~ @$$$@$ +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$@ ~+++ + == $$$$$$@%· =@$$$$$@%xooooooooooooooo*@$$$$$$ == + == @$$$$$$@@@$=· $$$$@o @$$$$@ == + == ·$$$$$$$@@@$+ $$$$@o @$$$$$· == + == ·@$$$$$$% =@$$$$$@$+xxxxxxxxxxxxxxx%@$$$$$@· == + == ·@$$$$@% +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$ x%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@%$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==x· @@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@ ·+== + ++++ ·%@@@@@@@@@%o =$@@@@@@@@$= ~%@@@@@@@@@%· ++++ + ==+x ··· ·· ·· x+== + ++=*++ ++%% %*++ ++*=++ + ++==**=*++ooxx+=*=**==**=*=+xxxx+=%=**==**=*=+xxxx++*=**==+x + +++=******==++xx ++++********++++ xx++==******++++ + + + diff --git a/src/build/framegen/frames/frame_008.txt b/src/build/framegen/frames/frame_008.txt new file mode 100644 index 0000000000..3e4706fdd5 --- /dev/null +++ b/src/build/framegen/frames/frame_008.txt @@ -0,0 +1,41 @@ + + ++++==**%%%%%%%%**==++++ + +=***%== ==%***=+ + ++**+= =+**++ + x+**=+ ·o+*%$$$$$$%*+o· +=**+x + x+**xx x%@@@@@@@@@$$$$@@@@@@@@@%x xx**+x + x+**~ ~%@@@@$$$$$$$$$$$$$$$$$$$$$$@@@@%~ ~**+x + ox**o· %@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@% ·o**xo + ==+o $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ o+== + x+++ %@$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% +++x + ==x @@$$@@%$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ x== + x++= @$$$$ x%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ =+xx + +++o @$$$@% +$@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$@ o+++ + == $$$$$$@% ·*@$$$$$@$+xxxxxxxxxxxxxxx%@$$$$$% == + == @$$$$$$@@@%+ $$$$@o @$$$$@ == + == $$$$$$$@@@$*· $$$$@~ @$$$$$ == + == ·@$$$$$@%· +@$$$$$@%x~~~~~~~~~~~~~~o*@$$$$$@· == + == ·@$$$$$$ x%@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$ o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@$*%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==x· $@@$$$$$$$$$@@@@@@@@@@$$$$$$$$@@@@@@@@@@$$$$$$$$$@@$ ·+== + +++= =@@@@@@@@@*· x%@@@@@@@@%x *@@@@@@@@@* =+++ + ==++ +=== + xx=*=+ ++%%+x x+%%++ +=**xx + ++**=*=+++++==*=**++**=%==++++==%=**++**=***+++++=*=**++ + xx++==**==++++ ++========++ ++++======++xx + + + diff --git a/src/build/framegen/frames/frame_009.txt b/src/build/framegen/frames/frame_009.txt new file mode 100644 index 0000000000..00e5a6170f --- /dev/null +++ b/src/build/framegen/frames/frame_009.txt @@ -0,0 +1,41 @@ + + ++++==***%%%%%%***==++++ + +=****=*o~ ~o*=%***=+ + ++**=* *=**++ + x+**== ~x=*%%%%%%*=x~ ==**+x + ++**++ o*$@@@@@@@@@@@@@@@@@@@@$*o ++**++ + x+**o~ *@@@@$$$$$$$$$$$$$$$$$$$$$$@@@@* ~o**+x + xx**o· =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= ·o**xx + ==+o %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% o+== + x+++ *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* +++x + ==x· @@$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ·x== + x+== @$$$$~ ·*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==+x + +++o @$$$@% ~*@@@@$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$@ o+++ + == %$$$$$$+ o%@$$$$$@@=++++++++++++++=$@$$$$$% == + == @$$$$$$@@$*~ $$$$@x @$$$$@ == + == $$$$$$$$@@@%~ $$$$@~ @$$$$$ == + == ~@$$$$$@$~ x@$$$$$@*~ ·+@$$$$$@~ == + == ·@$$$$$$ o*@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$ ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@$**@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+· %@@$$$$$$$$$@@@@$$@@@@$$$$$$$$@@@@$$@@@@$$$$$$$$$@@$ ·+== + x+== =$@@@@@@@@* o%@@@@@@@@%o *$@@@@@@@$= ==+x + ==++ ++== + ===+ +=%%++ ++%%== +=== + ++**=%==++++****==++**=***++++***=**++==****++++==%***++ + ++======++++ +++======++x ++++=====+++ + + + diff --git a/src/build/framegen/frames/frame_010.txt b/src/build/framegen/frames/frame_010.txt new file mode 100644 index 0000000000..bdfaa69d90 --- /dev/null +++ b/src/build/framegen/frames/frame_010.txt @@ -0,0 +1,41 @@ + + xx++==************==++xx + ++***%*%++ ++%*%***++ + ++**=*+x x+*=**++ + xx====x ·ox+====+xo· x====xx + x+**++ x%@@@@@@@@@@@@@@@@@@@@%x ++**++ + xx**+o +$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$+ o+**xx + oo**+~ x$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$x ~+**oo + ==++ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ++== + ++== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==++ + ==+· $@$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ·+== + ox== @@$$@* ·=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xx + +++x @$$$$$ ·=@@@@$$$$$$$$$$@@@@@@@@@@@@@@@$$$$$$@ x+++ + == *$$$$$$ ~*@@$$$$$@@$%%%%%%%%%%%%%%$@@$$$$$* == + == @$$$$$$@@*· $$$$$= o$$$$$@ == + == $$$$$$$$@@@@x $$$$@ @$$$$$ == + == ~@$$$$$@@+ %$$$$$@x ~$$$$$$@~ == + == ·@$$$$$$ +$@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@· == + == ·@$$$$$$ x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@*x+%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$· == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+· %@@@$$$$$$$$@@@@$$@@@@$$$$$$$$@@@@$$$@@@$$$$$$$$@@@% ~+== + xx== +$@@@@@@@$= ~%@@@@@@@@%~ =$@@@@@@@$+ ==xx + ===+ +=== + ==== ==%%++ ++%%== ==== + ++**=***++==%***==++***%**++++**%***++==***%==++**%=**++ + ++++==++++ x+++====+++x ++++==+++x + + + diff --git a/src/build/framegen/frames/frame_011.txt b/src/build/framegen/frames/frame_011.txt new file mode 100644 index 0000000000..e3170d5333 --- /dev/null +++ b/src/build/framegen/frames/frame_011.txt @@ -0,0 +1,41 @@ + + ++++==********==++++ + ++*****%**+x x+**%*****++ + ++**=*++ ++*=**++ + ====++ ~ooxxoo~ ++==== + xx**++ ~+%@@@@@@@@@@@@@@@@@@%+~ ++**xx + ++**++ ~*@@@@@$$$$$$$$$$$$$$$$$$@@@@@*~ ++**+x + ==+x ·%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@%· x+== + ==++ x@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x ++== + xx== ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ ==xx + ==+~ %@$$$@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ~+== + xx== @@$$@$o +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xx + +++x $$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ x+++ + ==+ =@$$$$$ +$@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$@= +== + == @$$$$$$@%o x$$$$$% =$$$$$@ == + == $$$$$$$$@@@@= $@$$@ @$$$$$ == + == ~@$$$$$@@*~ +$$$$$% *$$$$$@~ == + == ·@$$$$$$ ·*@@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@· == + == ·@$$$$$$ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$@@x·~=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$· == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$@@ == + ==+· %@@@$$$$$$$$@@@$$$@@@@$$$$$$$$@@@@$$$@@@$$$$$$$$@@@% ~+== + xx== +$@@@@@@@$= ~*@@@@@@@@*~ =$@@@@@@@$+ ==xx + ===+ +=== + ====o ==%%++ ++%%=+ ==== + ++**=***=+==%***++++==***%====%***==++++***%====***=**++ + xx++++++++ ++====++ ++++++++xx + + + diff --git a/src/build/framegen/frames/frame_012.txt b/src/build/framegen/frames/frame_012.txt new file mode 100644 index 0000000000..90a65ed526 --- /dev/null +++ b/src/build/framegen/frames/frame_012.txt @@ -0,0 +1,41 @@ + + ++++++========++++++ + ++==***%*%**==++++==**%*%***==++ + ++==**=*xo ox*=**==++ + ++**+= =+**++ + xx==*= ·+*$@@@@@@@@@@@@@@$*+· =*==xx + xx===+ o%@@@@@$$$$$$$$$$$$$$$$@@@@@%o +===xx + ==++ x$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$x +=== + ++=+ %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% +=++ + xx== %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ==xx + ==+x x@@$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x x+== + xx== %@$$$@%**$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ==xx + ++++ %@$$$$ =@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++++ + ==+· o@$$$$$ ·=@@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$@o ·+== + == @$$$$$@$~ ~$@$$$$@+ x@$$$$$@ == + == $$$$$$$$@@@%o $$$$@· @$$$$$ == + == ·@$$$$$$@@%x $$$$$+ ~$$$$$@· == + == ·@$$$$$$~ +$@$$$$$@@%***************@@$$$$$@· == + == ·@$$$$@% +$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@· == + == ·@$$$$$$= x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$· == + == @@$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$@@ == + ==+~ %@@@$$$$$$$$@@@$$$@@@@$$$$$$$$@@@@$$$@@@@$$$$$$$@@@% ~+== + x+== +%@@@@@@@$+ ~*@@@@@@@@*~ +$@@@@@@@%+ ==+x + ++=+ +=++ + ++==o ==%%++ ++%%== o==++ + ++***%**====%***++++==***%====%***==++++***%====**%***++ + ++++++++ ++++++++ x+++++++ + + + diff --git a/src/build/framegen/frames/frame_013.txt b/src/build/framegen/frames/frame_013.txt new file mode 100644 index 0000000000..b2b6a980c5 --- /dev/null +++ b/src/build/framegen/frames/frame_013.txt @@ -0,0 +1,41 @@ + + ++++++++====++++++++ + ++++*******%********%*******++++ + +x==**=*=+ +=*=**==xx + ++**== ==**++ + ====++ ·x*%@@@@@@@@@@@@%*x· ++==== + ===+ +$@@@@@@$$$$$$$$$$$$@@@@@@$+ +=== + ===+ ·%@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@%· +=== + ++== +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ==++ + xx==o· =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ·o==xo + ==++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++== + ox== +@$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + ++++ =@$$$$~ ·=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ++++ + ==+· ·@$$$$$ ~*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$@· ·+== + == @$$$$$$+ ~*@$$$$$@$+xxxxxxxxxxxxxx+%@$$$$$@ == + == @$$$$$$@@@*o $$$$@x @$$$$@ == + == ·@$$$$$$@@@$= $$$$@~ @$$$$@· == + == ·@$$$$$@* +@$$$$$@%o~~~~~~~~~~~~~~o*@$$$$$@· == + == ·@$$$$$$ +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$ x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$· == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$@@ == + ==+· %@@@$$$$$$$$@@@$$$@@@@$$$$$$$$@@@@$$$@@@$$$$$$$$@@@% ~+== + xx== +$@@@@@@@$= ~%@@@@@@@@%~ =$@@@@@@@$+ ==xx + ++=+ +=++ + ++==o ==%%++ ++%%== o==++ + ++***%**====%***++++==***%====%***==++++***%====**%***++ + ++++++++ ++++++++ ++++++++ + + + diff --git a/src/build/framegen/frames/frame_014.txt b/src/build/framegen/frames/frame_014.txt new file mode 100644 index 0000000000..96194979cd --- /dev/null +++ b/src/build/framegen/frames/frame_014.txt @@ -0,0 +1,41 @@ + + xx++++++++++++xx + ++==*****%*%%%%%%*%*****==++ + ++*****%=+ +=%*****++ + ++====+* *+====++ + ++==+= ·x+*%%$$$$%%*+x· =+==++ + ====xx x%@@@@@@@@@$$$$@@@@@@@@@%x xx==== + ++==o~ ~%@@@@$$$$$$$$$$$$$$$$$$$$$$@@@@%~ ~~==+= + ++==o· *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ·o==++ + xx==+o $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ o+==xx + ++++ %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ++++ + ox==x @@$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ x==xo + ++++ @$$$@$x ·+$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++++ + ==+o @$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+== + == $$$$$$$ =$@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$$ == + == @$$$$$$@%o x$$$$$% *$$$$$@ == + == $$$$$$$$@@@@+ $$$$@ @$$$$$ == + == ·@$$$$$$@%o x$$$$$% *$$$$$@· == + == ·@$$$$$$ =$@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@· == + == ·@$$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$@$x ·+$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$· == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+· %@@$$$$$$$$$@@@@$$@@@@$$$$$$$$@@@@$$@@@@$$$$$$$$$@@% ·+== + xx== +$@@@@@@@$= o%@@@@@@@@%o =$@@@@@@@$+ ==xx + ++=+ +=++ + ++== ==%%++ ++%%== ==++ + x+=*=***++==%***++xx==*%**====**%*==xx++***%==++***=*=+x + ++++++++ ++++++++ ++++++++ + + + diff --git a/src/build/framegen/frames/frame_015.txt b/src/build/framegen/frames/frame_015.txt new file mode 100644 index 0000000000..33cf5ec652 --- /dev/null +++ b/src/build/framegen/frames/frame_015.txt @@ -0,0 +1,41 @@ + + ++++++++++++ + xx++==****************==++xx + ++==**=***++o o++***=**==++ + x+=====*+x x+*=====+x + ++====ox ·ox++==++xo· xo====++ + ++==++ x*$@@@@@@@@@@@@@@@@@@$*x ++==++ + ++==+x x$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$x x+==++ + ++==+o x@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@x o+==++ + ===+ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= +=== + ++== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==++ + ==+· $@$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ·+== + ++== @@$$$@$**@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==++ + +++x @$$$$$ ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + == *$$$$@$ ~*@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$* == + == @$$$$$@$~ o$$$$$$@= x@$$$$$@ == + == $$$$$$$$@@@%~ $$$$@· @$$$$$ == + == ~@$$$$$$@@$x $$$$$+ ·@$$$$@~ == + == ·@$$$$$$o x$@$$$$$@@*==============*$@$$$$$@· == + == ·@$$$$@% x%@@@@$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@· == + == ·@$$$$$$+ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+· $@@$$$$$$$$$@@@@$@@@@@$$$$$$$$@@@@@$@@@@$$$$$$$$$@@$ ·+== + xx== =$@@@@@@@@* o%@@@@@@@@%o *@@@@@@@@$= ==xo + ++=+ +=++ + ++== ==%%++ ++%%== ==++ + x+**=%==++++%***=+x+==*%**++++**%*==+++=***%++++=*%=**+x + ++++++++ ++++++++ ++++++++ + + + diff --git a/src/build/framegen/frames/frame_016.txt b/src/build/framegen/frames/frame_016.txt new file mode 100644 index 0000000000..50eea78320 --- /dev/null +++ b/src/build/framegen/frames/frame_016.txt @@ -0,0 +1,41 @@ + + xxxx + ++++==************==++++ + xx++==****%%=*++++++++*=%*****==++xx + ++=====*+~ ~+*=====++ + ++====+= =+====++ + ++===+ ·+*$@@@@@@@@@@@@@@$*+· +===++ + ++===+ o%@@@@@$$$$$$$$$$$$$$$$@@@@@%o +===++ + xx===+ x@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@x +===xx + ==++ %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ++== + ++== %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ==++ + ==+x x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x x+== + xx== %@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ==++ + ++++ %@$$$$* ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++++ + ==x· o@$$$$$ ~*@@@@$$$$$$$$$$@@@@@@@@@@@@@@$$$$$$$@o ·x== + == @$$$$$$ ~*@@$$$$$@@$%%%%%%%%%%%%%%$@@$$$$$@ == + == $$$$$$$@@= $$$$$* x$$$$$$ == + == ·@$$$$$$$@@@@x $$$$@ @$$$$@· == + == ·@$$$$$@@+ *$$$$$$o ·$$$$$$@· == + == ·@$$$$$$ x$@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@· == + == ·@$$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$@@*x+%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==x· @@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@ ·+== + xx== *@@@@@@@@@%~ +$@@@@@@@@$+ ~%@@@@@@@@@* ==xx + ===+ +=== + ===+ ++%% %%++ +=== + ++***%++++++**%*=+++===*==++x+==*===++++*%**++x+++%***++ + x+++====++ ++==++++ ++====+++x + + + diff --git a/src/build/framegen/frames/frame_017.txt b/src/build/framegen/frames/frame_017.txt new file mode 100644 index 0000000000..e05674a723 --- /dev/null +++ b/src/build/framegen/frames/frame_017.txt @@ -0,0 +1,41 @@ + + + ++++++============++++++ + ++==**==*%*%********%*%*==**==++ + ++=====*== ==*=====++ + ++======+x x+======+x + ++====++ o=%$@@@@@@@@@@$%=o ++====++ + ++==== x%@@@@@@$$$$$$$$$$$$@@@@@@%x ====+x + xx===+ *@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@* +===xx + ==== +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ==== + x+==x· +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ·x==++ + ==++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++== + xx== x@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ==xo + ++++ +@$$$@@=ox%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ++++ + ==+~ @$$$$$ o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+== + == $$$$$$$ x%@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$$ == + == @$$$$$@@+ *$$$$$$~ %$$$$$@ == + == ·@$$$$$$$@@@@+ $@$$@ @$$$$@· == + == ·@$$$$$$@$= ~$$$$$* x$$$$$@· == + == ·@$$$$$$ o%@@$$$$$@@@$$$$$$$$$$$$$$$@@$$$$$@· == + == ·@$$$$$$ o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$@% ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==x· @@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@ ·+== + ++++ ~%@@@@@@@@@%o =@@@@@@@@@@= o%@@@@@@@@@%~ ++xx + ==+x ~~~ ·~~· ~~· x+== + ==++ x+%% %%++ +=== + ++**=%++ooox==*===++**=%++xoox++%=**++===*==xooo++%=**++ + ++++==++++ ++++====++++ ++++==++++ + + + diff --git a/src/build/framegen/frames/frame_018.txt b/src/build/framegen/frames/frame_018.txt new file mode 100644 index 0000000000..5e92ec0e03 --- /dev/null +++ b/src/build/framegen/frames/frame_018.txt @@ -0,0 +1,41 @@ + + + ++++++++++++++++++++ + xx++==****=**%%%%%%**=****==+++x + x+++====*%==o~ ~o==%*====+++x + ++====+= =+====++ + ====++ o+=*%%%%%%*=+o ++==== + ====xx o*$@@@@@@@@@@@@@@@@@@@@$*o xx==== + ====o· ·*@@@@$$$$$$$$$$$$$$$$$$$$$$@@@@*· ·o==== + ++==o· *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ·o==++ + xx==+o $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ o+==xx + ==++ *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ++== + xx==x· @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ·x==xo + ++++ @$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++++ + ==+o @$$$$$~ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+== + == %$$$$@% ·*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$% == + == @$$$$$$= ~*@$$$$$@$+xxxxxxxxxxxxxxx%@$$$$$@ == + == $$$$$$$@@@*o $$$$@o @$$$$$ == + == ·@$$$$$$@@@$+ $$$$@~ @$$$$@· == + == ·@$$$$$@* +@$$$$$@%o~~~~~~~~~~~~~~o*@$$$$$@· == + == ·@$$$$@% +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$ +%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ·@@$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@· x== + ++++ x$@@@@@@@@@@= o%@@@@@@@@@@%o =@@@@@@@@@@$x ++++ + ==+o ox++o ~x++x~ o++xo o+== + **+x ** ** x+** + ++**+=o~ ++*+**==**+*xo ox*+**++**+*++ ~o=+**++ + ++==****++++ ++==****==++ ++++=***==++ + + + diff --git a/src/build/framegen/frames/frame_019.txt b/src/build/framegen/frames/frame_019.txt new file mode 100644 index 0000000000..f528dc8766 --- /dev/null +++ b/src/build/framegen/frames/frame_019.txt @@ -0,0 +1,41 @@ + + + ++++++++++++++++ + ++++==****************==++++ + ++===****%++~~ ~~++%****===++ + ++=====*+x x+*=====++ + ++====xx ·ox+====+xo· xx====++ + ====++ x%$@@@@@@@@@@@@@@@@@@$%x ++==== + ++==+o x$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$x o+==++ + ++==+o x$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$x o+==++ + ox==++ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ++==xo + +++= x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x =+++ + ox==+· $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ·+==xo + ++== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==++ + ==+x @$$$$@= +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+== + == *$$$$$$ =$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$* == + == @$$$$$$· =@@$$$$$@@%**************%@@$$$$$@ == + == $$$$$$$@@*o $$$$$= ~@$$$$$ == + == ~@$$$$$$$@@@@o $$$$@ @$$$$@~ == + == ·@$$$$$@$o ·%$$$$$@+ o$$$$$$@· == + == ·@$$$$$$ =$@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@%==$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == o@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@o == + ++++ =@@@@@@@@@@@%o +$@@@@@@@@@@$+ o%@@@@@@@@@@@= ++++ + **x~ ~+===+o x====x o+===+~ ~+** + **xx ++ ++ xx** + ==**++ ~o*+**==**+= =+**==**+*o~ ++==== + +==*****=+++ ++=******=++ +++==****==+ + + + diff --git a/src/build/framegen/frames/frame_020.txt b/src/build/framegen/frames/frame_020.txt new file mode 100644 index 0000000000..02720e639c --- /dev/null +++ b/src/build/framegen/frames/frame_020.txt @@ -0,0 +1,41 @@ + + + ++++++++ + ++++==************==++++ + ++++****=***=+xo~~~~ox+=%**=****++++ + xx++===*+= =+*===++xx + ++====++ ·~~~~· ++====++ + ++===+ x*$@@@@@@@@@@@@@@@@$*x +===++ + ++==++ =@@@@@$$$$$$$$$$$$$$$$$$@@@@@= ++==++ + x+===+ *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ++==+x + ==++ ~$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$~ ++== + ++== @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==++ + ==+o *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* o+== + ++== $@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ==++ + +++x $@$$$@$o x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ x+++ + ==x· +@$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ·x== + == @$$$$$$ +$@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$@@%x x$$$$$% =$$$$$$ == + == ~@$$$$$$$@@@@= $@$$@ @$$$$@· == + == ·@$$$$$@@*~ +$$$$$% *$$$$$@· == + == ·@$$$$$$ ·=@@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@· == + == ·@$$$$$$ ·=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$@@x·~=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@* == + +++x $@@@@$$$@@@@@*x~o+%@@@@@$$@@@@@%+o~o=$@@@@$$@@@@@$ x+++ + **o ~=%$$$%=o +*$$$$*+ o=%$$$%=~ o** + xx**o~ ~~**xx + xx**+* ++=***=*+x x+*=****++ *+**xx + ++==*%%%%%**== x+++*%%%%%%*+++x ++**%%%%%*==++ + + + diff --git a/src/build/framegen/frames/frame_021.txt b/src/build/framegen/frames/frame_021.txt new file mode 100644 index 0000000000..e7305fbc69 --- /dev/null +++ b/src/build/framegen/frames/frame_021.txt @@ -0,0 +1,41 @@ + + + + ++++====********====++++ + ++==**=**%**++++++++**%**=**==++ + ++=*=*=* ~ *=*===++ + ++====+= =+====+x + ++===+ ~+%$@@@@@@@@@@@@@@$%+· +===+x + ++==++ x$@@@@@$$$$$$$$$$$$$$$$@@@@@%x +===+x + xx==++ +@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@+ +===xo + ==++ $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ++== + ++== $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ==++ + ==+o +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x x+== + xx== %@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xx + ++++ %@$$$@@+~o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++++ + ==x· +@$$$$% ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ·+== + == @$$$$$$ ~*@@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$@@*· =$$$$$$ %$$$$$$ == + == ~@$$$$$$$@@@@+ $$$$@ @$$$$@ == + == ~@$$$$$@@%x o$$$$$* =$$$$$@ == + == ~@$$$$$$ +$@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ == + == ~@$$$$@% x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ~@$$$$$@$~ x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ~@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == %@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@% == + +++o o@@@@$$$$$@@@@%*+=*$@@@$$$$$$@@@@*=+=%@@@@$$$$$@@@@o o+++ + o ** +%@@@@@%= o*$@@@@$*o +%@@@@@%+ ** + ++** **++ + x+**+= x****== ==**=*xo =+**++ + ++**%%%%%%%*==++ ++==%%%%%%%*==++ x+==*%%%%%%%**=+ + + + diff --git a/src/build/framegen/frames/frame_022.txt b/src/build/framegen/frames/frame_022.txt new file mode 100644 index 0000000000..943c443257 --- /dev/null +++ b/src/build/framegen/frames/frame_022.txt @@ -0,0 +1,41 @@ + + + + ++++++========++++++ + ++==*****%*%********%%%*****+++x + ++==***%++ ++*=**==+x + ==**+= *+**++ + ox====oo o=%$@@@@@@@@@@@@$%+~ xx==== + xx===+ ~*@@@@@@$$$$$$$$$$$$$@@@@@@$+ ==== + ===+ x$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ++== + ==++ %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ==++ + ++== %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ·x==xx + ==+o +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++++ + xx== %@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ==xo + +++x $@$$$@@*+=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ++xx + ==x =@$$$$% +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + == @$$$$@* +$@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$% == + == o$$$$$$@$o ·$$$$$$$o o$$$$$$@ == + == +@$$$$$$$@@@@o @$$$@ @$$$$$ == + == =@$$$$$$@@=· ~@$$$@+ +@$$$$@ == + == +@$$$$$$ ~*@@$$$$$@@$%%%%%%%%%%%%%%$@@$$$$$@ == + == +@$$$$@* ~*@@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@ == + == +@$$$$$@= ·*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == +@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == o$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + == @@$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$$$@@$$$$$$$$$$$$$$$@@ == + ==+· %@@@$$$$$$$$@@@$$$@@@@$$$$$$$$@@@@$$$@@@$$$$$$$$@@@% ~+== + xx== +$@@@@@@@$= ~*@@@@@@@@*~ +$@@@@@@@$+ ==xx + ++=+ +=++ + ++**oo ==%%=+ ++%%== oo=*++ + x+==*%=*===**=**++++++*%**====**%*++++++******==*=%*==+x + ++++++ x+++++++ ++++++ + + diff --git a/src/build/framegen/frames/frame_023.txt b/src/build/framegen/frames/frame_023.txt new file mode 100644 index 0000000000..673dd9482b --- /dev/null +++ b/src/build/framegen/frames/frame_023.txt @@ -0,0 +1,41 @@ + + + + x+++++++==++++++++ + ++++*******%%%**%%%*%*%****=++ + xx==**=*++ ==****++ + ++**+= ++*=**++ + xx====oo ~+*$$@@@@@@@@@@$%=o +x*=++ + x+===+ ·=@@@@@@@$$$$$$$$$$$$@@@@@@%o ~==== + ===+ o$@@@$$$$$$$$$$$$$$$$$$$$$$$$$@@@@= ==++ + ==++ %@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$~ ==++ + ++== %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ ~+== + ==+o +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++++ + xx== $@$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ·x== + +++x @@$$$@@%=*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==xx + == %@$$$$* ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + == @$$$$@= o*@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$+ ·+== + ox== =$$$$$$@%· o@$$$$$@o +@$$$$$$ == + ox++ %@$$$$$$$@@@%· @$$$@ @$$$$@ == + ox++ %@$$$$$@@@*~ ~@$$$@~ +@$$$$@ == + ox++ %@$$$$$$ ·=@@$$$$$@@***************%@@$$$$$@ == + ox++ %@$$$$@+ =$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@ == + ox++ %@$$$$$$x =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox++ %@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox++ *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == +$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==x @@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@$ ·+== + ++++ ·*@@@@@@@@@%o +@@@@@@@@@@= ~%@@@@@@@@@* ==+x + ===x ·~· ~~ ·~· ++== + ==++ x+%% %%+x ++== + ++**=*++xoox==*===++===%==xoox==%=**+++=*%=*+xox++*=**++ + +++++==+++ +++==+++ ++====++++ + + diff --git a/src/build/framegen/frames/frame_024.txt b/src/build/framegen/frames/frame_024.txt new file mode 100644 index 0000000000..2339b7811a --- /dev/null +++ b/src/build/framegen/frames/frame_024.txt @@ -0,0 +1,41 @@ + + + + x+++++++++++++ + ++==***%%%%%%%%%%*%*****+++x + ++***%== o+*=****++ + ++**=* ++====+x + ====xx x=%$@@@@@@@@@$%=x ++**++ + ===+ =$@@@@@@@$$$$$$$$$@@@@@@@$+ xx*=++ + ==++ o$@@@$$$$$$$$$$$$$$$$$$$$$$$$$@@@%~ ~o==++ + ===+ %@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ~x==xo + ++== $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ++== + ==+~ *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==+x + x+++ @@$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% o+== + ==+~ @$$$$@@%%$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ==xo + == @$$$$$= +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++++ + ox== x@$$$$@~ +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$@ o+++ + xx++ $$$$$$$@= =@$$$$$@x ~*@$$$$$= ·+== + ++++ @$$$$$$$@@@@+ @$$$@ x@$$$@% x== + ++++ @$$$$$$@@@*x o@$$$$ =@$$$@% == + ++++ @$$$$$$% =@@$$$$$@$===============*@@$$$$@% == + xx++ @$$$$$@~ +$@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@$ == + xx++ @$$$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + xx++ @$$$$$$@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox++ *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== *$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + == o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == =@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@ ·+== + +++x *@@@@@@@@@@@%o +$@@@@@@@@@@@=· ~*@@@@@@@@@@@= +++x + **x· ~+=**+o x=**=x· ~+=*=+~ ~+== + oo**xx ++ ++ o+** + ==**++ =+**==**+= ++**==**==x· ++==== + +==*%%**===+ ++=***%%*=++ ++===*%%*==+ + + diff --git a/src/build/framegen/frames/frame_025.txt b/src/build/framegen/frames/frame_025.txt new file mode 100644 index 0000000000..9667d9db3e --- /dev/null +++ b/src/build/framegen/frames/frame_025.txt @@ -0,0 +1,41 @@ + + + + xx++++++++++ + ++==*****%%$%%%%%%%*****++xx + ++*****= x+%***==++ + ++**=* ==**== + ====xx o=%$$@@@@@@@$%*+o ==**xx + ===+ +$@@@@@@@$$$$$$$$$@@@@@@@%x x+**+x + ==++ o$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@* o+**xx + ===+ %@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ o+== + ++== $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= +=++ + ==x· %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@· ==xx + ++++ @@$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= x+++ + ==+· @$$$$@@$%$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ** + == @$$$$$= =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x =+xx + xx++ *$$$$$@· =@@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$@ x+++ + ++++ @$$$$$$@x ·*@$$$$$@+· o%@$$$$$~ ~+++ + xx+x $$$$$$$$@@@$x ·@$$$$ +@$$$@= ·+++ + xx+x @$$$$$$@@@%x o@$$$$ *@$$$@= ·+== + xxxx @$$$$$$$ +$@$$$$$@%+++++++++++++++=@@$$$$@= ·+== + +++x @$$$$$@· +$@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@= ·+== + +++x @$$$$$$% x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ·+== + ++++ @$$$$$$@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ·+== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% x== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% x== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox++ %$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + ox== =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == $@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@x x== + +++o ~$@@@@$$$@@@@@*x~o+*@@@@@$$$@@@@%+o~o=$@@@@$$$@@@@% ++++ + ** ~+%$$$$*o x*$$$$%+· ~=%$$$%+~ ·o** + x+**~~ ~o**xx + x+**==o~ ++*=**=*++ oo==****++ =+**xx + ++==*%%%%%%*==xx xx==*%%%%%%*==++ +=*%%%%%%*==++ + + diff --git a/src/build/framegen/frames/frame_026.txt b/src/build/framegen/frames/frame_026.txt new file mode 100644 index 0000000000..5558114882 --- /dev/null +++ b/src/build/framegen/frames/frame_026.txt @@ -0,0 +1,41 @@ + + + + ++++++++ + ++++****%%%%%%%%%%%***==++ + ++**=*=* ++*=**++ + ++**=*x *=**++ + ++**xx o+*%$$@@@@@$$%=x· +=== + ===+ +$@@@@@@@$$$$$$$$@@@@@@@@*o ++**xx + ===+ x$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@= o+** + ===+ %@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$o x=== + ++== ·@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ++++ + =*o· %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ** + ++++ @@$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ++++ + ==x· ~@$$$$@@$%$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + ox== @$$$$$* ·=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==xx + xx++ $$$$$$@· ·=@@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$ ++++ + +++x @$$$$$$$x ~*@$$$$$@+~~~~~~~~~~~~~~~o$@$$$$@ o+++ + +++o $$$$$$$$@@@%o ·@$$$$ +@$$$@~ ~+++ + +++o @$$$$$$$@@$+ ~@$$$$ =@$$$@o ~+++ + +++o @$$$$$$$· +$@$$$$$@*+xxxxxxxxxxxxxx=@@$$$$@o ~+++ + +++o @$$$$$@~ x%@@@@$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@o ~+++ + +++o @$$$$$$% o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ·+++ + +++x @$$$$$$@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ·+++ + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ·+++ + xx+x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ·+== + ++++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ·+== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% x== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + ox++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + ox++ %$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == @@$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$$@@@$$$$$$$$$$$$$$@* == + ==x· *@@@$$$$$$$@@@@%**%@@@@$$$$$$$@@@$%**$@@@$$$$$$$@@@x x+++ + xx== =$@@@@@@$+ o%@@@@@@@*· =$@@@@@$=· *= + ++== ==++ + ++**++ ==%%== ++%%** +x**++ + ++***%%%%%%***++ ++***%*%%%%***=+ x+==*%*%%%%***=+ + xxxx xxxx xx + diff --git a/src/build/framegen/frames/frame_027.txt b/src/build/framegen/frames/frame_027.txt new file mode 100644 index 0000000000..d0b2080888 --- /dev/null +++ b/src/build/framegen/frames/frame_027.txt @@ -0,0 +1,41 @@ + + + + +++x + xx++=***%%%%%%%%%%****++++ + ++***%=* ==%***++ + ++**=*x =+**++ + ++**xx o+*%$$@@@@$$$%=x· =*== + ===+ +$@@@@@@@@$$$$$$$@@@@@@@@*~ ++** + ==++ x$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@+ x+*= + ===+ ·%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$~ ++== + ++== ~@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@~ ==++ + ** $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ o*= + ++++ ~@@$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@· ++++ + == o@$$$$@@$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ·x== + ox== @$$$$$% =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == o + xx++ $$$$$$@o ·=@@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$ ++xx + +++o @$$$$$$$x ~*@$$$$$@=~~~~~~~~~~~~~~~o$@$$$$@ x+++ + +++~ ~@$$$$$$$@@@%o @$$$@ x@$$$$ o+++ + +++~ ~@$$$$$$$@@$=· @$$$@ +@$$$@ o+++ + +++~ ·@$$$$$$$~ x%@$$$$$@%+xxxxxxxxxxxxxx+$@$$$$@· ~+++ + +++~ ·@$$$$$@x o%@@@@$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@~ ~+++ + +++o @$$$$$$$ o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++o @$$$$$$@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ·+++ + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ·+++ + xx+x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ·+++ + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ·+== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + ox++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + ox++ %$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% == + ==x $@@@$$$$$$$$@@@@$$@@@@$$$$$$$$$@@@@$$@@@@$$$$$$$@@@= o+++ + x+== o%@@@@@@@@%o =@@@@@@@@$+ o%@@@@@@@$x ==xx + ++=+ +=++ + ++**++ ++%%*= %%%= x~=*++ + x+++*%*%====%*%*==xx++**=%*===***===++++***%**==*=%*==xx + ++++++++ xx++=+++ ++=+++ + diff --git a/src/build/framegen/frames/frame_028.txt b/src/build/framegen/frames/frame_028.txt new file mode 100644 index 0000000000..aec5d18e86 --- /dev/null +++ b/src/build/framegen/frames/frame_028.txt @@ -0,0 +1,41 @@ + + + + + ++=***%%%%%%%%%%****++++ + ++***%=* · ==****++ + ++**=*x *=**+x + ++**xo o+*%$$@@@@$$%%=x ===+ + ===+ ·=$@@@@@@@@$$$$$$$@@@@@@@@=~ ++== + ==+x x$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$+ x+== + ==++ ·$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$· +=++ + ++== o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@· ==+x + ** $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ·x== + ++++ o@$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++x+ + == +@$$$$@@@$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ·+== + ox== @$$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ++++ @$$$$$@= +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$% ++xx + +++o $$$$$$$@= =@$$$$$@*o~~~~~~~~~~~~~~o*@$$$$@ x+++ + +++~ x@$$$$$$$@@@$x @$$$@ @$$$$ o+++ + +++~ x@$$$$$$$@@$*~ @$$$@ ~@$$$@ o+++ + +++~ o@$$$$$$$x o*@$$$$$@%+xxxxxxxxxxxxxx+%@$$$$@ o+++ + +++~ o@$$$$$@* ~*@@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$@ o+++ + +++~ ~@$$$$$$$· ·*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++~ ·@$$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ~+++ + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ·+++ + xx+x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ·+== + ++++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ·+== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + ox++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + == o@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$@@$ ~+++ + ++=+ +@@@@@@@@@@*~ ~%@@@@@@@@@$x +@@@@@@@@@* ==xo + ==+o ~~~ ·~~ ~~ +=== + ==== **++ =**= +=== + ++===*=+o~··++*=**++++*%=*x~··x+*=**++++**=*+x··++*=**++ + ++======++ +=++====++ ++======++ + diff --git a/src/build/framegen/frames/frame_029.txt b/src/build/framegen/frames/frame_029.txt new file mode 100644 index 0000000000..a576302733 --- /dev/null +++ b/src/build/framegen/frames/frame_029.txt @@ -0,0 +1,41 @@ + + + + + ++=**%%%%%%%%%%%%*==+++x + ++==*%=* · ==****=+ + ++**=* *=**++ + ++**xo o=*%$$@@@@@$$%=x ==++ + ===+ ·=$@@@@@@@$$$$$$$$@@@@@@@@=~ ++== + ==+x +$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$+ ++== + ===+ ~$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$· +=++ + ++=+ o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@· ==++ + ** @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ·x== + ++++ x@$$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++xx + == +@$$$$$@@$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ·x== + ox== ~@$$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ++++ @$$$$$$$ +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$* ++xx + +++~ $$$$$$$@* +@$$$$$@%o~~~~~~~~~~~~~~~=@$$$$@ ++++ + +++· x@$$$$$$$@@@$= $$$$@o @$$$$ x+++ + +++· x@$$$$$$$@@@*o $$$$@x @$$$@ x+++ + +++· x@$$$$$$$= ~*@$$$$$@$+xxxxxxxxxxxxxx+%@$$$$@ o+++ + +++~ x@$$$$$@$ ·*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$@ o+++ + +++~ o@$$$$$$$x =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++~ ~@$$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++~ ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ·+++ + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ·+++ + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ·+== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + ox++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + ~x== x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + =* +@@@$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@ ·+++ + ++++ =@@@@@@@@@@@+ o$@@@@@@@@@@*~ =@@@@@@@@@$o =++x + **x~ ox+xo ~x++x· oxx~ x+== + **+= ==++ ++== ++** + ==**=*+x ·o*+**++===*++ =+*===++**+*~· ·o==**++ + ++++****==++ ++==**=+++ ++==**==++ + diff --git a/src/build/framegen/frames/frame_030.txt b/src/build/framegen/frames/frame_030.txt new file mode 100644 index 0000000000..7a9f0d7b98 --- /dev/null +++ b/src/build/framegen/frames/frame_030.txt @@ -0,0 +1,41 @@ + + + + + ++=**%%%%%%%%%%*%*===+xx + ++===*== +=%***++ + x+**=* =+**+x + ++*= ·x=%$$@@@@@@$$%=x· ==++ + ==++ ~*@@@@@@@@$$$$$$$$@@@@@@@@*~ ++== + ==+x +@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@+ x+== + ===x o$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$· +=++ + ++++ x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@· ==xx + ** @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ·x== + ++++ +@$$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++xx + == =@$$$$$@@$%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ·x== + ox== o@$$$$$$x ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx+x @$$$$$$@ ~*@@@@$$$$$$$@@@@@@@@@@@@@@@@@$$$$$* ++xx + +++~ ~$$$$$$$$$~ o$@$$$$@$x···············x@@$$$@ ++xx + +++· +@$$$$$$$$@@@%o +@$$@% %$$$$ x+++ + +++· +@$$$$$$$@@@%x =@$$$% %$$$@ x+++ + +++· +@$$$$$$$% =@@$$$$@@*+++++++++++++++*@@$$$@ x+++ + +++· x@$$$$$$@ =$@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$@ o+++ + +++~ x@$$$$$$$* +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++~ o@$$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++~ ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ~+++ + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ·+++ + ++xx @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ·+== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% +== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + ox== =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$@@ ·x== + +++x ~=@@@@@@@@@@@$x ·x$@@@@@@@@@@@=~ ~*@@@@@@@@@@+ ++++ + **o· ~+***=x x=***+~ ~+**+~ ~+== + xx**x+ +o ++ xx** + ++**=*x· o+=+**===*++ ·x=+**====++ ++==== + +++==*%%*===++ ++===*%%**=+=+ +==***==++ + diff --git a/src/build/framegen/frames/frame_031.txt b/src/build/framegen/frames/frame_031.txt new file mode 100644 index 0000000000..f7615a100b --- /dev/null +++ b/src/build/framegen/frames/frame_031.txt @@ -0,0 +1,41 @@ + + + + + ++=**%*%%%$$%%%*%*==++++ + ++***%++ ++%***++ + x+**=* =+**++ + ++== ~+*%$@@@@@@@@$%*+~ ==++ + =*++ o*@@@@@@@$$$$$$$$$$@@@@@@@*o ++== + **+x =@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@= x+== + ==+x x@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$~ +=++ + ++++ +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ ==+x + ** @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ·x== + +++x =@$$$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++xx + == *@$$$$$@@$%$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ·+== + ox== x@$$$$$$= +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + +++x @$$$$$$@~ +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$* ++xx + +++~ ~$$$$$$$$@+ =@$$$$$@+ ~%@$$$@ ++xx + +++· +@$$$$$$$$@@@$+ @$$$@ x@$$$ x+++ + +++· +@$$$$$$$@@@%x o@$$$@ =@$$@ x+++ + +++· +@$$$$$$$$ +$@$$$$$@%=++++++++++++++*@@$$$@ x+++ + +++· +@$$$$$$@o +$@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$@ x+++ + +++· x@$$$$$$$$ x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++~ o@$$$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++~ ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ~+++ + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ·+++ + ++xx @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ·+== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% x== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + ox== =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == %@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$@@ ·+== + +++o o=@@@@@@@@@@@@*o· ~+$@@@@@@@@@@@$+~ o*@@@@@@@@@@* ++xx + **o ~=%%%%=o x*%%%*+ o=%%*x ~+** + x+**x+ x~ xx ox**xo + ==**== ==****=*+o =+****==++ x+==== + ++==*%%%%**=++ ++==**%%%*===+ ++=*%%**==++ + diff --git a/src/build/framegen/frames/frame_032.txt b/src/build/framegen/frames/frame_032.txt new file mode 100644 index 0000000000..0f9edba556 --- /dev/null +++ b/src/build/framegen/frames/frame_032.txt @@ -0,0 +1,41 @@ + + + + ++ + xx++=*%%%%%%%%%%%%%***==++ + ++***%++ o+****++ + ++**=* =+**++ + ==== o=%$@@@@@@@@@@$%=x +=== + **++ x%@@@@@@@$$$$$$$$$$$@@@@@@%x ++** + **+o *@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@* o+** + ==+o +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@o x=++ + ++=+ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==+x + ** ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + +++x *@$$$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· ++++ + == %@$$$$$$@$**$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· ·x== + ox+= +@$$$$$$$ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + +++x @$$$$$$$$ ·=@@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$* ++xx + +++~ o$$$$$$$$$$o ~$@$$$$@= o@$$$@ ++++ + +++· +@$$$$$$$$$@@@$o %@$$@o @$$$ x+++ + +++· +@$$$$$$$$@@%+ $$$$$= @$$@ x+++ + +++· +@$$$$$$$$x x%@@$$$$@@%==============*$@$$$@ x+++ + +++· +@$$$$$$$$ x%@@@@$$$$$$$$@@@@@@@@@@@@@@@@$$$$$@ x+++ + +++· x@$$$$$$$$* o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++~ o@$$$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++~ ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ~+++ + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ·+++ + ++xx @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ·+== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% +== + ++++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + ox== *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == $@@@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$@@~ ·x== + +++o ·o=@@@@@@$@@@@@@=o~o+%@@@@@@@@@@@@%x~~o=@@@@@@@@@@% ++xx + ** ~=%$$$%=~ x*$$$%*x o=%$%+· ·x** + ++**~o ~o**xx + ++****=* ++==**=*+x xx*=****++ =+**xx + ++==*%%%%%**++ ++++*%%%%%%*+++x ++**%%%*==++ + diff --git a/src/build/framegen/frames/frame_033.txt b/src/build/framegen/frames/frame_033.txt new file mode 100644 index 0000000000..6155425b4b --- /dev/null +++ b/src/build/framegen/frames/frame_033.txt @@ -0,0 +1,41 @@ + + + + ++++++ + ++==*%%%%%%*****%%%*%**=++ + +=***%+o · *=**=+ + ++**+= ++**++ + ==++ ·x*$$@@@@@@@@@@@$%+~ +=== + **+x +$@@@@@@$$$$$$$$$$$$@@@@@@@= x+** + **+o ·%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@%· ~x** + ==+o =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ x+== + ++=+ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ++++ + =* ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ** + +++x *@$$$$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ++++ + == *@$$$$$$@@*=%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + ox== x@$$$$$$$x o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ++++ @$$$$$$$@· o%@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$ ++xx + +++~ ·$$$$$$$$$@* x@$$$$$%· =@$$@ xx++ + +++~ x@$$$$$$$$$@@@@* @$$$$ +@$$ o+++ + +++~ x@$$$$$$$$@@$+ =@$$$$ %$$@ o+++ + +++~ o@$$$$$$$$* o%@@$$$$$@@%**************$@@$$@ o+++ + +++~ o@$$$$$$$@· o%@@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$@ o+++ + +++~ ~@$$$$$$$$$o ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++~ ·@$$$$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ~+++ + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ·+++ + xx+x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ·+== + ++++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ·+== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + ox== *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == $@@@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@@@@@$$$$$$$$$$@@o +== + +++o ~o=$@@@@$$$@@@@@%+oo+%@@@@@$$$@@@@@=xoo=$@@@@@@@@@$ ++++ + o ** ·=%$$$$%+ x*$$$$%=~ ~=%$%=~ ·o** + x+**·~ ·~**+x + ++****==x ox*=***=+x ==****+= =+**++ + ++==*%%%%%%*==++ ==*%%%%%%*==++ ++**%%%*==++ + diff --git a/src/build/framegen/frames/frame_034.txt b/src/build/framegen/frames/frame_034.txt new file mode 100644 index 0000000000..690b7d83da --- /dev/null +++ b/src/build/framegen/frames/frame_034.txt @@ -0,0 +1,41 @@ + + + + +++++++++++ + ++=**%%%**==++==**%*%***++ + +=**=* =+*===+x + ++**++ xx==== + ==== o=%$@@@@@@@@@@@@@$%+~ ++**xo + **+x ·=@@@@@@$$$$$$$$$$$$$$$@@@@@$x ~**+x + **+o ~%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@= **xx + ==+o =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ ·x** + ++=+ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ x+++ + ** ·@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + ++++ +@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ o+++ + == +@$$$$$$$@%xo=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + == @$$$$$$$@ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ++xx + ++++ $$$$$$$$$@ ·=@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$@ x+++ + ++xx @$$$$$$$$$@%~ o$$$$$$+ +$$$~ ~+++ + +++x @$$$$$$$$$$@@@@% o@$$@% *@@+ ·+++ + +++x @$$$$$$$$$@@*~ %$$$$$ $$@+ ·+++ + +++x @$$$$$$$$@x ·=@@@$$$$$@@$$$$$$$$$$$$$$$@@$$@+ ·+++ + +++x @$$$$$$$$@ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ·+++ + +++x @$$$$$$$$$@x =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ·+++ + xxxx @$$$$$$$$$$@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ·+== + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ·+== + ++++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ·x== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox+= *$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + ox== =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == $@@@@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$@+ == + +++o ox+*@@@@@$$$@@@@@*+xx=$@@@@$$$$@@@@$=xx+%@@@@@@@@@$ ++++ + ox** x%$$@$$*x ~=%$@@$%=· x%$$*o ** + ++** **xx + xx*****=xx ==****+= ++****+= =+**+x + xx++***%%%%**=++ ++**%%%%%%**++ ++=**%%*==++ + diff --git a/src/build/framegen/frames/frame_035.txt b/src/build/framegen/frames/frame_035.txt new file mode 100644 index 0000000000..32f4209a78 --- /dev/null +++ b/src/build/framegen/frames/frame_035.txt @@ -0,0 +1,41 @@ + + + + ++++++++++++++ + ++=**%*%==xxxxxx++**%*%*==++ + +=**=* ~+*=**++ + ++**++ ==**xx + ===+ +%$@@@@@@@@@@@@@@@$%+ ox**++ + **+x ~*@@@@@$$$$$$$$$$$$$$$$$@@@@@*~ ==++ + **x~ o$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@%~ ==++ + ==+x =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ **xx + ++=+ +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ~+== + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ +++x + ++++ o@$$$$$$$$@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ·x== + ==+· ~@$$$$$$$$@+ ·+$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==xo + == @$$$$$$$$$ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++++ + ox== =$$$$$$$$$$ =$@@$$$$$@@@@@@@@@@@@@@@@@@@@$@~ ~+++ + xx++ @$$$$$$$$$$@%o x$$$$$$ =$@* ·+== + xx++ @$$$$$$$$$$$@@@@= %@$$@~ @@$ == + xx++ @$$$$$$$$$$@%x o$$$$$% +$@$ == + xx++ @$$$$$$$$$$ +$@@$$$$$@@@@@@@@@@@@@@@@@@@@$@$ == + xx++ @$$$$$$$$$$ +%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + xx++ @$$$$$$$$$$@x x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + xx++ @$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== =$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + == x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == $@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@@@@@$$$$$$$$$$@= == + +++o oxx=$@@@@$$$$@@@@$=xx+%@@@@$$$$@@@@@*+xx*@@@@@@@@@@· x+++ + o ** ~=$$@@$%=· +%$@@@$*x ~*$$*x ** + ++** **+x + xx******++ =+****+= +x==**== =+**++ + +=**%%%%%%**++ ++***%%%%***+++x ++==*%%**=++ + diff --git a/src/build/framegen/frames/frame_036.txt b/src/build/framegen/frames/frame_036.txt new file mode 100644 index 0000000000..9545578f3e --- /dev/null +++ b/src/build/framegen/frames/frame_036.txt @@ -0,0 +1,41 @@ + + + + ++++==****==++++ + x==***%==oo x+==%***=+ + xx===*++ ++**== + ++**x+ ·oxx++xxo· ++**++ + **++ o*$@@@@@@@@@@@@@@@@@@$=~ +=== + **+x o%@@@@@$$$$$$$$$$$$$$$$$$@@@@@%~ x+== + =*+o o$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@%· x+== + ++=+ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@o +=++ + xx== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@· *=o~ + ==+· $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* o+++ + ox== @@$$$$$$$$@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + +++o @$$$$$$$$$$~ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == %$$$$$$$$$@ =$@@@$$$$$$$$$$@@@@@@@@@@@@@@$$$@x ·+== + == @$$$$$$$$$$o =@@@$$$$@@@$$$$$$$$$$$$$$$@@$@ == + == o$$$$$$$$$$$@@*~ %$$$$$ $$ == + == x@$$$$$$$$$$$@@@@* +@$$@* %@ == + == x@$$$$$$$$$$@*· +@$$$$@= =$@ == + == x@$$$$$$$$$@ ~*@@@$$$$$$@@@@@@@@@@@@@@@@@@@$@ == + == x@$$$$$$$$$@ ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@$$$$$$$$$$@%xx*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@$$$$$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == o$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == %@@@@@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@@@@@$$$$$$$$$@% == + +++o ~+x+*@@@@@$$$$@@@@%=xx=$@@@@$$$$@@@@@*xx+*@@@@@@@@@o x+++ + ** o*$@@@$%+ =%$@@$$*o x*$%x ** + x+** **+x + xx******+= x=***==x ==**==++ =+**++ + ++==*%%%%*%*==xx ++==*%*%%%%*==++ ++**%**=++ + diff --git a/src/build/framegen/frames/frame_037.txt b/src/build/framegen/frames/frame_037.txt new file mode 100644 index 0000000000..1e29736ef4 --- /dev/null +++ b/src/build/framegen/frames/frame_037.txt @@ -0,0 +1,41 @@ + + + + ++++=***%%%%***=++++ + ++==*%*%+x x+**%*==++ + x+**=*+o *=**++ + ++**x ~x==*****=+o· *=== + **++ +%@@@@@@@@@@@@@@@@@@@@$=~ ++**xx + **+x x$@@@@$$$$$$$$$$$$$$$$$$$$@@@@@* ~o**xx + ==+o o$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ·x** + ++=+ +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ o+== + xx== o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ++++ + +++o %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + == $@$$$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ++xx + ++++ $@$$$$$$$$$* o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ·+++ + +++· o@$$$$$$$$$@ o%@@@@$$$$$$$$@@@@@@@@@@@@@@@@$$@ == + == $$$$$$$$$$$$+ x%@@$$$$@@%*=============*$@$· == + == @$$$$$$$$$$$@@$+ %$$$$% $= ==xx + == @$$$$$$$$$$$$@@@$x =@$$@= $* ==xo + == @$$$$$$$$$$$@x %@$$$$@*· ~$@= ==xo + == @$$$$$$$$$$@ =$@@@$$$$$$@@@@@@@@@@@@@@@@@@@@= ==xo + == @$$$$$$$$$$@ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$@$**$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == *@@@@@@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@@@@@@$$$$$$$$@% == + +++x ~=xx+%@@@@$$$$@@@@@*+xx*@@@@@$$$$@@@@%=xx+%@@@@@@@@o o+++ + ** +%$@@$$*o o*$$@@$%+ +%%x ** + x+** **++ + ++**==****xo ==****++ =+****+= =+**++ + ++ x+==*%*%%%%*==++ ==**%%%%%%**=+ +=**%***++ + diff --git a/src/build/framegen/frames/frame_038.txt b/src/build/framegen/frames/frame_038.txt new file mode 100644 index 0000000000..01d7bdf81f --- /dev/null +++ b/src/build/framegen/frames/frame_038.txt @@ -0,0 +1,41 @@ + + + + x+++==*%%%%%%%%%%%%*==++ + ++***%++ ox**%*==+x + x+**+= x+*=== + ==== ·x=*%$@@@@@$$%*+~ x+**++ + **++ o*@@@@@@@@$$$$$$$@@@@@@@@%+ ==++ + **+o =@@@@$$$$$$$$$$$$$$$$$$$$$$$@@@@%o +=++ + ==+x o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* +=++ + ++++ x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ ==+x + ** @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ·x== + ++++ x@$$$$$$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ +++x + == +@$$$$$$$$$@@@$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ·+== + ox== @$$$$$$$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ++++ @$$$$$$$$$$@= =$@@@$$$$$$$@@@@@@@@@@@@@@@@@@% ++xx + +++o $$$$$$$$$$$$@= =@$$$$$@*o~~~~~~~~~~~~~~o%@ x+xx + +++~ o@$$$$$$$$$$$$@@@%x @$$$@ ~ o+++ + +++~ x@$$$$$$$$$$$$@@$*~ @$$$@ x o+++ + +++· x@$$$$$$$$$$$$o o%@$$$$$@%xxxxxxxxxxxxxxx+$@ o+++ + +++· x@$$$$$$$$$$@+ o*@@@@$$$$$$$@@@@@@@@@@@@@@@@@@$ o+++ + +++· +@$$$$$$$$$$$$ ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + +++· +@$$$$$$$$$$$@@@$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + +++· =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + +++· =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ xxxx + ==+· *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + ==x %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++++ + == %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++++ + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$* ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == x@@@@@@@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@@@@@@$$$$$$$@$ == + ++++ *+oox*@@@@@$$$@@@@@*xox=$@@@@$$$@@@@@%+oo+%@@@@@@@x o+++ + **o· x%$$@$%*o ~=%$$$$%+ +*x **xo + xx**~· **++ + xx**+=****+= ++****== x+******xo =+**+x + ++ +=***%%%%%**== ++==*%*%%%%*==+x x+==***=++ + diff --git a/src/build/framegen/frames/frame_039.txt b/src/build/framegen/frames/frame_039.txt new file mode 100644 index 0000000000..cea0cf6e28 --- /dev/null +++ b/src/build/framegen/frames/frame_039.txt @@ -0,0 +1,41 @@ + + + ++++++ + +++=**%$%%******%%$%%*=+++ + ++***%+o x+%=**++ + ++**+= ==**++ + ===+ ·x*%$@@@@@@@@@@$%*x ==== + **+x +$@@@@@@$$$$$$$$$$$$@@@@@@$+ ++** + ==+o *@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@%~ ~+** + ++=+ o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= ~+== + xx== o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ++++ + ==x· $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==xo + xx++ @@$$$$$$$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% o+++ + ==x· @$$$$$$$$$$@@%=*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + == @$$$$$$$$$$$$ ·=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$* ++xx + ox== +$$$$$$$$$$$@% ·*@@@@$$$$$$@@@@@@@@@@@@@@@@@@ o+++ + xx++ $$$$$$$$$$$$$@$o ~$$$$$$@+ x= ·+== + xx++ @$$$$$$$$$$$$$$@@@$o $$$$@ x== + xx++ @$$$$$$$$$$$$$@@%o $$$$$+ · x== + xx++ @$$$$$$$$$$$$$ =@@$$$$$@@%**************%@$ x== + ++++ @$$$$$$$$$$$@% +$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$* ·x== + +++x @$$$$$$$$$$$$$+ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ·+== + ++xx @$$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ·+== + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ·+++ + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ·+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ~+++ + +++~ ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++~ o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++· +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + ==+· *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ xx++ + ==x %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + ==+· ~@@@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@@@@@@$$$$$$@$ == + ++++ %*xoox%@@@@@$$@@@@@$+oox=@@@@@$$$@@@@@*xoo+%@@@@@@x ~+== + **+· +%$$$$%+ o=%$$$%=o +o **xo + xx**o~ **++ + xx**+=****==+o ~x==**==++ ==****++ =+**+x + ++xx x+==*%%%%%%*==++ x+++**%%%%%*==++ ++****++ + diff --git a/src/build/framegen/frames/frame_040.txt b/src/build/framegen/frames/frame_040.txt new file mode 100644 index 0000000000..e8bfe577f0 --- /dev/null +++ b/src/build/framegen/frames/frame_040.txt @@ -0,0 +1,41 @@ + + + ++++======++++ + +=***%=*++++xx++++**%*%*==+x + xx==**++ o+****++ + ++**x+ · *=** + ===+ ·+*$@@@@@@@@@@@@@@@$*+· xx**++ + **+x ·*@@@@@$$$$$$$$$$$$$$$$$@@@@@%o =*++ + ==+x %@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@x +=++ + ++=+ o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ==++ + ox=* @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ** + +++o =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ x+++ + == $@$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% == + ++++ %@$$$$$$$$$$$@$o ~=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + ==x· o@$$$$$$$$$$$@* ·*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$+ ·+== + == @$$$$$$$$$$$$$% ~*@@@$$$$$@@@@@@@@@@@@@@@@@@ == + == $$$$$$$$$$$$$$@@*~ =$$$$$* == + == @$$$$$$$$$$$$$$$@@@@x @$$$@ == + == @$$$$$$$$$$$$$@@*~ =$$$$$* == + == @$$$$$$$$$$$$$* ~=@@@$$$$$@@@@@@@@@@@@@@@@@@o == + == ·@$$$$$$$$$$$$@= ·=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$· == + == ~@$$$$$$$$$$$$$@%~ ~=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ·x== + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ·+++ + +++~ ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· ~+++ + +++· +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + ==+· *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ x+++ + ==x %$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + +++~ @@$@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@$$$$$@@ == x + ++++ =@*o··~+@@@@@@@@@@@@$+~·~x%@@@@@@@@@@@@*o··o=@@@@@x ~+== + ==x~ ~=%%$%*+ x*%$%%=~ · **xx + x+**x~ oo **++ + x+**+===****+= *=****==xo ++==**==++ =+**++ + ++++ ++****%%%***++ ++==**%%%***+++x ++==**++ + diff --git a/src/build/framegen/frames/frame_041.txt b/src/build/framegen/frames/frame_041.txt new file mode 100644 index 0000000000..aa4c748987 --- /dev/null +++ b/src/build/framegen/frames/frame_041.txt @@ -0,0 +1,41 @@ + + + x+++==******===+++ + ++==****++xo ·~++**%***++ + ++****+x ==%*++ + ++*=ox ~ox+++xx~ ++**++ + **++ o*$@@@@@@@@@@@@@@@@@@%=~ ==== + =*+x o%@@@@$$$$$$$$$$$$$$$$$$$@@@@@%o x+** + ==+x ·%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$o ~+** + ++=+ ~@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* x+== + ** $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= +=++ + ++++ x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ** + == +@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ++++ + xx== o@$$$$$$$$$$$$$% ·=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@o ·+== + +++x @$$$$$$$$$$$$$$ ·=@@@@$$$$$$$$$$@@@@@@@@@@@@$@ == + +++· +$$$$$$$$$$$$$$$· ~*@@$$$$$@@@%%%%%%%%%%%%%%= ==xo + == %@$$$$$$$$$$$$$$@@=~ $$$$$% ++xx + == $$$$$$$$$$$$$$$$$@@@@= *@$$@x ++xx + == $$$$$$$$$$$$$$$$@= =@$$$$$x ++xx + == $$$$$$$$$$$$$$$$ o%@@@$$$$$$@@@@@@@@@@@@@@@@@ ++xx + == @$$$$$$$$$$$$$$$ o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$@*x+%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + == o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + xx+x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ·+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++· x$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ o+++ + ==+· =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++++ + +++o @@$$@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@$$$$@@ ==xo + xx== x@@+~ ·x$@@@@@@@@@@@*o o*@@@@@@@@@@@$+~ ·+$@@$o ~+== + ==+o +*%%*=o o=*%%*x *=+x + ox**+o ++ x+ o~**++ + xx**===+****==+= ++*=****+= o+*=****==++*=**++ + ++++xx x+==********==+x ++********==++ ++==++ + diff --git a/src/build/framegen/frames/frame_042.txt b/src/build/framegen/frames/frame_042.txt new file mode 100644 index 0000000000..96715cf2b1 --- /dev/null +++ b/src/build/framegen/frames/frame_042.txt @@ -0,0 +1,41 @@ + + + xx===***%%%%%%%%***=++ + ++**=%=+ ·o==%*==++ + ++**+= *=**++ + ==== o+=*%$$$$%*=+o =+** + **++ ~=$@@@@@@@@@@@@@@@@@@@@@%+ o**++ + ==+x +$@@@$$$$$$$$$$$$$$$$$$$$$$@@@@$+ ==++ + ++++ ·$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ==++ + xx== $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* *=xo + ==+· %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ·x== + xx== @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++++ + +++~ @$$$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + == @$$$$$$$$$$$$$$$= =$@@@$$$$$$$$$$$$$$$$$$$$$$$$@ ==xo + ox== x@$$$$$$$$$$$$$$@ =$@@@$$$$$$$$@@@@@@@@@@@@@@ ++x+ + xx++ $$$$$$$$$$$$$$$$$* =@@$$$$@@*+++++++++++++ ~+++ + ++++ @$$$$$$$$$$$$$$$$@@@*x *@$$$% ·+== + +++x @$$$$$$$$$$$$$$$$$@@@*o =@$$$* ·+== + ++xx @$$$$$$$$$$$$$$$$$~ o$@$$$$@$x············· ·+== + xx+x @$$$$$$$$$$$$$$$@ o*@@@@$$$$$$$@@@@@@@@@@@@@@* ·+++ + +++x @$$$$$$$$$$$$$$$$~ o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$+ ·+++ + +++o @$$$$$$$$$$$$$$$$@@%%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ·+++ + +++o ·@$$$$$$$$$$$$$$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++~ o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++· =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+xx + == %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++++ + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + ox== =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% == + +++o $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+ ·+++ + +++~ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + ++++ *@@$$@@@@@@@$$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@@@@@@$@@@ ==xx + ox== %@@=~ x$@@@@@@@@@@%x ~*@@@@@@@@@@@=· x$$~ ~+== + ==++ o+==x~ ~x+=+o *=xx + ox**++ == == == xo**++ + xx==**+=*=****==+= ·x*=******+=x~ *=******+===**++ + x++=++ ++==********++xx xx++********++++ ++++ + diff --git a/src/build/framegen/frames/frame_043.txt b/src/build/framegen/frames/frame_043.txt new file mode 100644 index 0000000000..849ab37940 --- /dev/null +++ b/src/build/framegen/frames/frame_043.txt @@ -0,0 +1,41 @@ + + + ++==***%*%%%%%%%%%**=+++ + ++**=*+x o+**%*== + ++**+= x+**=+ + ===+ o+*%$@@@@@@@$%=+~ ++**+x + =*=x o*@@@@@@@$$$$$$$$$@@@@@@@%+ +=++ + ==+x +@@@@$$$$$$$$$$$$$$$$$$$$$$$@@@@$x x+== + ++=+ $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$o x+== + xx== $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ +=++ + ==+~ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o *=xo + ox== $@$$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ~+== + +++x $@$$$$$$$$$$$$$$@@@%$@@@$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==x =@$$$$$$$$$$$$$$$% +$@@@@$$$$$$$$$$$$$$$$$$$$$$$ ++++ + == @$$$$$$$$$$$$$$$@= +$@@@$$$$$$$@@@@@@@@@@@@@= ·x== + == o$$$$$$$$$$$$$$$$$@= =@$$$$$@*~···········~ == + ox== =@$$$$$$$$$$$$$$$$$@@@$+ @$$$@ == + ox== *@$$$$$$$$$$$$$$$$$@@$=~ @$$$@· == + ox== *@$$$$$$$$$$$$$$$$$o o%@$$$$$@$+xxxxxxxxxxx+ == + ox++ %@$$$$$$$$$$$$$$$@= o%@@@@$$$$$$$@@@@@@@@@@@@@@ == + xx++ %@$$$$$$$$$$$$$$$$$ o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$ == + xx++ $@$$$$$$$$$$$$$$$$@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + ++++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% x== + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ·+++ + +++~ ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· ~+++ + +++· =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + == $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + ox== =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + xx+x $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$% ·x== + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + ++++ o@@$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@@@@@@@@$ ==xx + xx== =@@@+ =@@@@@@@@@@%o x$@@@@@@@@@@+ ·= ~x== + ===+ ~xxo ·oxo· ·o**xx + ===+ =*=+ +=*= x+== xx**++ + ==**=**=**==**=*+= ·o=+*=****===*x~ +=*=**==***=**++ + ++++++++ ++==******==++ ++==******==++ x+++ + diff --git a/src/build/framegen/frames/frame_044.txt b/src/build/framegen/frames/frame_044.txt new file mode 100644 index 0000000000..5ebc901a33 --- /dev/null +++ b/src/build/framegen/frames/frame_044.txt @@ -0,0 +1,41 @@ + + xx++++++ + ++***%*%*%****%%%%$%**== + x+==**+= x+%%%*++ + ++**x+ =***++ + ===+ ~+%$@@@@@@@@@@@@$*+· *=== + ===+ +$@@@@@$$$$$$$$$$$$$@@@@@@$=· xx**xx + ===+ =@@@@$$$$$$$$$$$$$$$$$$$$$$$$$@@@$+ **+x + ++== %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$o **xo + ** %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ·x** + ++++ ·@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ ++++ + == x@$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$@* == + xx== ~@$$$$$$$$$$$$$$$$$@$=+%@@@@$$$$$$$$$$$$$$$$$$$$$$@$ x+++ + +++x @$$$$$$$$$$$$$$$$$@ x%@@@@$$$$$$$$$$$$$$$$$$$$* == + ==+· +$$$$$$$$$$$$$$$$$$@ x%@@@$$$$$$@@@@@@@@@@@@ == + == $$$$$$$$$$$$$$$$$$$$@= =@$$$$@* ==xo + == @$$$$$$$$$$$$$$$$$$$$@@@@= +@$$@* ++xx + == @$$$$$$$$$$$$$$$$$$$@@%x %$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$x +$@@$$$$@@$%%%%%%%%%%* ++xx + == @$$$$$$$$$$$$$$$$$$@ x$@@@@$$$$$$$$$@@@@@@@@@@% ++xo + == @$$$$$$$$$$$$$$$$$$$% +%@@@@$$$$$$$$$$$$$$$$$$$$$$$= ==xo + == @$$$$$$$$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + == o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + x+== *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ·+== + +++o ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + ==+· *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++++ + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + ox== +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ++++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ~+++ + xx+= $@@$$$@@@@@@@@@@$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@@@@@@% ++xx + ox==x· ·%@@@%o ~%@@@@@@@@$+ =@@@@@@@@@*~ o+== + +++= ~x**xo + ====~o **== =*** +=%%x+++**++ + ++**===*%=**====**=*++++==*=**====**=*==++++%**=**==**==++ + ++++==++++ ++++****==++++ x+++==***=+++x xx + diff --git a/src/build/framegen/frames/frame_045.txt b/src/build/framegen/frames/frame_045.txt new file mode 100644 index 0000000000..5ad22dd68d --- /dev/null +++ b/src/build/framegen/frames/frame_045.txt @@ -0,0 +1,41 @@ + + xx++++++++xx + x+==***%****======%%%%%%==++ + xx=**=++ **%*++ + ++**xx x+%*++ + ===+ x*$@@@@@@@@@@@@@@$*+· ++**+x + ===+ =$@@@@@$$$$$$$$$$$$$$$@@@@@$+ **++ + ++=+ =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@%~ ==++ + x+== %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ==++ + ==x· *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ **xo + x+++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ·+== + ==+· @$$$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$@@ ++xx + == @$$$$$$$$$$$$$$$$$$$@=ox*@@@@$$$$$$$$$$$$$$$$$$$$$@ ·+== + xx++ %$$$$$$$$$$$$$$$$$$$$ o*@@@@$$$$$$$$$$$$$$$$$$@ == + +++o @$$$$$$$$$$$$$$$$$$$$ o%@@@$$$$$$@@@@@@@@@@@ ++++ + +++· x$$$$$$$$$$$$$$$$$$$$$@= =@$$$$$o ++++ + +++· =@$$$$$$$$$$$$$$$$$$$$$@@@@= %@$$@o x+++ + +++· =@$$$$$$$$$$$$$$$$$$$$@$= $$$$$% x+++ + ==+· =@$$$$$$$$$$$$$$$$$$$$ o%@@$$$$$@@@$$$$$$$$$ xx++ + ==+· *@$$$$$$$$$$$$$$$$$$@$ o*@@@@$$$$$$$$$$$$$$$$$$@ x+++ + ==x %@$$$$$$$$$$$$$$$$$$$$% o*@@@@$$$$$$$$$$$$$$$$$$$$$$$ ++++ + == $@$$$$$$$$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + == +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ++++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ·+== + +++~ ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + ==x %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++++ + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* =+xo + == ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$· == + xx++ %$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ *@$$$$$$$$$$$@@$$$$$$$$$$$$$$$$@@$$$$$$$$$$$$$$$$@@@@@ ~+== + ++== *@@@$$$@@@@$%%@@@@$$$$$$$$@@@@%%$@@@$$$$$$$$@@@@$%$* =+xx + ==+~ +$@@@%o ~*@@@@@@@$= +$@@@@@@@*o o+== + ++==o x+*=xx + ====x+ ++%%== ox***= ****+=**++ + ++**=*=****=**++==**=%****%*%***++==**=*=***%%%***======+x + ++++++++ ++++======++ x+++======++++ + diff --git a/src/build/framegen/frames/frame_046.txt b/src/build/framegen/frames/frame_046.txt new file mode 100644 index 0000000000..be7984158e --- /dev/null +++ b/src/build/framegen/frames/frame_046.txt @@ -0,0 +1,41 @@ + + ++++====++++xx + x+==***%=*=+xooox+==%%%*%*== + xx**==+x x+%*==+x + ++**ox ···· %=== + ===+ ·+%@@@@@@@@@@@@@@@@$%+· x**++ + ==++ *@@@@@$$$$$$$$$$$$$$$$$@@@@@%+ +=== + ++=+ =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@* ++== + ox== *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ x+== + ==+~ x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* +=++ + ox== %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x *= + +++o $@$$$$$$$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$$$$$$$$@% o+++ + == %@$$$$$$$$$$$$$$$$$$$$@$o ·=$@@@$$$$$$$$$$$$$$$$$$@$ == + == @$$$$$$$$$$$$$$$$$$$$@% =$@@@$$$$$$$$$$$$$$$$% ++x+ + x+++ %$$$$$$$$$$$$$$$$$$$$$$$ =$@@$$$$$@@@@@@@@@ o+++ + xx++ @$$$$$$$$$$$$$$$$$$$$$$@@%o x$$$$$% ·+++ + ++++ @$$$$$$$$$$$$$$$$$$$$$$$$@@@@+ $$$$@ ·x== + ++++ @$$$$$$$$$$$$$$$$$$$$$$@@%o x$$$$$% ·x== + +++x @$$$$$$$$$$$$$$$$$$$$$$$ =$@@$$$$$@@@@@@@@@% ·+== + xx+x @$$$$$$$$$$$$$$$$$$$$$@% =@@@@$$$$$$$$$$$$$$$$$= ·+== + +++x @$$$$$$$$$$$$$$$$$$$$$$@$o ~=$@@@$$$$$$$$$$$$$$$$$$$$@+ ·+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++~ x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + ==+· *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + xx++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ·x== + +++~ o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ox++ + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$= ==xo + ox== x$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx== @$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@@@ ·+== + ox== @@@@$$$@@@@@*+x+*@@@@$$$$$@@@@%=+x=$@@@@$$$$@@@@@*x ==++ + ==++ x%$@@$=o x%$@@@$%x ~=$@@@@$=~ ++== + ++==+o x+==+x + ++==++ ++****++ ++*=*=++ x*=**==xx + ++==*****%%%%***++++*****%%%%**=**++++==***%*%%*****==++ + ++++++++++ xx++++++++++xx ++++++++++++ + diff --git a/src/build/framegen/frames/frame_047.txt b/src/build/framegen/frames/frame_047.txt new file mode 100644 index 0000000000..8dfb79787b --- /dev/null +++ b/src/build/framegen/frames/frame_047.txt @@ -0,0 +1,41 @@ + + ++++========++++ + ++==**=*==+x~····~x+**%%%%*=+x + ++**==xx ·x%*%*++ + ++==~o ~~ooo~· *=**+x + ===+ ~=$@@@@@@@@@@@@@@@@@$*x *==+ + ===+ *@@@@@$$$$$$$$$$$$$$$$$$@@@@@*· ++** + ++=+ +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$o ox** + ox== =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@% ~+*= + ==+o ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ x+++ + ox== =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ==+x + ++++ *@$$$$$$$$$$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$@@ ·+== + ==x· x@$$$$$$$$$$$$$$$$$$$$$$$%~ x%@@@@$$$$$$$$$$$$$$$$@ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$ x%@@@@$$$$$$$$$$$$$@ x+++ + o == x$$$$$$$$$$$$$$$$$$$$$$$$$ x%@@$$$$$@@@$$$$= ·+== + ox++ %@$$$$$$$$$$$$$$$$$$$$$$$$@$+ ·$$$$$% == + ox++ $@$$$$$$$$$$$$$$$$$$$$$$$$$@@@@= %@$$@o == + ox++ $@$$$$$$$$$$$$$$$$$$$$$$$$@*~ +@$$$$$~ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$ ~*@@@$$$$$$@@@@@@@ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$ ~*@@@@$$$$$$$$$$$$$$$$ == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$@+~o=@@@@$$$$$$$$$$$$$$$$$$@$ == + ++++ @$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$@* ·+== + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ·+++ + +++~ ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++· =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+xx + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + o == +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + x+++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$o ~+++ + ==x· *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ +++x + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$% ++xo + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== @@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$@@$ ~+== + ox== +$@@@@@@@@@@@%+~ o*@@@@@@@@@@@@=o ~+$@@@@@@@@@@%· ==++ + ==++ ~+*%%%=x x=%%%*+~ ~+*%%=x +=== + ++==++ xo xx +===xx + ++====++ ======++ox =+**====++ =+====+x + ++++=***********==++++==**=**%******++++==**=*********++ + ++++++++++ ++++++++++ ++++++++++ + diff --git a/src/build/framegen/frames/frame_048.txt b/src/build/framegen/frames/frame_048.txt new file mode 100644 index 0000000000..74a3df818c --- /dev/null +++ b/src/build/framegen/frames/frame_048.txt @@ -0,0 +1,41 @@ + + ++======**====++ + ++****=*++o~ ·~++%*$%**++ + x+**+= **%*++ + ==== ~oxxxxxo~ ++**++ + ===+ o*$@@@@@@@@@@@@@@@@@@%=~ ==== + ===+ *@@@@$$$$$$$$$$$$$$$$$$$@@@@@%x xx**xo + ++== +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@= **xx + ** =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x ** + +++x @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ·+== + =* x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++++ + ++++ x@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$$$@+ ** + +++~ @$$$$$$$$$$$$$$$$$$$$$$$$$$$x +$@@@@$$$$$$$$$$$$@= ++xx + == $$$$$$$$$$$$$$$$$$$$$$$$$$$@· x$@@@@$$$$$$$$$@~ ·+++ + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$* x$@@$$$$$@@$$ == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$x +$$$$$o == + == o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$o @$$$@ == + == o@$$$$$$$$$$$$$$$$$$$$$$$$$$$@$o ·$$$$$$% == + == x@$$$$$$$$$$$$$$$$$$$$$$$$$$@x =$@@@$$$$$@@@@ == + xx== +@$$$$$$$$$$$$$$$$$$$$$$$$$$@x =$@@@$$$$$$$$$$$$$ == + xx== *@$$$$$$$$$$$$$$$$$$$$$$$$$$$@$+x=$@@@$$$$$$$$$$$$$$$$@ == + xx++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$@ == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + ++xx @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ·+== + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + ==+· *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$o == + xx++ %$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + +x+x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ·+== + +++· +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ox== *@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$@@% ~+== + ==+~ +@@@@@@@@@@%x ~%@@@@@@@@@@= =@@@@@$+ ==++ + ==== ~ooo ·ooo~ +=== + xx==++·~+==+ +=** o+** xo==== + ++==========+= ~ ~x==*=======+=xo ++%=======+=++==*===== + x+++++++==**********==++++====********==++++++==****==++ + ++++++++ xx+++++xxx ++ + diff --git a/src/build/framegen/frames/frame_049.txt b/src/build/framegen/frames/frame_049.txt new file mode 100644 index 0000000000..a8e97f28dc --- /dev/null +++ b/src/build/framegen/frames/frame_049.txt @@ -0,0 +1,41 @@ + + ++====*****=**=+++ + ++***%==++ ··x+**$%**++ + ++**+= =*%*++ + ==== ·ox+++++xo· x+**++ + ===+ x*@@@@@@@@@@@@@@@@@@@$=o *+== + ===+ ·*@@@@$$$$$$$$$$$$$$$$$$$$@@@@$= xx**xx + ++== +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@% **+x + ** +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ **xo + ++++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ·x** + ** o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ++++ + xx++ ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@* == + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$% +$@@@$$$$$$$$$$@% +++x + == %$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ +$@@@@$$$$$$@+ ·+== + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$x +$@@$$$$$@ == + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@%o %$$$$$ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@* o@$$@% == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@=· +@$$$$@* == + == ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o%@@@$$$$$$@~ == + == o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o*@@@@$$$$$$$$$$ == + o == +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@%++*@@@@$$$$$$$$$$$$$@ == + x+== *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$$$$@ == + xx++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ++++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% == + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ·+++ + +++· +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ +++x + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+ == o + ox== *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ++++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$% ·x== + +++~ x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + == ~@@@@@@@@@@$$$$$$$$$@@@@@@@@@@$$$$$$$$@@@@@@@@@@$@@@+ o+== + ==+x o =@@@@@@@@@*· o$@@@@@@@@$x *@*~ ==xx + +++=x· ==+= + oo====xo x+%%+x %%=+ %*%* ++==== + ++============+==*++++*=%*=======*+=++++==**=========*%=*=++ + ++==++xx++====******==++ ++====******==++xx++====++++ + ++++ ++++ + diff --git a/src/build/framegen/frames/frame_050.txt b/src/build/framegen/frames/frame_050.txt new file mode 100644 index 0000000000..3151bf4408 --- /dev/null +++ b/src/build/framegen/frames/frame_050.txt @@ -0,0 +1,41 @@ + + ++===*******===+++ + ++****=*+x o+**%**=++ + ++**++ +=$*== + ==== ~o++===++o~ o+%*++ + ==++ x%@@@@@@@@@@@@@@@@@@@$*x =+** + ===+ ~%@@@@$$$$$$$$$$$$$$$$$$$$@@@@$= o**+x + ++== =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@%· **xx + ** =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= **xo + ++++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ·o** + == x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o x+++ + xx++ o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@@@$$$$$$$$@% *= + +++~ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$* +$@@@$$$$$@$ +++x + == %$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ +$@@@$$@= ·x== + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ +$@$@ == + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@%o $$· == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@+ *@o == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ *@@~ == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ +$@@@$@· == + == o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ +$@@@@$$$$@ == + == +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@%+=$@@@@$$$$$$$$@ == + ox== *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$@ == + xx++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% == + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ·+++ + +++· +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ +++x + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+ ==xo + ox== *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ++++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$% x== + +++~ o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @@$$$$$$$$$@@@@$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$@@@@@@% == + ==x· $@@@$@@@@$***%@@@$$$$$$$@@@@%***@@@@$$$$$$@@@@$***%· x+== + ==++ +$@%+ =$@@@@@$*o x%@@@@@@%+ ·x==xx + +++++x xx==++ + ==+=++ ==**=+ +=%**= x+***==+==++ + =======*%**==========*****%**==========*******%**=======++ + ++++====++ ++++========++xx xx++========++++ + + diff --git a/src/build/framegen/frames/frame_051.txt b/src/build/framegen/frames/frame_051.txt new file mode 100644 index 0000000000..bdf577d22a --- /dev/null +++ b/src/build/framegen/frames/frame_051.txt @@ -0,0 +1,41 @@ + + x++===*******===+++ + ++**=*==+x o+**%**=++ + ++**+= ==%*++ + ===+ ~x+=====+x~ x=**++ + **+x +%@@@@@@@@@@@@@@@@@@@$*x =+== + ===x o$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$= ox**xx + ++=+ %@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* **xx + oo== %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ** + ==+o o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ·+== + == *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++++ + ++++ *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@@@$$$$@+ == + ==x· ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ·=@@@@$@= ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ·=@@@~ ~+++ + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ·* == + == +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@*~ == + ox== +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$x == + ox== +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$o == + ox== =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% =$@ == + ox== *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$% =@@@@$$ == + ox++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@*+=$@@@$$$$$@ == + ox++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$$$$$$$$$$ == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% == + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ·+++ + +++~ o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + ==x %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++++ + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$% +++x + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + xx++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$= ·+++ + +++· =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xx + == $@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@= ** + ==+· =@@@@$$$$@@@@@*+xx=$@@@@$$$$@@@@%=xx+%@@@@$$$$@@@@* ++++ + ==++ +%$@@@%=o o*$@@@$*+ +%$@@$*+ o+==xo + ++++++ ++==++ + ====*=x+ ++**==++ x+*===++ x+====++ + ++=====***%%***=**+++======*%%%*****==++====*%*%%**=**==++ + ++++====++++xx +++++===++++++ ++++++==++++++ + + diff --git a/src/build/framegen/frames/frame_052.txt b/src/build/framegen/frames/frame_052.txt new file mode 100644 index 0000000000..22bc6aea29 --- /dev/null +++ b/src/build/framegen/frames/frame_052.txt @@ -0,0 +1,41 @@ + + ++++===*******===+++ + +=***%==x~ o+**$%*=++ + +=**++ **%*++ + x+**++ ·ox+====+xo~ ==**+x + xx**xo o*@@@@@@@@@@@@@@@@@@@@%=~ ==++ + xx**x· *@@@@$$$$$$$$$$$$$$$$$$$$@@@@@%o ++*= + ==+o x@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$x o+** + ++++ x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ~+== + ** $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ++++ + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ == + ==+· @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@@ o+++ + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$· x == + ox++ *$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + ++++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$* x+++ + +++x $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$=· ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@*· ·+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@*· ·+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ~+++ + +++~ ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@=+*@@~ o+++ + +++· x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$$ o+++ + ==+· *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + o == +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ·+++ + +++· +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ x+++ + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == $@$$$@@@@@@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$@@@ ·x== + ==+· x@@@*o o%@@@@@@@@@@@%x· ~=@@@@@@@@@@@@=~ x%@$~ ==++ + ==++ x=***=o o+***=+· ++== + x+===+ ++ ox ~x +===xx + ++====x+==*====+++ ++*=====++ x+*=*===++*+====xx + ++==****++++==************++++++=*******%***==++++==**++ + ++ ++++++++++xx ++++++++++++ + + diff --git a/src/build/framegen/frames/frame_053.txt b/src/build/framegen/frames/frame_053.txt new file mode 100644 index 0000000000..a49bc03a13 --- /dev/null +++ b/src/build/framegen/frames/frame_053.txt @@ -0,0 +1,41 @@ + + ++++==*********==+xx + x+==**=*+=o~ ··++%*%%*=++ + ==**++ %***++ + x+**++ ~o++====+x~ *+** x + ++** +%@@@@@@@@@@@@@@@@@@@@*x **++ + ++=* o%@@@@$$$$$$$$$$$$$$$$$$$$@@@@$= +=++ + ox**o %@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* +=++ + ==+o %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x +=++ + xx== =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ==xx + ==+~ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ·x== + == $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + xx++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ·+== + +++~ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* == + ==x· *$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ==xo + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$o ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$% ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$* ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ++++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ++++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% x== + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ~+++ + +++· =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+xx + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xo + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+ == o + == ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ox== $@$$$$$$$$$@@@@@@@$$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@@@% ~+== + ==x· o$@@@@@@@$+ +@@@@@@@@@@@%o o%@@@@@@@@@@$+ ==xx + ==++ ·x+o ·x+=+x· o+==+o +=== + xx===+ == ++ o+=++=== + ++====+=xo ~x*===**==+=xo ++****==+=+x ++*=**==== + xx++=*********++++++=***********++++++==**********==++xx + ++xx++ xx++++xx xx++++++ + + diff --git a/src/build/framegen/frames/frame_054.txt b/src/build/framegen/frames/frame_054.txt new file mode 100644 index 0000000000..ffc240ee74 --- /dev/null +++ b/src/build/framegen/frames/frame_054.txt @@ -0,0 +1,41 @@ + + ++====******====++ + x+==**=*++o· ~x==%*%*== + ++**=*+x ++%=== + ++**xx ~x++==++o~ o+**++ + ===+ o*$@@@@@@@@@@@@@@@@@@@*x =+** + ===+ ·*@@@@$$$$$$$$$$$$$$$$$$$$@@@@$= oo**xx + ++=+ *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* **xx + xx== %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@o ·~** + ==+~ +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ o+== + xx== $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ==xx + +++o @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ·x== + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ +++x + ++++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ·+== + ++++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ·x== + ++++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ·x== + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ·+== + ++xx @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ·+== + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ·+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ·+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++~ o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++· =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + ==x %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + xx== +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ·x== + xx== @@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$@@@~ ++++ + ox==x =@@@@@@@@@@%~ ~%@@@@@@@@@$+ =@@@@%~ ·x== + ==++ ~oo~ ·ooo· ==++ + xx===+ %% %% **=+ +x==++ + ox====**====+*+x ++*=**===*=*++ xx**==**=*==+=+=*=**++ + ++xx ++==********==++ ++==********==++ ++++****++xx + + + diff --git a/src/build/framegen/frames/frame_055.txt b/src/build/framegen/frames/frame_055.txt new file mode 100644 index 0000000000..f233e3ef09 --- /dev/null +++ b/src/build/framegen/frames/frame_055.txt @@ -0,0 +1,41 @@ + + ++=====*****====++ + ++***%=*=+o· x+**%%**=+ + ++**+* ==**=+ + ==== ·~ox+++xo~ ++**++ + xx**++ +%@@@@@@@@@@@@@@@@@@@%+· =*== + ox**xo +$@@@@$$$$$$$$$$$$$$$$$$$@@@@@*~ ++*= + =*+o o$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$~ x+== + ++++ +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ++++ + ox== ·@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==xx + +++o =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ·x== + == %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· ++xx + +x++ =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ·+== + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==+ *$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$x ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + == o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + +x++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% x== + +++x $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+ ·+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ x+++ + ++++ ·@@$$@@@@@@@@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@@% ==++ + xx== x$$= ~%@@@@@@@@@%~ =@@@@@@@@@$+ o= o+== + ==++ ·· ·· ·x==xx + ox===+ %*+x %%=+ **** ++**++ + x==**=**=**==**+*++xxxx++%***==**=*=*xxoo++%=***=======**++ + xx++++xx xx++********++xx ++==******==++ ++++ + + + diff --git a/src/build/framegen/frames/frame_056.txt b/src/build/framegen/frames/frame_056.txt new file mode 100644 index 0000000000..60a5299155 --- /dev/null +++ b/src/build/framegen/frames/frame_056.txt @@ -0,0 +1,41 @@ + + ++++====**====++++ + ++***%=*==o~ · o+*=%%%*==++ + ++**== x+%***++ + ++**++ ~ooooo~· **== + ++**o o=$@@@@@@@@@@@@@@@@@@*x ++**++ + ++== o%@@@@$$$$$$$$$$$$$$$$$$$@@@@@= **++ + ++== ~$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* **++ + ox=* +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@o ·o** + ==+o ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ x+== + xx== %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xx + ==+o @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ~+== + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + xx== +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++xx + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + +++o $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$· ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++~ ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++~ ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++~ o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++~ x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++· +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++· =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + ==+· *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + ==+ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ==xo + +++x =@@$$$$$$$@@@@@@@@@$$$$$$$$$@@@@@@@@@@$$$$$$$$@@@@@$~ == + xx== +$@@@@@$+ =@@@@@@@@@*· o%@@@@@@@@$+ ++++ + ===+ x+== + ===+ **%% ++%%+x x+%%=+**xx + ++**+*++++*=**====**=*=+++++***=**++**=*==++++==%=**++=+ + xx++=====+xx xx++======++++ ++========++ + + + diff --git a/src/build/framegen/frames/frame_057.txt b/src/build/framegen/frames/frame_057.txt new file mode 100644 index 0000000000..ce6e787a89 --- /dev/null +++ b/src/build/framegen/frames/frame_057.txt @@ -0,0 +1,41 @@ + + xx+++===========++xx + ++++***%=*==xx~~~~ox==******+++x + xx==**== ++**==xx + ++**++ ·~~~~· ++**++ + ==== x*$@@@@@@@@@@@@@@@@@*+ ==*= + ===+ =$@@@@$$$$$$$$$$$$$$$$$$@@@@@*· ++** + ===+ =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@%· x+== + ++=+ $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x +=++ + xx== $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ ==xx + +++x +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ·+== + == %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xx + xx++ *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + ==+· ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$% == + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$o == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ == + =* @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$x == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==+~ %@@@$$$$$$$$@@@@$$@@@@$$$$$$$$$@@@@$$@@@@$$$$$$$$@@$ ·x== + ++== o%@@@@@@@@%o *@@@@@@@@$= x%@@@@@@@$= ++++ + ===+ +=== + ====xx ++%%=+ %%== +=== + ++**=*==++++=**=**++=*****++++==%=**++==***%++++==%***++ + ++=======+++ ++++=====+++ xx++++==++++ + + + diff --git a/src/build/framegen/frames/frame_058.txt b/src/build/framegen/frames/frame_058.txt new file mode 100644 index 0000000000..76dd6000d7 --- /dev/null +++ b/src/build/framegen/frames/frame_058.txt @@ -0,0 +1,41 @@ + + xx++++++====++++++ + ++==**%%*%**==++++==**%*****++ + ++***%== x+*=**++ + ==**++ ==**++ + xx**++ x*$@@@@@@@@@@@@@@@%=o x+**++ + ++**xx ~*@@@@@$$$$$$$$$$$$$$$$@@@@@$+ ~==++ + xx**o~ x$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@= ==++ + =*+~ *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ ·x*=xx + ++++ %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ x+== + x+== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ==xx + +++x %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% x+++ + == %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% == + xx++ x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ==xx + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++++ + +++o $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ x+++ + +++~ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++~ %$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++~ x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++~ ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++~ ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++~ ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· ~+++ + +++o ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ~+++ + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ·+++ + xx+x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ·+++ + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ·+== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% x== + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= == + == $$$$@@@@$$$$$$$$@@@$$$@@@@$$$$$$$$@@@@$$$@@@$$$$@@@+ x+++ + ++++ +$@@@@@@@$= ~%@@@@@@@@%~ =$@@@%o ==+x + ==++ +=++ + ===+=*%% ==%%++ ++%%*= x~==== + ++++++**=%**++==%***==++==*%**++++**%***++==***%***=**++ + x+++==++++ ++++++==+++x +++++x + + + diff --git a/src/build/framegen/frames/frame_059.txt b/src/build/framegen/frames/frame_059.txt new file mode 100644 index 0000000000..c2adc0c02b --- /dev/null +++ b/src/build/framegen/frames/frame_059.txt @@ -0,0 +1,41 @@ + + xx++++++++++++++++ + ++==***%%%************%***==++ + ++***%== =+****++ + xx====++ ++====xx + ++**++ x=%@@@@@@@@@@@@@$%+o +=**xx + ++**oo o%@@@@@@$$$$$$$$$$$$$$@@@@@$= x+*=+x + ++== +@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* x+==xx + x+== ~$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@o ++== + ==+~ ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==+x + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ~+== + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==+x + ++++ ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$* == + == @@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ·$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == o$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == $%%%$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + ==x %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++++ + ==+· =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + +++~ x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++o ·$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+ ·+++ + xx++ *@$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$$$$$$$$$@ ~+++ + == x@@@$$$@@@@$$$$$$$$@@@$$$@@@@$$$$$$$$@@@@$$$@@@@$@@@· ++++ + ++++ +$@@@@@@@$= ~*@@@@@@@@%~ +$@%~ == + ==+x ==++ + ===+o ==%%+o ==%%++ ++%%== xo=*++ + ++**++++***%**====%***==++==***%====**%***++++**%%%***++ + ++++==++ ++++++++++ ++xx + + + diff --git a/src/build/framegen/frames/frame_060.txt b/src/build/framegen/frames/frame_060.txt new file mode 100644 index 0000000000..f28078a15d --- /dev/null +++ b/src/build/framegen/frames/frame_060.txt @@ -0,0 +1,41 @@ + + ++++++++++++++ + ++++****%%%%%%%%%%%%%*****++xx + ++***%**+x +=*=**==+x + x+**=*++ ==**++ + ++**+= ·x=%$@@@@@@@@@$%=o xx==== + ==== o*@@@@@@@$$$$$$$$$$$@@@@@@%x ==== + ==*= =@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@= ==++ + ++== +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$~ ==++ + ox== =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ o+== + ==+o x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* =+++ + x+== %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ~+== + ==+o @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==xx + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$% ++++ + xx== x@@@@@@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + xx++ ~%@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$= ·+== + ++++ x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% == + xx++ =$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + xx++ ============*@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + xx++ @@@@@@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ++++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx== *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx== =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ +++x + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + ==+· *$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ x+xx + +++x @@$$$$$$@@$$$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$$$@@$$$$$$@ x+xx + ox== =@@@@@@$$@@@@$$$$$$$$@@@@$$$@@@@$$$$$$$$@@@$$$@@@@@$ ==xo + +++x ~*· =$@@@@@@@%x o%@@@@@@@@* = ·x== + **+o ==++ + ==+= ++%$=* %%** **%% xo**++ + ++****==++++***%====***=**++++***%**==**%=**++++****==xx + ++++++++ ++++++++ + + + diff --git a/src/build/framegen/frames/frame_061.txt b/src/build/framegen/frames/frame_061.txt new file mode 100644 index 0000000000..c33681cc05 --- /dev/null +++ b/src/build/framegen/frames/frame_061.txt @@ -0,0 +1,41 @@ + + ++++++++++++++ + ++++*****%*%%%%%%*%%****==++ + ++=****%++ x+*===**++ + x+==**=* =+====++ + ++**+= ~x=*%$$@@@$$%=+~ ++==++ + ====oo =%@@@@@@@@$$$$$$@@@@@@@@%+ ox==++ + ==*= +$@@@@$$$$$$$$$$$$$$$$$$$$$$@@@@%· ·x==++ + ++== x@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ o+==xx + x+== *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= +=== + ==+~ +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xx + ++++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ++++ + ==+· @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ == + ox== @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==xx + xx++ %@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++++ + ++xx *ooooooooooooooo=@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++o $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ~+++ + +++o %$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ~+++ + +++o =ooooooooooooooo=@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ~+++ + +++o @@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ·+++ + +++x $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ·+++ + xx+x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ·+== + ++++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ·+== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xx + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + +++o @@$$$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$$$$@@ ++++ + ox== *@@@$@@@$$$@@@@$$$$$$$$@@@@$$@@@@@$$$$$$$@@@@$$$@@@$ ==xo + ==+x o%$= ~%@@@@@@@@%~ =$@@@@@@@$+ ·+== + **+o ==xx + ==+= ++%%=+ ++%**= %%*% oo**++ + ++***%**++xx+=***%====*=**==++++***%====***=**++++**==+x + ++++++++ ++++++ + + + diff --git a/src/build/framegen/frames/frame_062.txt b/src/build/framegen/frames/frame_062.txt new file mode 100644 index 0000000000..b8e23ebe0e --- /dev/null +++ b/src/build/framegen/frames/frame_062.txt @@ -0,0 +1,41 @@ + + xx++++++++ + xx++==*******%%*******==++++ + ++==**=***++ · ++==*=**==++ + ++==**=%+o ox=+====++ + ++**== ~ox+====++o· xo+===++ + ++==++ ·+%@@@@@@@@@@@@@@@@@@@@*x +===++ + ====~ *@@@@@$$$$$$$$$$$$$$$$$$$$@@@@%~ ++==++ + ++== *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ++==xo + ++==o· o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ ==++ + ==+x ·@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ·x==xx + ++== %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++++ + ==+~ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ·x== + ox== @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==xo + ++++ *$$$$@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$* ++++ + +++x @$$@@%**************%@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + +++~ ·$$$* ·$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ o+++ + +++~ ~@$@~ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++~ ~@$$@+ ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· ~+++ + +++o @$$$@@@@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ·+++ + xx+x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ·+++ + ++++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ·+== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + ox++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + +++~ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + ox== %@@@$$$@@@@$$@@@@$$$$$$$$@@@@$$@@@@@$$$$$$$$@@@@$$@$ ==xo + ==+o x%@@@%~ ·*@@@@@@@@$+ x%@@@@@@@@*· ·+== + **xo ==xx + ==+= ++%%*= %%** **%%+o**++ + ++***%*=%*==++++***%==++***=**++++***%==++==%***++x++++x + ++ ++++++++ ++++++ + + + diff --git a/src/build/framegen/frames/frame_063.txt b/src/build/framegen/frames/frame_063.txt new file mode 100644 index 0000000000..e3eb943d49 --- /dev/null +++ b/src/build/framegen/frames/frame_063.txt @@ -0,0 +1,41 @@ + + xxxxxxxx + ++++==**************==++++ + ++++*****%==++xx oo++==%*==**==++ + ++**=*== ++======xx + ++===*++ ~oooo~ ++====++ + ++==+= ~+%@@@@@@@@@@@@@@@@@$*x +===++ + ++==xx o%@@@@@$$$$$$$$$$$$$$$$$$@@@@$+ +===++ + ++==o~ x@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ +=== + xx==+~ %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ==++ + ==++ %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ~+==xo + ++== +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + ==+o %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ~+== + == %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==xo + xx++ x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$* ++xx + ++++ @$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + +++o +$$$$$= *$$$$$$$$$$$$$$$$$$$$$$$$$$$$ o+++ + +++o @$$$@ @$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++o %$$$$$% ·$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++o @$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ~+++ + +++o $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ·+++ + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ·+++ + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ·+== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% x== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + ox++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xx + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + +++~ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++xx + xx== %@@@$$$$$@@@@$@@@@@$$$$$$$$@@@@$@@@@@$$$$$$$$$@@@@$$ ==xo + ==+o +$@@@@$+ +$@@@@@@@@%~ ~%@@@@@@@@$+ ·x== + **+o ==xx + **++ +=%%+o ++%%== *%=+=*++ + ++***%==****++++==*%**++++*=**==++++***%++++==*=**++++xx + ++++ ++++++++ ++++++++ + + + diff --git a/src/build/framegen/frames/frame_064.txt b/src/build/framegen/frames/frame_064.txt new file mode 100644 index 0000000000..9fd11f4cb2 --- /dev/null +++ b/src/build/framegen/frames/frame_064.txt @@ -0,0 +1,41 @@ + + + xx++++==********====++++ + ++==****%%****++++==**%**=====++++ + ++==***%++ x+*+====++ + xx====== =+====++ + ++====x+ o=%$@@@@@@@@@@@@$%=o xo====++ + ++===+ ~*$@@@@@$$$$$$$$$$$$$$@@@@@$= +===++ + x+==++ x$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@% ==== + ===+ %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x ==++ + ++== $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ ~+==xx + ox==x· %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++++ + ++++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ·+== + ==+· @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==xx + ox== @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$% ++++ + xx++ +@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + xx++ +@$$$$@= *@$$$$$$$$$$$$$$$$$$$$$$$$= ·+== + ++++ x@$$@* %@$$$$$$$$$$$$$$$$$$$$$$@% ·x== + xx++ %$$$$$ $$$$$$$$$$$$$$$$$$$$$$$$@% x== + xx++ +$@@$$$$$@@$$$$$$$$$$$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$@% x== + xx++ @@$$$$$$$$$$@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx== *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + ==+· %$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ x+++ + +++o @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ xxxx + ox== %@@$$$$$$$$@@@@@@@@@$$$$$$$$$@@@@@@@@@@$$$$$$$$$@@@@ ++xx + ==+o =$@@@@@@@* *@@@@@@@@@%o x%@@@@@@@@@= ** + **x~ ==++ + **+= +=%% %%=x *+**++ + ++***%++++*=**==++===%==++x+==*=**++++*%**++++++*=**++++ + ++++==++ ++====++ ++++==++++ + + + diff --git a/src/build/framegen/frames/frame_065.txt b/src/build/framegen/frames/frame_065.txt new file mode 100644 index 0000000000..b1ee24c5bf --- /dev/null +++ b/src/build/framegen/frames/frame_065.txt @@ -0,0 +1,41 @@ + + + xx++++==========++++xx + ++++==**=*%%%%******%*%*======++++ + ++==**=**%+o ++*=====++ + ++===*+= ======++ + ====++ ·x=%$@@@@@@@@@$%=x +x====++ + x+====oo ·=$@@@@@@@$$$$$$$$$@@@@@@@%+ +===++ + ==== +@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@* +===xx + ++== ~$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x ==== + ++==x· o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ·+==+x + ==++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++== + ox== +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· ==xo + ++++ =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· ++++ + ==+· x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+== + == +%@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$* x== + == =@$$$$$@+ ~*@$$$$$$$$$$$$$$$$$$$$$$@ == + == @$$$@ o@$$$$$$$$$$$$$$$$$$$$$$ == + == ~@$$$@ +$$$$$$$$$$$$$$$$$$$$$$@ == + == +$@$$$$$@$=++++++++++++++*@@$$$$$$$$$$$$$$$$$$$$$$@ == + == $@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xx + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + ==x %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + +++· =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + +++~ o$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ o+++ + +x+x @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + ox== %@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@o ++++ + +++o =@@@@@@@@@$x =@@@@@@@@@@* o%@@@@@@@@@%o *= + **x· ~~ ~~· ·~~ +=++ + **++ **** +=%% ~==++ + ++**=*+xoo++*+**++++**=*+x~~ox=+**==++**=*++ooox++*=**++ + ++======++ ++====++++ ++++====++ + + + diff --git a/src/build/framegen/frames/frame_066.txt b/src/build/framegen/frames/frame_066.txt new file mode 100644 index 0000000000..8b97f549e6 --- /dev/null +++ b/src/build/framegen/frames/frame_066.txt @@ -0,0 +1,41 @@ + + + ++++++++++++++++++ + ++++==*******%%%%***==**====++ + ++=====**%++ x+****====++ + ++====+=++ +x======+x + ++====++ ~x=**%%%%**=+~ +x====++ + ++===+ o*$@@@@@@@@@@@@@@@@@@@@$*o +===++ + ++==++ ·*@@@@$$$$$$$$$$$$$$$$$$$$$$@@@@* +===+x + xx===+ *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= ++== + ==++ $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ==++ + ++==o %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ~+==xo + ==++ ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ==++ + ox== x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ o+== + ++== o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ==xo + +++x o*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$$$$$@o ==xx + +++~ o%@$$$$$@@=++++++++++++++=$@$$$$$$$$$$$$$$$$$$$$$ ++++ + +++· $$$$@+ @$$$$$$$$$$$$$$$$$$$@ ++++ + ==+· $$$$@~ @$$$$$$$$$$$$$$$$$$$@ x+++ + ==+· x@@$$$$@*~ ·+@$$$$$$$$$$$$$$$$$$$$@ xx++ + ==+· o%@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$$$$$$@ x+xx + +++· x@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + +++· =$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + +++· x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++~ o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++~ ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ·+++ + xx+x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ·+++ + ++++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ·+== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$% == + xx++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% == + == *@@@$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$@@$ o+++ + +++x =@@@@@@@@@@@+ o%@@@@@@@@@@%~ ·=@@@@@@@@@%· ==xx + **x· ox++o ~x++x~ o+x~ x+== + **++ ==+x x+=+ ++== + ++**=%++ ·o=+**++===*++ ++*===+=**==o· ~x*+**++ + x++==**===++ +++=***=+++x ++=***==++ + + + diff --git a/src/build/framegen/frames/frame_067.txt b/src/build/framegen/frames/frame_067.txt new file mode 100644 index 0000000000..f8d68716d2 --- /dev/null +++ b/src/build/framegen/frames/frame_067.txt @@ -0,0 +1,41 @@ + + + ++++++++++++++xx + ++====****************==++++ + ++++=====*=*+x ~o==***=====++ + ++====== ++=+====+x + xx====+= ~x+=====+o~ ++====++ + xx====ox o*$@@@@@@@@@@@@@@@@@@@%+· +===++ + ox==== o%@@@@@$$$$$$$$$$$$$$$$$$$@@@@@= +===++ + ==== ·%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= +===xo + ++==o x@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ==++ + ==+x ·@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ·x==xo + ++== *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++++ + ==+o $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ·+== + ox== @@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==++ + xx+= ·*@@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$$$$$$ ++++ + xx++ ~*@@$$$$$@@%%%%%%%%%%%%%%%%@@$$$$$$$$$$$$$$$$$$@ o+++ + +++x ~@$$$@x +$$$$$$$$$$$$$$$$$$$x ·+++ + +++x @$$$@ @$$$$$$$$$$$$$$$$$@+ ·+++ + +++x ~$$$$$$@o x@$$$$$$$$$$$$$$$$$$@+ ·+++ + +++x =@@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$$$$@+ ·+++ + +++x =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ·+++ + xx+x @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ·+++ + +++x $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ·+== + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ·+== + ++++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* x== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + ox++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + ox== =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@@@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$@@ ·+== + +++x ~*@@@@@@@@@@@%o +$@@@@@@@@@@$+ o*@@@@@@@@@+ ++xx + **o· ~+===+o x====x ~+=+· o+== + x+**x++x ++ ++ x+** + ++==*=++ ~o*+**==**+= ++**==**+*x~ ++**=+ + ++=****===++ ++=******=++ +++==**==+ + + + diff --git a/src/build/framegen/frames/frame_068.txt b/src/build/framegen/frames/frame_068.txt new file mode 100644 index 0000000000..829d8f6a31 --- /dev/null +++ b/src/build/framegen/frames/frame_068.txt @@ -0,0 +1,41 @@ + + + ++++xx++ + ++++====************==++++ + ++==**===%**++xo~~~~xx+=%*%****=+++x + ++======+= *=*===++ + ++====++ ·~~~· =+====xx + ++==++ ·+%$@@@@@@@@@@@@@@@@$=o +===++ + ++==+x o*@@@@@$$$$$$$$$$$$$$$$$@@@@@$x +===+x + ++==+o ~$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@+ +===xo + ox==+x =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ++== + ++++ +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ·o==+x + xx==x· @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++== + ++++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ==xo + ==+· @@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ++x+ + == x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + xx== +$@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$$= ·+== + xx++ ~$$$$$$ +$$$$$$$$$$$$$$$$$$ == + ++++ @* *@$$@o @$$$$$$$$$$$$$$$$@ == + ++++ x$$$$$$· =$$$$$$$$$$$$$$$$$@ == + ++++ =$@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$$@ == + ++++ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ++++ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ %$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox++ *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== =$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + == x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == %@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@@@@@@$$$$$$$$@@+ == + +++o oo+%@@@@@$$@@@@@%+o~o=$@@@@$$$@@@@@*x~o+%@@@@@@@@% ++++ + ** x*%$$$*+ ~=%$$$%=o +*%=~ ·o** + xx**· ·o**++ + xx******+= =+****=* ++=***=*+x =+**xx + ++==%%%%%%==++ ++==*%%%%%**=+ x+==*%%*==++ + + + diff --git a/src/build/framegen/frames/frame_069.txt b/src/build/framegen/frames/frame_069.txt new file mode 100644 index 0000000000..2868c56cd4 --- /dev/null +++ b/src/build/framegen/frames/frame_069.txt @@ -0,0 +1,41 @@ + + + + ++++==**********====++++ + xx++==****%%**++++++++**%**=**==++ + ++**=*=* ~x*=====++ + xx====+= ++====++ + ++===+ ~=%$@@@@@@@@@@@@@@$%+· o=*==++ + x+==++ +$@@@@@$$$$$$$$$$$$$$$$@@@@@%o +===xx + ox===+ =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$x +===xx + ==++ ·$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ++== + ++== @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ==xx + ==+~ *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ ++== + ++== $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + ==+x $@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ++++ + == +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+== + == +$@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$$ == + == ~$$$$$$+ x$$$$$$$$$$$$$$$$@ == + == %@%· ~@$$@% =@$$$$$$$$$$$$$$$ == + == %$$$$$~ $$$$$$$$$$$$$$$$@ == + == ~*@@@$$$$$@@@@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$@ == + == ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ~=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == +$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + == o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == $@@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@@@@@$$$$$$$$$@% == + +++o x*+=*@@@@$$$$$$@@@$*=+=%@@@@$$$$$@@@@%=+=*@@@@$@@@@o x+++ + ~o** o*$@@@@$*o +%@@@@@%+ x*$%+ ** + ++** **++ + ++**==**== ==****xo =+****++ ++**++ + +x ++==%%%%%%%*==++ x+==*%%%%%%%**=+ +=*%%%**=+ + + + diff --git a/src/build/framegen/frames/frame_070.txt b/src/build/framegen/frames/frame_070.txt new file mode 100644 index 0000000000..67de5408d6 --- /dev/null +++ b/src/build/framegen/frames/frame_070.txt @@ -0,0 +1,41 @@ + + + + ++++++========++++++ + ++==*****%%%********%*%*****==++ + ++==**=*++ ++*=**==++ + ++**+= =+**++ + xx====ox ~+%$@@@@@@@@@@@@$%=~ xo====xx + xx===+ ·=@@@@@@$$$$$$$$$$$$$$@@@@@@=· +===++ + ===+ ~%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@%~ +=== + ++== =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ==++ + ++==o =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ==+x + ==++ ·@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ x+== + xx== =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + ++++ ~$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++++ + ==+· x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ·+== + == x%@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$@ == + == *@$$$$@+ ·%$$$$$$$$$$$$$$$ == + == ·@@@+ %@$$@x $$$$$$$$$$$$$$@~ == + == ·*o $$$$$* ·$$$$$$$$$$$$$$@~ == + == =@@$$$$$@@$%%%%%%%%%%%%%%%@@$$$$$$$$$$$$$$@~ == + == =$@@@$$$$$$$$$$@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$@~ == + == +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == ~@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == ·$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$@@$$$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$$$@@$$$$$$$$$@@ == + ==+~ %@@$$@@@@$$$$$$$$@@@@$$$@@@@$$$$$$$$@@@@$$@@@@$$@@@% ·+== + xx== =@@@@@@@@%x x%@@@@@@@@= *@@$+ ==xx + ++=+ +=++ + ++==x+**%* **%%+x +=%%=+ oo==++ + ++==++x+**=%=*==**%***++x+==*%*%====****+++++=*****===++ + ++++++ ++++++++ ++ + + diff --git a/src/build/framegen/frames/frame_071.txt b/src/build/framegen/frames/frame_071.txt new file mode 100644 index 0000000000..42ed54523f --- /dev/null +++ b/src/build/framegen/frames/frame_071.txt @@ -0,0 +1,41 @@ + + + + +++++++===+++++++x + xx++*****%*%*%%%*%%*%*******++xx + xx++***%== +=*=**==++ + ++**+=+x *=**++ + ====x+ x=%$@@@@@@@@@@$%*x· xx==== + ==== x%@@@@@@$$$$$$$$$$$$@@@@@@$+ +=== + ==== *@@@@$$$$$$$$$$$$$$$$$$$$$$$$$@@@%~ +=== + ++== x@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= ==++ + xx==x· x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ==+x + ++++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o x+== + == o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xx + xx++ %$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++++ + +++o =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ·x== + == =$@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$@ == + == x ·%@$$$$@= o$$$$$$$$$$$$$$$~ == + == @@@$x %@$$@x $$$$$$$$$$$$$@+ == + == @%x $$$$$* $$$$$$$$$$$$$@+ == + =* +$@@$$$$@@%***************@@$$$$$$$$$$$$$@+ == + =* x%@@@@$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$@+ == + == x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$o == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==x· $@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@@@@@@@$$$@@@ ·x== + xx== =@@@@@@@@@@= o%@@@@@@@@@%~ =@@@%· ++xx + ==++ ·· ·~· x+== + ==++ x+%% %%+x **=+ +=== + ++**==++===%==xoox==%===++==*%==+xoo++*=**+++=**=**=**++ + +++==+++ +++===++++ ++++ + + diff --git a/src/build/framegen/frames/frame_072.txt b/src/build/framegen/frames/frame_072.txt new file mode 100644 index 0000000000..95a4a2af40 --- /dev/null +++ b/src/build/framegen/frames/frame_072.txt @@ -0,0 +1,41 @@ + + + + +++++++++++++x + ++==*****%*%%%%%%%%*****==++ + ++***%=*x~ *=*=**++ + x+**==++ xx=+**++ + ++**++ ~+*%$@@@@@@@@$$%=o +x**=+ + ++==oo ~*@@@@@@@$$$$$$$$$$@@@@@@@%x =*== + ++== +$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@* +=== + x+==o $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= ==++ + ==+o @@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ==xx + ++++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o x+== + ==x· @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xx + xx== $%$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% x+++ + +++x =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= == + ==+· =$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$$$$$$@ == + == @x ·*@$$$$@$o o$@$$$$$$$$$$$$x ==xo + == $@@@$x o@$$$$ *@$$$$$$$$$$@* ==xo + == @@@*o =@$$$$ %$$$$$$$$$$$@* ==xo + == % ·=@@$$$$@@%===============*@@$$$$$$$$$$$@* ==xo + == ·=$@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$$$$$$$$@* ==xo + == % =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == @@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==x ~@@@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@@@@@@$$$$@@+ == + x+++ =%o +$@@@@@@@@@@@+· o*@@@@@@@@@@@*o ·+$@@@* ++++ + =*+~ x=**=x· o+=*=+o ·x** + **+x x+ +x ++ ox**xo + ====*===**+*+x x+*=**===*++ *+**==**++++*=== + +x xx===*%%*===++ x +==*%%*=*=++ ++=+ x + + diff --git a/src/build/framegen/frames/frame_073.txt b/src/build/framegen/frames/frame_073.txt new file mode 100644 index 0000000000..d2d6c6d725 --- /dev/null +++ b/src/build/framegen/frames/frame_073.txt @@ -0,0 +1,41 @@ + + + + x+++++++++++ + ++++****%%%%%%%%$%%%****++++ + ++****=*+x *=*=**++ + xx==**++ ++*=**++ + ++**++ x=%$$@@@@@@@$%*+~ ++**++ + ++**oo =$@@@@@@@$$$$$$$$$@@@@@@@%x =*== + ++==o· o$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@* +=== + x+=*o· %@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ==++ + ==+x $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ==xx + ++== *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o x+== + ==+· @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ==xx + xx== @%%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ x+++ + ++++ o ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* == + ==x· x o*@@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$$$$$$@ == + == $$~ o$@$$$$@$o ·x@$$$$$$$$$$$$= ==xx + == @$@@@*~ *@$$@= $$$$$$$$$$$@% ++xx + == @@@@*o %$$$$* $$$$$$$$$$$@% ++xo + == @= ~*@@$$$$@@*+++++++++++++++%@$$$$$$$$$$$@% ++xx + == @ ·*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$$$$$$$@% ++xx + == @+ ·=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+ == o + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + == +@@$$@@@@@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$@% == + ++++ %@$=o~o=$@@@@$$$@@@@@*x~~x*@@@@@$$$@@@@$+o~o=$@@@$~ x+++ + **o· ~+%$$$%*o x*$$$$%+· · ** + x+**o~ ~**+x + xx**+==+****== ++*=**=*x+ oo==****++ =+**+x + ++=+ ++==*%%%%%%*== xx+=*%%%%%%**=++ ++==++ + + diff --git a/src/build/framegen/frames/frame_074.txt b/src/build/framegen/frames/frame_074.txt new file mode 100644 index 0000000000..bb794eea44 --- /dev/null +++ b/src/build/framegen/frames/frame_074.txt @@ -0,0 +1,41 @@ + + + + xx+++++x + ++==***%*%%%%%%%%%****+++x + x+==***%++ ~x%*****++ + ++**++ ++*=**xx + xx**++ o+*%$$@@@@@$$%*x~ +x**++ + x+**xx +$@@@@@@@@$$$$$$$@@@@@@@@%x =*++ + x+**o~ ~%@@@@$$$$$$$$$$$$$$$$$$$$$$$@@@@* +=++ + o **+~ *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ==++ + ==+x %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ==xx + x+== +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o o+== + ==+~ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ==xo + ox== @@%$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ x+++ + ++++ $o o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* == + +++~ ·@ o%@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$$$$$$@ == + == %$$· x$@$$$$@$x~~~~~~~~~~~~~~~+@$$$$$$$$$$$* =+xo + == @$$@@@*~ *@$$@* $$$$$$$$$$$$ ++xx + == @$@@@*x *$$$$* $$$$$$$$$$$$ ++xo + == @$* =@@$$$$@@=xxxxxxxxxxxxxx+*@$$$$$$$$$$@$ ++xx + == @@ =$@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$$$$$$@$ ++xx + == @$+ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xo + == @$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* =+xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+ == + == %@$$$$$@@@$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$$@@@$$$$$@@ == + +++o +@@@@$**%$@@@$$$$$$$@@@@%**%@@@@$$$$$$$@@@$**%@@@@@* ~+== + ox** ~ ~*@@@@@@@%o +$@@@@@@$= oo ==xx + ++== ==++ + ++**o+ ++%%** ++%%**xx **%%++ +x**++ + +=**++ ++***%*%%%%*%*++ xx==*%*%%%%*%*==++ +=**=+ + xx ++ + diff --git a/src/build/framegen/frames/frame_075.txt b/src/build/framegen/frames/frame_075.txt new file mode 100644 index 0000000000..497f7840cf --- /dev/null +++ b/src/build/framegen/frames/frame_075.txt @@ -0,0 +1,41 @@ + + + + x+++ + ++==***%%%%%%%%%%***==++ + ++**=*++ ~x%*%*==+x + ++**== ++*===+x + xx**== ~+*%$$@@@@@$$%=x· ++**++ + xx**+x x%@@@@@@@@$$$$$$$@@@@@@@@%o ==++ + xx**x~ ·*@@@@$$$$$$$$$$$$$$$$$$$$$$$@@@@* +*++ + **x~ =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ==++ + ==++ *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* *=xx + xx== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x o+== + ==+~ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ==xo + == @@$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ x+++ + ++++ %$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% == + +++~ @* +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$$$$@ == + ==+ *$@= =@$$$$$@*o~~~~~~~~~~~~~~o*@$$$$$$$$$$* +++x + == $$$@@@$+ @$$$@ @$$$$$$$$$$ ++xx + == @$$@@$*~ @$$$@ ~@$$$$$$$$$$ ++xx + == @$$x o%@$$$$$@%+xxxxxxxxxxxxxx+$@$$$$$$$$$$$ ++xx + == @@= ~*@@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$$$$$$ ++xx + == @$$ ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+ ==xo + == $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + +++~ *@@@@@@$$@@@@$$$$$$$$@@@@@$@@@@$$$$$$$$$@@@@$$@@@@@$ ·x== + oo== x* ·*@@@@@@@@$x +$@@@@@@@@= ~+ ==+x + ++=+ +=++ + ++==o **** =*%%x ++%$++ ==++ + x+==****+x++***%**==**%***++++==**=*====%***=+xx++**==xx + ++++++ +++++++x + diff --git a/src/build/framegen/frames/frame_076.txt b/src/build/framegen/frames/frame_076.txt new file mode 100644 index 0000000000..517a5a5b34 --- /dev/null +++ b/src/build/framegen/frames/frame_076.txt @@ -0,0 +1,41 @@ + + + + + ++==***%%%%%%%%*%***==++ + +=****++ o+*=%*==xx + ++**+= ++*=== + **+= ~+*%$$@@@@@$$%=x~ ++**++ + xx**+x x%@@@@@@@@$$$$$$$@@@@@@@@%x ==++ + ox**x~ *@@@@$$$$$$$$$$$$$$$$$$$$$$$@@@@* =*++ + =*+~ =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= ==++ + ++++ *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* *=oo + xx== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x o+== + =++o %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ==xo + == $@@$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ o+++ + xx++ *@% =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + +++o @$o ·=@@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$$$@· == + ==+· *$$$x ~*@$$$$$@=~~~~~~~~~~~~~~~o%@$$$$$$$$$% ++xx + == $$$$@@@%o @$$$@ x@$$$$$$$$@ ++xx + == $$$$@@$=· @$$$@ +@$$$$$$$$@ ++xx + == $$$$~ x%@$$$$$@*+xxxxxxxxxxxxxx+$@$$$$$$$$$@ ++xx + == $$@o o%@@@@$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$$$$$$@ ++xx + == $$$% o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == @$$@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==+· $@@$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@@@@@@@@@· == + x+== ·%@$x =@@@@@@@@@@= o$@@@@@@@@@$o =o ++xx + ===+ ·~~· ~o~ x+== + ===+ ++%% %%+x **=+ ++== + ++**=**===++**=*=+~·~o+=%=**++===*==o~··x+%=**++++****++ + ++ x++=====++xx ++======++ + diff --git a/src/build/framegen/frames/frame_077.txt b/src/build/framegen/frames/frame_077.txt new file mode 100644 index 0000000000..4ead3508d6 --- /dev/null +++ b/src/build/framegen/frames/frame_077.txt @@ -0,0 +1,41 @@ + + + + + +++==**%%%%%%%%*%%**=+++ + ++****++ ·x**%*==xx + ++**+= +x*=== + ==+= ~+*%$$@@@@@$$%*+~ +x**++ + xx**+x x%@@@@@@@@$$$$$$$@@@@@@@@%x ==++ + **x~ *@@@@$$$$$$$$$$$$$$$$$$$$$$$@@@@* +*++ + ==+~ =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= ==++ + ++++ *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* **oo + xx== o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ o+== + +++o %@$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ==xo + == $@@@$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ o+++ + ++++ *@$~ o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + +++o @$@ x%@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$$$@~ == + ==+· =$$$% x@@$$$$@$x~~~~~~~~~~~~~~~=@$$$$$$$$$% ++xo + == $@$$@@@$=· %$$$@+ @$$$$$$$$@ ++xx + == $@$$@@@*o %$$$@= $$$$$$$$$@ ++xx + == $@$$= ·=@@$$$$@@=xxxxxxxxxxxxxx+*@$$$$$$$$$@ ++xx + == $$$$ =$@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$$$$$@ ++xx + == $$$$x =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == $$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==+· @@$$$@@@@@@@@$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@@@x == + x+++ x$@@%o +@@@@@@@@@@@+ o%@@@@@@@@@@%o =+ ++++ + ==+x ox+xo ~x++x· ~+** + **++ == ==++ ++== ++** + ++**==*=**==**=*++ ·o*+**++===*++ ++*===++****=+ + ++++ ++++****==++ +==***==++ + diff --git a/src/build/framegen/frames/frame_078.txt b/src/build/framegen/frames/frame_078.txt new file mode 100644 index 0000000000..4a7f7ab729 --- /dev/null +++ b/src/build/framegen/frames/frame_078.txt @@ -0,0 +1,41 @@ + + + + + +++===*%*%%%%%%%%%%*=+=+ + ++**=*++ ·o**%*==xx + ++**+= +x*=== + ==== o+*%$@@@@@@@$%*+o +x**++ + xx**+x +%@@@@@@@$$$$$$$$$@@@@@@@%x ==++ + **x~ ·%@@@@$$$$$$$$$$$$$$$$$$$$$$$@@@@%~ +=++ + =*+~ =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ==++ + ++++ *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% **oo + ox== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ~+== + +++o %@$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ==xo + == $@$@$%$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ o+++ + xx++ *@$% +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + +++o @$@= +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$$@o ==xo + ==+· =$$$@= =@$$$$$@=~··············~*@$$$$$$$$$ ++xo + == %@$$$@@@$+ @$$$@ ·@$$$$$$$@ ++xx + == $@$$$@@$= @$$$@ o@$$$$$$$@ ++xx + == $@$$$~ x%@$$$$$@$+++++++++++++++=$@$$$$$$$$@ ++xx + == $@$@+ o%@@@@$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$$$$$@ ++xx + == $$$$$ o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == $$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + ==x· ~@@$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@@@@@@@@= == + xx++ =@@@@$x ·x$@@@@@@@@@@@=~ o*@@@@@@@@@@@%x ·+* x+++ + **x~ x=***+~ ~+***=o ·o** + **+x o++ ++ ++ oo**xo + ====++x+*===**==+o ·x*=**====++ =+**==*===== + ++=+++ ++===*%%*==+++ ++==*%%**=++ xx + diff --git a/src/build/framegen/frames/frame_079.txt b/src/build/framegen/frames/frame_079.txt new file mode 100644 index 0000000000..98c847b442 --- /dev/null +++ b/src/build/framegen/frames/frame_079.txt @@ -0,0 +1,41 @@ + + + + + ++==**%%%%%%%%%%$%%**=++ + ++****+o ==%*==xx + ++**+= ox*=== + =*== o=%$$@@@@@@@$$%=o xx**++ + xx**+x +$@@@@@@@$$$$$$$$$@@@@@@@$+ =*++ + **o~ ~%@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@%o +=++ + **x~ *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ==++ + ++=+ *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% **xx + xx== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ~+== + +++o %@$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + == $@$@@%%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + xx++ *@$@· ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + +++o @$$@ ~*@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$$@o ==xo + ==+· =$$$$$~ o$@$$$$@%~ x@$$$$$$$$$ ++xx + == %@$$$$@@@%o %@$$@+ $$$$$$$$@ ++xx + == $@$$$@@$=~ %$$$@* $$$$$$$$@ ++++ + == $@$$$= o*@@$$$$@@*++++++++++++++=%@$$$$$$$$@ ++xx + == $@$$$ ~*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$$$$@ ++xx + == $@$$$+ ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == $$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xo + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* =+xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + == o@@$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@@@* == + ++++ *@@@@@*x· ~x%@@@@@@@@@@@$+~ o=@@@@@@@@@@@@*o· ~+* x+++ + **x· o x*%%%*+· ~=%%%%=o ·o** + ox**xo oo xx xx oo**xx + ===*+x *+****=*+x =+****==++ ++==*****=== + ++====++ ++==**%%%**=++ ++=**%%%*==+x ++ + diff --git a/src/build/framegen/frames/frame_080.txt b/src/build/framegen/frames/frame_080.txt new file mode 100644 index 0000000000..b8e4d01399 --- /dev/null +++ b/src/build/framegen/frames/frame_080.txt @@ -0,0 +1,41 @@ + + + + ++ + +==**%*%%$%%%%%%%%%*==++ + +=**=*x· +=%***++ + ++**++ *=**+x + **++ ~+*$$@@@@@@@@@$$*+~ ox**++ + xx**oo ~=@@@@@@@$$$$$$$$$$$@@@@@@@=~ +=++ + xx**o· x$@@@$$$$$$$$$$$$$$$$$$$$$$$$$@@@$x +==+ + **x· %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ++++ + +++x %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ ==xx + xx== =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ·+== + =++~ $@$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xx + == @@$@@%*%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + xx++ %$$$= o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + +++o @$$@x x%@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$@x ==xo + ==+· =$$$$@* +@$$$$$@o =@$$$$$$$$ ++xx + == %@$$$$@@@@* @$$$@ o@$$$$$$@ ++xx + == $@$$$@@@*~ o@$$$@ =$$$$$$$@ ++++ + == $@$$$% ·=@@$$$$$@$*==============%@@$$$$$$$@ ++xx + == $@$$@~ ·=$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$$$@ ++xx + == $@$$$$~ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == $$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* =+xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == +@@$$$$$$@@@@@@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@@@@@@@% == + ++++ %@@@@@@*x~~x*@@@@@@$@@@@@$+o~o+$@@@@@$@@@@@@*x~~x*~ o+++ + **o· ·+x o*%$$$%+· ·+%$$$%*o ** + xx**o~ ~**++ + xx**+= x+=****=++ ~x==****+= =+****==**++ + ++==**==xx xx==**%%%%%*==++ ++**%%%%%*==++ ++ + diff --git a/src/build/framegen/frames/frame_081.txt b/src/build/framegen/frames/frame_081.txt new file mode 100644 index 0000000000..aed9c8aaac --- /dev/null +++ b/src/build/framegen/frames/frame_081.txt @@ -0,0 +1,41 @@ + + + + ++++++++ + ++==*%*%%%****%%$%%%**++xx + xx==**=* ++%***++ + ++**++ ==**++ + xx**++ o=%$@@@@@@@@@@@$%=o ==++ + xx**oo o%@@@@@@$$$$$$$$$$$$$@@@@@@%o +=== + x+** +@@@@$$$$$$$$$$$$$$$$$$$$$$$$$@@@@+ ++== + **o· $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$· +=++ + +++x $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@· ==xx + xx== *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ·x== + ==+~ @@$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + == @$$@@%==$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + xx++ %$$$$ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + +++~ @$$@% =@@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$$x ==xo + ==x· *$$$$@$o ~$$$$$$@x o@$$$$$$$$ ++xx + == %@$$$$$@@@$o $$$$@ @$$$$$$@ ++xx + == $@$$$$@@*~ $$$$@+ o@$$$$$$@ ++++ + == $@$$$$ ·=@@$$$$$@@%**************%@@$$$$$$$@ ++xx + == $@$$@% =$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$$$@ ++xx + == $@$$$$+ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == $$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == +@@$$$$$$$@@@@@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@@@@@@$ == + ++++ $@@@@@@$=xox=@@@@@$$$@@@@@*xoo+%@@@@@$$$@@@@$=oox*o o+++ + **x· ~==· ~=%$$$$*x +%$$$$%=· ** + x+**~· **+x + xx**+= ==****++ ==****+= =+****==**++ + ++==**==++ ++**%%%%%*==++ +=***%%%%%**++ ++ + diff --git a/src/build/framegen/frames/frame_082.txt b/src/build/framegen/frames/frame_082.txt new file mode 100644 index 0000000000..ba491c1f56 --- /dev/null +++ b/src/build/framegen/frames/frame_082.txt @@ -0,0 +1,41 @@ + + + + xx++++++++++ + ++==***%%%*=++++==**%*%*==++ + ++**=*++ · *=**++ + ===* ==**++ + ++**x+ o=%$@@@@@@@@@@@@@$%=~ ==== + ++** +$@@@@@$$$$$$$$$$$$$$$@@@@@$+ ++*= + xx** *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* x+== + o ** o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x ++++ + ==+o ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==+x + x+== %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ·x== + ==x· @@$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == @$$$@%xo=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ·+== + ++++ $$$$@ ·=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + +++~ ·@$$$@ ·=@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$$$+ ==xo + ==+· *$$$$$@*~ x$$$$$$+ =$$$$$$$$ ++xx + == $@$$$$$@@@@* +@$$@* %@$$$$$@ ++xx + == $@$$$$@@=· %$$$$$ ·$$$$$$$@ ++xx + == $@$$$$~ ~*@@@$$$$$@@$$$$$$$$$$$$$$$@@$$$$$$$@ ++xx + == $@$$$@ ·*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == $$$$$$$o ·=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == $$$$$$$@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == =@$$$$$$$$@@@@@@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@@@@@@$ == + +++x $@@@@@@@%+xx=%@@@@$$$$@@@@@*xox*@@@@@$$$$@@@@%+xx=x o+++ + ** o*%+ +%$@@$%=~ o*$$@@$%+ ** o + x+**~ **+x + xx**+= =+****++ ++****== x+****==**++ + ++==*%**++ ++**%%%%$%**=+ ++==*%%%%*%*==++ ++ + diff --git a/src/build/framegen/frames/frame_083.txt b/src/build/framegen/frames/frame_083.txt new file mode 100644 index 0000000000..d730b375fd --- /dev/null +++ b/src/build/framegen/frames/frame_083.txt @@ -0,0 +1,41 @@ + + + + ++++++++++=++x + ++==*%*%**++xxxxxx==%*%**=++ + +=**=*+~ *=**=+ + x+**== ++**++ + ++**xo ·+%$@@@@@@@@@@@@@@@$%x ==== + ++*= ~*@@@@@$$$$$$$$$$$$$$$$$@@@@@*~ x+** + ++== ~$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@%~ ~+** + xx** +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= x+== + ==x· x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ +=++ + x+++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ** + ==x· @$$$@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ++++ + ox== @$$$@*~ o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· ·+== + xx++ @$$$@~ o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + +++~ o$$$$$x x%@@@$$$$$@@@@@@@@@@@@@@@@@@@$$$$$$$= ==xo + ==x %@$$$$@$+ %$$$$$+ ~$$$$$$$$ ++xx + == $@$$$$$$@@@$~ @$$@$ +@$$$$$@ ++xx + == $@$$$$@@= %$$$$$x ·$$$$$$$@ ++xx + == $@$$$@x o*@@@$$$$$@@@@@@@@@@@@@@@@@@@$$$$$$$@ ++xx + == $$$$$@ o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == $$$$$$@=· ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == $$$$$$$@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == =@$$$$$$$$$@@@@@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$@@@@@$ == + +++x ·$@@@@@@@$=xx+%@@@@@$$$@@@@@%+xx=$@@@@$$$$@@@@$=xx=x o+++ + ** o*%=~ x%$@@@$%x ~=%$@@$%=~ **xo + ++** **++ + x+**+= =+****== xx****== ==**==**+x + ++=**%**=+ ++***%%%%*%*++xx ++==*%%%%*%*==++ xx + diff --git a/src/build/framegen/frames/frame_084.txt b/src/build/framegen/frames/frame_084.txt new file mode 100644 index 0000000000..1fd3352b97 --- /dev/null +++ b/src/build/framegen/frames/frame_084.txt @@ -0,0 +1,41 @@ + + + + ++++===***==++++ + +=***%**+x~· oo==%%%*++++ + +=**== x+*=**++ + ++**++ ~ox+++xo~ ===+ + ++== +%@@@@@@@@@@@@@@@@@@$%x ++**xx + ===+ =@@@@@$$$$$$$$$$$$$$$$$$$@@@@@+ oo**xx + ++=+ =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= ·o** + x+== $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ~+== + ** %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% +=++ + ++++ ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ ** + == x@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ++++ + ox== ~@$$$@= o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ·x== + +++x @$$$@+ o%@@@@$$$$$$$$$$@@@@@@@@@@@@@@$$$$$$$$@ == + +++· +$$$$$% x%@@$$$$$@@$$$$$$$$$$$$$$$$@@$$$$$$= ==xo + == $@$$$$@@$+ x@$$$$x =$$$$$$$ ++xx + == $$$$$$$$@@@$o @$$$@ @$$$$$@ ++xx + == $$$$$$@$x ·$$$$$$$· o$$$$$$$@ ++xx + == $$$$$@= +$@@@$$$$$@@@@@@@@@@@@@@@@@@@$$$$$$$@ ++xx + == $$$$$$= +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == @$$$$$@@=x+$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == @$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* =+xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == =@$$$$$$$$$@@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@@@@@$ == + +++x ·@@@@@@@@@%+xx=$@@@@$$$$@@@@$=xx+%@@@@$$$$$@@@@%+x+o o+++ + ** o*$%x ~=%$@@$%=~ +%$@@@$%x **xo + ++** **++ + ++**+= xx****** ==****xo ==******+x + ++***%%*++xx ++==*%%%%*%**=++ ++==*%*%%%%***++ + diff --git a/src/build/framegen/frames/frame_085.txt b/src/build/framegen/frames/frame_085.txt new file mode 100644 index 0000000000..4e074e65be --- /dev/null +++ b/src/build/framegen/frames/frame_085.txt @@ -0,0 +1,41 @@ + + + + ++++****%%%%***=++++ + ++==***%+x ox**%*==++ + xx===*+x *=**++ + ++**x+ ~x+=*****=+o· ==*= + ===+ x%$@@@@@@@@@@@@@@@@@@@$*o +x**+x + ==+x o%@@@@$$$$$$$$$$$$$$$$$$$$$@@@@%~ **xx + ===+ ·%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@% **xx + ++++ o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@~ ·x** + ** @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + +++x +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x *= + =* *@$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++++ + xx++ +@$$$$x +$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ == + +++o @$$$@* +$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@ == + ==+· =$$$$$$· +$@$$$$$@@*==============*@@$$$$$$= ==xo + == $@$$$$$@@*x $$$$@x o@$$$$@$ ++xx + == $$$$$$$$@@@%~ $$$$@ @$$$$$$ ++xx + == @$$$$$@%· o$$$$$$@+ +@$$$$$$$ ++xx + == @$$$$@* ~*@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$$ ++xx + == @$$$$$% ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$@@%*%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == =@$$$$$$$$$@@@@@@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@@@@@$ == + +++x ·$@@@@@@@@$=xx+%@@@@$$$$@@@@@*+xx*@@@@@$$$$@@@@%=xxo o+++ + ** o*$%+ +%$@@$$*o o*$$@@$%+ ** o + ++** **++ + x+**+= ~x=*****xo ==***=++ =+******+x + ++==*%%*==xx x+==*%*%%%%*==++ ==**%$%%%%**=+ + diff --git a/src/build/framegen/frames/frame_086.txt b/src/build/framegen/frames/frame_086.txt new file mode 100644 index 0000000000..d1ca5713f2 --- /dev/null +++ b/src/build/framegen/frames/frame_086.txt @@ -0,0 +1,41 @@ + + + + x+++==*%%$%%%%%%%**===++ + +=****++ ox****== + ++**+= ++**++ + ==== ~+*%$$@@@@@$%*=x· ++**+x + xx**xx +%@@@@@@@@$$$$$$$@@@@@@@@*o **++ + ox**o~ ~%@@@@$$$$$$$$$$$$$$$$$$$$$$$@@@@= ==++ + **+~ *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ==xx + ++=x %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ** + xx== +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o x+++ + ==+~ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* == + == @@$$$@@$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ x+++ + +x++ $$$$$$ x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* == + +++~ ·@$$$@$ +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ == + == %$$$$$@* +@@$$$$@%x~~~~~~~~~~~~~~o=@$$$$$$= ==+x + == @$$$$$$@@@$+ $$$$@o @$$$$@$ ++xx + == @$$$$$$@@@*o $$$$@x @$$$$@$ ++xx + == @$$$$$@= ~*@$$$$$@$+xxxxxxxxxxxxxxx%@$$$$$@$ ++xx + == @$$$$@$ ·=@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@$ ++xx + == @$$$$$$~ ·=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++++ + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + == =@$$$$$$$$$$@@@@@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@@@@% == + +++x $@@@@@@@@@=xox=@@@@@$$$@@@@@%+oo+%@@@@@$$$@@@@$=xo~ o+++ + ** o*$%=~ o*%$$$$%x +%$$$$%=~ ** o + ++**~ **+x + x+**+= *=***=++ =+****+= =+******++ + ++==*%%*==++ ==**%%%%%***++ ++***%%%%%**++ + diff --git a/src/build/framegen/frames/frame_087.txt b/src/build/framegen/frames/frame_087.txt new file mode 100644 index 0000000000..cfec2a6984 --- /dev/null +++ b/src/build/framegen/frames/frame_087.txt @@ -0,0 +1,41 @@ + + + ++++ + ++=**%*%*%******%%%*==++ + xx==*%== +=*=**++ + ++*=x+ ==**xx + xx**++ o=%$@@@@@@@@@@@%*+~ xx**++ + ++**~ o%@@@@@@$$$$$$$$$$$$@@@@@@$=· =*++ + x+** =@@@@$$$$$$$$$$$$$$$$$$$$$$$$$@@@$o +=++ + ** ~$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ==++ + ==+~ ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ** + x+== %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ o+== + ==x· @@$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ==xo + ox== @$$$$@$=*$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ x+++ + +++x @$$$$$ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% == + ==+· +$$$$$$ =$@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ == + == $$$$$$$$o ·%@$$$$@= o$$$$$$$= ==xo + == @$$$$$$$@@@$o $@$$@o @$$$$@% ++xx + == @$$$$$$@@%x $$$$$= @$$$$@% ++xx + == @$$$$$$~ +$@$$$$$@@%**************%@@$$$$$@% ++xx + == @$$$$$$ +$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@% ++xo + == @$$$$$$= x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ == + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == =@@$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@@@@% == + +++x $@@@@@@@@@*xoo+$@@@@@$$@@@@@%+oox*@@@@@$$$@@@@@=xo· o+++ + **o· ~=%%=o +%$$$$%+ o*%$$$%=~ ** o + x+**~· ~**+x + ++**+= ==****++ =+****+= ++******++ + ++==*%%**=++ ++***%%%%***++ ++***%%%%%**++ + diff --git a/src/build/framegen/frames/frame_088.txt b/src/build/framegen/frames/frame_088.txt new file mode 100644 index 0000000000..237b9c931f --- /dev/null +++ b/src/build/framegen/frames/frame_088.txt @@ -0,0 +1,41 @@ + + + ++++====++++++ + ++==*%**==++++++++==**%*==++ + ++**=* ==**++ + ++**+= · ++**++ + ++== ~=%@@@@@@@@@@@@@@@@$*x ==== + ++=+ x$@@@@@$$$$$$$$$$$$$$$$@@@@@$= ++== + ++=+ +@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* x+== + x+== %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@o +=++ + ** %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ ==xo + ++++ o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ·+== + =* =@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xx + xx++ +@$$$$@x ·+$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++~ @$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$% == + == $$$$$$$ =$@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ == + == @$$$$$$@%o x$$$$$$ =$$$$$$+ == o + == $$$$$$$$@@@@= %@$$@~ @$$$$@= ==xx + == @$$$$$$@%o o$$$$$$ =$$$$$@= ==xo + == @$$$$$$ +$@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@= ==xo + == @$$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$@x +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == +@@$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@* == + ++++ %@@@@@@@@@*o··o=@@@@@@@@@@@@%x~·~x%@@@@@@@@@@@@=o~ x+++ + **x· +*%=o ~=%%$%*x x*%$%%=~ ·o** + x+**o~ o oo**++ + ++**+=xo =+****== *+****==+x ++==****++ + ++==******++ ++****%%%***++ ++==**%%%***==+x + diff --git a/src/build/framegen/frames/frame_089.txt b/src/build/framegen/frames/frame_089.txt new file mode 100644 index 0000000000..b3622446f7 --- /dev/null +++ b/src/build/framegen/frames/frame_089.txt @@ -0,0 +1,41 @@ + + + ++++==****==++++ + ==***%==++ ox==%***== x + ==**++ ++*===xx + ++**++ ·oxx++xxo~ +x**=+ + ===+ ~=$@@@@@@@@@@@@@@@@@@$*o ++** + ==+x ~%@@@@@$$$$$$$$$$$$$$$$$$$@@@@$x x+** + ==+x ·%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$o o+== + ++=+ o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= +=++ + ox=* ·@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==xo + +++o *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ·+== + == $@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==xx + x+++ $@$$$@% ~=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + ==+· x@$$$$$ ~*@@@@$$$$$$$$$$@@@@@@@@@@@@@@@$$$$$$$$ == + == @$$$$$$ ~*@@$$$$$@@$%%%%%%%%%%%%%%$@@$$$$$@ == + == $$$$$$$@@=· $$$$$* o$$$$$$o == + == @$$$$$$$@@@@+ $$$$@· @$$$$@x == + =* @$$$$$$@+ *@$$$$$o %$$$$$@x == + =* @$$$$$$ x%@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@x == + == @$$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$@*x+%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$o == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@@$$$$$$$$$@@@@@@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@+ == + ++++ *@@@@@@@@@=o ·x%@@@@@@@@@@@%x· ~=@@@@@@@@@@@@=~ ++++ + **x· o=*+~ x=%%%=x ~+*%%*+~ ·x** + x+**xo xo ++ ++oo**+x + ++**=*xx *=****==+x *+****==++ ++==****++ + ++==******++ ++==********++xx ++==********==++ + diff --git a/src/build/framegen/frames/frame_090.txt b/src/build/framegen/frames/frame_090.txt new file mode 100644 index 0000000000..e781e1e310 --- /dev/null +++ b/src/build/framegen/frames/frame_090.txt @@ -0,0 +1,41 @@ + + + +++=**%%%%%%%%**=+++ + ++***%==o· ·x==%***++ + ++**+* *+**++ + ==== o+=*%$$$$%*=+o ==== + xx**xx o*@@@@@@@@@@@@@@@@@@@@@@*x xx**+x + xx**o~ ·%@@@@$$$$$$$$$$$$$$$$$$$$$$@@@@%~ ~o**xx + **x· *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ·o** + ==+o %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ o+== + xx== *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ++++ + ==x· @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + xx== @$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ =++x + ++xo @$$$$$o ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + == %$$$$@$ ~*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$$+ ~*@$$$$$@$=+++++++++++++++%@$$$$$@ == + == $$$$$$$@@@=~ $$$$$x @$$$$$~ == + == @$$$$$$@@@@= $$$$@~ @$$$$@~ == + == @$$$$$@% +@$$$$$@%~··············~=@$$$$$@~ == + == @$$$$@$ x%@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@~ == + == @$$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == @$$$$$$@@%%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == @$$$$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ~@@$$$$$$$$$@@@@@@@$$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@@@o == + ++++ +@@@@@@@@@+· +$@@@@@@@@@@%o ~*@@@@@@@@@@@+ ++++ + **+~ x+o o+=+x~ ~x+=+o ~x** + xx**+x == == ++++**+x + xx**==+= x+==*****=+* ~x*=******=*x~ =+==**==xx + ++==******++ ++==********+++x xx++********==+x + diff --git a/src/build/framegen/frames/frame_091.txt b/src/build/framegen/frames/frame_091.txt new file mode 100644 index 0000000000..425fbf0204 --- /dev/null +++ b/src/build/framegen/frames/frame_091.txt @@ -0,0 +1,41 @@ + + + ++==**%%*%%%%%%*%%**==++ + ==***%xx xx%***== + ++**++ ++**++ + ++**++ x=*$$@@@@@@$$*=x ++**+x + ++**~o ~*$@@@@@@@$$$$$$$$@@@@@@@$*~ oo**++ + ++** +$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$+ **++ + xx** ~$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$~ **xx + ==+~ ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ ~+== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ~@$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + x+++ o@$$$$@@%$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o +++x + +++~ @$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + == $$$$$$$ x$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@* +@$$$$$@%o··············~=@$$$$$@ == + == $$$$$$$@@@$= $$$$@~ @$$$$$· == + == ·@$$$$$$@@$*o $$$$@x @$$$$@~ == + == ·@$$$$$$+ ~*@$$$$$@$=xxxxxxxxxxxxxx+%@$$$$$@~ == + == ·@$$$$$$ ~*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@~ == + == ·@$$$$$$o ·=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == ·@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==x· @@$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@@@ x== + ++++ o$@@@@@@@@+ ~*@@@@@@@@@@*~ +@@@@@@@@@@$x ++++ + ==+o ·o~ ~oo~ ~oxo· o+== + xx**+x == == ==++**+x + x+==**+*x~ ++*=**==**+*+x x+*=**==**=*++ ~x*=****==xx + x+++****==++ ++++********++++ ++==******=++x + diff --git a/src/build/framegen/frames/frame_092.txt b/src/build/framegen/frames/frame_092.txt new file mode 100644 index 0000000000..040799032f --- /dev/null +++ b/src/build/framegen/frames/frame_092.txt @@ -0,0 +1,41 @@ + + xx++++xx + ===*%%%%*%****%*%%%%*=== + ++**=%++ ++%=**++ + ===* **== + x+**+x ~+*$@@@@@@@@@@@@$*+~ x+**+x + ++== =$@@@@@$$$$$$$$$$$$$$@@@@@$= ==++ + ++*= ~%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@%~ =*++ + x+== =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= ==+x + =*o =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= o*= + ++++ ·@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@· ++++ + == =@$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= == + x+++ =@$$$$@$**@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ++xx + +++· ·@$$$$$ ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· ·+++ + == @$$$$@$ ~*@@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ == + == $$$$$$@%~ ~$$$$$$@= x@$$$$$$ == + == ·@$$$$$$$@@@%~ $$$$@· @$$$$@· == + == ·@$$$$$$@@%x $$$$$+ ·@$$$$@· == + == ·@$$$$$$~ +$@$$$$$@@%***************$@$$$$$@· == + == ·@$$$$@% x$@@@@$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@· == + == ·@$$$$$@= +$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+· $@@$$$$$$$@@@@@@@@@@$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@@$ ·+== + ++++ =@@@@@@@%o ~%@@@@@@@@@+ +@@@@@@@@@%~ ++++ + ===+ +=== + x+=*=+ ++%%++ %%=+ =*====xx + ==**=*==++*=*=**==**=*=*++++=**=**===*=**%++++==%***==++xx + ++++==**==++ ++==****==++++ ++++****==++++ + diff --git a/src/build/framegen/frames/frame_093.txt b/src/build/framegen/frames/frame_093.txt new file mode 100644 index 0000000000..06e4f4dbe5 --- /dev/null +++ b/src/build/framegen/frames/frame_093.txt @@ -0,0 +1,41 @@ + + xx++++++++xx + x+==*%*%*%========%*%*%*==+x + ++**=*+x x+*=**++ + xx**=* ==**xx + ++**o x*$@@@@@@@@@@@@@@$*x o**++ + ++=+ o*@@@@@$$$$$$$$$$$$$$$$@@@@@*o +=++ + ++=+ x$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$x +=++ + x+== *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ==+x + ** %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ** + +++x x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x x+++ + =* *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* *= + ++++ *@$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++++ + ==+· o@$$$$@* o*@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ·+== + == @$$$$@* =$@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@ == + == $$$$$$% o*@@$$$$@@*++==========++=$@$$$$$$ == + == ·@$$$$$@@$+· $$$$$x $$$$$@· == + == ·@$$$$$$@@@*o %$$$@~ @$$$$@· == + == ·@$$$$$$~ o$@$$$$@*~ ·+@$$$$$@· == + == ·@$$$$@= +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$o o*@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$· == + == @@$$$$$$$$$$$$$@@$$$$$$$$$$$$$$$$@@@$$$$$$$$$$$$$$$$@@ *= + ==+~ *@@@$$$$$$@@@@%%$@@@@$$$$$$$@@@@$%%@@@@$$$$$$$$@@@@* ~+== + ++== o%@@@@@@= x$@@@@@@@%x ~*@@@@@@@@= ==++ + ===+ +=== + ox====o+ ++%%== x+%%** *=====xo + ++**=**%***=*=**++==***%****%**=**++==**=*=***%**=**==++ + x+++====++++ ++++======++xx ++++======++++ + diff --git a/src/build/framegen/frames/frame_094.txt b/src/build/framegen/frames/frame_094.txt new file mode 100644 index 0000000000..bd9522ac36 --- /dev/null +++ b/src/build/framegen/frames/frame_094.txt @@ -0,0 +1,41 @@ + + xx++++====++++xx + ++==*%**==++oooo++==**%*==++ + ++**=* *=**++ + ++**+= ···· =+**++ + ++== x=$@@@@@@@@@@@@@@@@$=x ==++ + ==++ =$@@@@$$$$$$$$$$$$$$$$$$@@@@$= ++== + ===+ *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* +=== + ++=+ ·$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$· +=++ + ox** $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ** o + +++o =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= o+++ + == $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + ==+· +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ·+== + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == $$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$ == + == ·@$$$$@= ·*@$$$$@x =@$$$$@· == + == ·@$$$@ ~@$$@ @$$$@· == + == ·@$$$$x +$$$@ x$$$$@· == + == ·@$$$$@@*==============*@@$$$$@$=++++==========*@@$$$$@· == + == ·@$$$$$$@@@@@@@@@@@@@@@@$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == %@$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@@% == + ==+o ~@@@@$$$$@@@@*+x+*@@@@$$$$$@@@@%=xx=$@@@@$$$$@@@@@*~ o+== + xx== x*$@@$*o o*$@@@@%+ ·=%@@@@$*o ==xx + ==== ~·++== + ====+= =+*==*++ ++****++ ++=*==== + ++*****%**%***==++++**=***%%%*****++++***%%%%%***=**++++ + ++++++++++ ++++++++++++ ++++++++++++ + diff --git a/src/build/framegen/frames/frame_095.txt b/src/build/framegen/frames/frame_095.txt new file mode 100644 index 0000000000..b566afe9cb --- /dev/null +++ b/src/build/framegen/frames/frame_095.txt @@ -0,0 +1,41 @@ + + ++============++ + ++**%%=*++o~····~o++*=%%**++ + +=**== ==**=+ + ++**++ ·~~oo~~· ++**++ + ===+ +%@@@@@@@@@@@@@@@@@@%+ +=== + ===+ ·*@@@@@$$$$$$$$$$$$$$$$$$@@@@@*· ++== + ===x %@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@% x=== + ++=+ o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@o +=++ + xx== @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + ==+~ *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ~+== + == $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + +++x $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ x+++ + ==x· =@$$$$@@@@@@@@@$$$$$$$$$$$$$$$@@@@@@@@@$$$$$$$$$$$$$@= ·x== + == @$$$$$% +%@@@@@$$$$$$$$$$% ·+%@@@@@$$$$$$$$$@ == + == $$$$$@o +%@@@$$$$$$@ +%@@@$$$$$$$ == + == ·@$$$$$$~ o$$$$$$$$ o$$$$$$@· == + == ·@$$$$$$@@@%~ x@$$$$$@@@@*· +@$$$$@· == + == ·@$$$$$@+ $$$$$$$$x $$$$$$@· == + == ·@$$$$@o ·+$@@@$$$$$@ ~+$@@@$$$$$@· == + == ·@$$$$$* ~=$@@@@@$$$$$$$$$= ~=$@@@@@$$$$$$$$@· == + == ·@$$$$$@@$%$@@@@@$$$$$$$$$$$$$$@@$%$@@@@$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@$$$$$$$$$$$$$$$$$$$$$@@$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == +@@$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@+ == + ==+x %@@@@@@@@@@%x· ·x*@@@@@@@@@@@@=~ ~+$@@@@@@@@@@@%x x+== + ++==o· x*%%=x o=%%%*+~ ~+*%%%=x ·x==++ + ====oo ox xo==== + ====+=+x ++======+= ======+= ~ ++*===== + ++==*******=**==++++************==++++==*****%******++++ + ++++++++++ +++++++++x ++++++++++ + diff --git a/src/build/framegen/frames/frame_096.txt b/src/build/framegen/frames/frame_096.txt new file mode 100644 index 0000000000..aff846aa19 --- /dev/null +++ b/src/build/framegen/frames/frame_096.txt @@ -0,0 +1,41 @@ + + ++====****====++ + +=***%==+o o+==%***=+ + ===*++ ++*=== + ++**x+ ·ooxxxxoo· +x**++ + ===+ o=$@@@@@@@@@@@@@@@@@@$=o +=== + **++ o%@@@@$$$$$$$$$$$$$$$$$$$$@@@@%o ++** + ==+x ~$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$~ x+== + ++++ +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ++++ + ox== ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ ==xo + ==+~ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ~+== + ox== @@$$$$$@@$$$$$$$$$$$$$$$$$$$$$$@@$$$$$$$$$$$$$$$@@ ==xo + +++x @$$$$$@@%%@@@@$$$$$$$$$$$$$$$@@$%%@@@@$$$$$$$$$$$$$@ x+++ + ==+ =@$$$$$ x%@@@@$$$$$$$$$$$$ x$@@@$$$$$$$$$$@= +== + == @$$$$@$ +$@@@$$$$$$$@% +$@@@$$$$$$$@ == + == $$$$$$@% =@$$$$$$$@* *@$$$$$$$ == + == ·@$$$$$$@@@$= $$$$$$$$@@@$+ $$$$$$@· == + == ·@$$$$$$@@@$+ $$$$$$$$@@@$+ $$$$$$@· == + == ·@$$$$$@% =@$$$$$$$@* *@$$$$$$@· == + == ·@$$$$$% +$@@@$$$$$$$@% +$@@@$$$$$$$@· == + == ·@$$$$$$ x%@@@$$$$$$$$$$$$$ x%@@@$$$$$$$$$$$@· == + == ·@$$$$$$@$%%@@@@$$$$$$$$$$$$$$$@@$%%@@@@$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$@@$$$$$$$$$$$$$$$$$$$$$$@@$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==x· @@@$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@@ ·x== + ++++ o$@@@@@@@@$+ ·*@@@@@@@@@@*· x$@@@@@@@@@$x ++++ + xx==+x ·oo~ ~oo~ ~ooo~ o+==+x + ++==+x ===+ +=** x++==++ + ++====+=+x x+*+*=====+===o~ ~o=+*+=====*==+x ++*=====++ + ++++==********==++++==**********==++++++=*********==++++ + ++++++ xxxx++xxxx ++++++++ + diff --git a/src/build/framegen/frames/frame_097.txt b/src/build/framegen/frames/frame_097.txt new file mode 100644 index 0000000000..316e492933 --- /dev/null +++ b/src/build/framegen/frames/frame_097.txt @@ -0,0 +1,41 @@ + + ++====****====++ + xx+=***%==x~ ~x==%***=+xx + x+===*++ ++*===+x + ++**+x ~ox++++xo~ x+**++ + ==++ x*$@@@@@@@@@@@@@@@@@@$*x ++== + **+x x%@@@@$$$$$$$$$$$$$$$$$$$$@@@@%x x+** + ==+o o$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$o o+== + ++++ +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ++++ + xx== o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ==xx + ==+· $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ·+== + x+== @@$$$$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$@@ ==+x + +++x @$$$$@@=xx%@@@@$$$$$$$$$$$$$$@@=x+%@@@$$$$$$$$$$$$$@ x+++ + ==x *$$$$$$ x$@@@$$$$$$$$$$$% +$@@@$$$$$$$$$$* x== + == @$$$$$$ =$@@@$$$$$$$% =$@@@$$$$$$@ == + == $$$$$$@@+ ~$$$$$$$$@@+ ~$$$$$$$$ == + == ~@$$$$$$$@@@@+ $$$$$$$$$@@@@+ $$$$$$@~ == + == ·@$$$$$$@@$+ $$$$$$$$@@$+ $$$$$$@· == + == ·@$$$$$$x +$@$$$$$$$$o =$@$$$$$$@· == + == ·@$$$$@$ x$@@@$$$$$$$$@% x$@@@$$$$$$$$@· == + == ·@$$$$$$o ~*@@@@$$$$$$$$$$$$$~ o%@@@@$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==x· $@@$$$$$$$$@@@@@@@@@@$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@$ ·x== + ++++ =@@@@@@@@*~ o%@@@@@@@@$+ =@@@@@@@@@*· ++++ + ox==++ ++==xx + ++===+ **%* =*%% +++===++ + ++=====%+=++==*==========*==++==***========%+=++++*=*+====++ + xx++==******==++xxx+++==******==+++x+x++==******====++ + xx++xx xx++xx xx++++ + diff --git a/src/build/framegen/frames/frame_098.txt b/src/build/framegen/frames/frame_098.txt new file mode 100644 index 0000000000..7eb1d4acfc --- /dev/null +++ b/src/build/framegen/frames/frame_098.txt @@ -0,0 +1,41 @@ + + xx++==********==++xx + x+==*%**=+~· ·~+=**%*==+x + x+=*=*+x x+*=*=+x + ++**xo ·ox+====+xo· ox**++ + **++ x*@@@@@@@@@@@@@@@@@@@@*x ++** + **+o x$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$x x+** + ==+o x$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$x o+== + ++++ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ++++ + xx== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==xx + ==+· $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ·+== + ox== @@$$$$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$@@ ==xo + +++x @$$$$@@+·~=@@@@$$$$$$$$$$$$$$@@x·~=@@@@$$$$$$$$$$$$@ x+++ + == *$$$$$$ ·=@@@@$$$$$$$$$@% ~*@@@@$$$$$$$$$* == + == @$$$$$$ ~*@@@$$$$$$$% ~*@@@$$$$$$@ == + == $$$$$$$@*~ =$$$$$$$@@*· =$$$$$$$ == + == ~@$$$$$$$@@@@+ $$$$$$$$$@@@@+ $$$$$$@~ == + == ·@$$$$$$@$x o$$$$$$$@@$x x$$$$$$@· == + == ·@$$$$$$ +$@@$$$$$$$$ +$@@$$$$$$@· == + == ·@$$$$@$ x$@@@@$$$$$$$$@% x$@@@@$$$$$$$$@· == + == ·@$$$$$@$~ x%@@@@$$$$$$$$$$$$$@%· x%@@@@$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == $@$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$@$ == + ==+o +@@@$$$$$$@@@@%**%@@@@$$$$$$@@@@$***%@@@@$$$$$$@@@@+ o+== + ++== ·=$@@@@@%x o*@@@@@@$= =$@@@@@@*x ==++ + ===+ +=== + ++====x+ x+****+x ****++ =+====++ + ++=====**%****%==========********==========**%****%*======++ + ++++======++++ ++++========++ ++++========++xx + + diff --git a/src/build/framegen/frames/frame_099.txt b/src/build/framegen/frames/frame_099.txt new file mode 100644 index 0000000000..624e9eea2d --- /dev/null +++ b/src/build/framegen/frames/frame_099.txt @@ -0,0 +1,41 @@ + + +++===********===+++ + ++==*%**++~· ·~++**%*==+x + xx**=%+x x+%=**xx + ++*=x ~ox+====+xo~ x=*++ + **++ x%@@@@@@@@@@@@@@@@@@@@%x ++** + **+o +$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$+ o+** + =*+o x$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$x ~+*= + ++++ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ++++ + xx== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==xo + ==+· $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ·+== + ox== @$$$$@@@@@@$$$$$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$$@ ==xo + +++x @$$$$@$o +$@@@$$$$$$$$$$$$$$@$o +$@@@$$$$$$$$$$$$@ x+++ + == *$$$$$$ +$@@@$$$$$$$$$@% +$@@@$$$$$$$$$* == + == @$$$$$$ +$@@$$$$$$$$ =$@@$$$$$$@ == + == $$$$$$$@%o x$$$$$$$@@%o +$$$$$$$ == + == ·@$$$$$$$@@@@+ $$$$$$$$$@@@@x $$$$$$@· == + == ·@$$$$$$@*~ +$$$$$$$@@*~ =$$$$$$@· == + == ·@$$$$$$ ·=$@@$$$$$$$% ~*@@@$$$$$$@· == + == ·@$$$$$$ =@@@@$$$$$$$$$@% ·=@@@@$$$$$$$$$@· == + == ·@$$$$$@@x·~=$@@@$$$$$$$$$$$$$$@$x·~=@@@@$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@@% == + ==+x ~$@@@@$$$@@@@$=xx+%@@@@$$$$@@@@@*+xx=$@@@@$$$$@@@@%~ x+== + ++== o*$@@$%=· +*$@@@$*x ~=%@@@$%+ ==++ + ==== ~·++== + xx====+= ++**=*+= x+====+= x=+====xx + ++====*%%%***=====++====%%%%%**=====++====*%*%%*%*====++ + xx++++===+++++ xx++++====++++ xx++++====++++ + + diff --git a/src/build/framegen/frames/frame_100.txt b/src/build/framegen/frames/frame_100.txt new file mode 100644 index 0000000000..c66e0879c8 --- /dev/null +++ b/src/build/framegen/frames/frame_100.txt @@ -0,0 +1,41 @@ + + +++===********===+++ + ++==*%**++~· ·~++**%*==+x + xx**=%+x x+%=**xx + ++*=x ~ox+====+xo~ x==++ + **++ +%@@@@@@@@@@@@@@@@@@@@%+ ++** + **+o +$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$+ o+** + ==+~ x$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$x ~+*= + ++++ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ++++ + xx== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==xo + ==+· $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ·+== + ox== @$$$$@@@@@@$$$$$$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$$@ ==xo + +++x @$$$$$%· o*@@@@$$$$$$$$$$$$$@% o%@@@@$$$$$$$$$$$@ x+++ + == *$$$$@$ o%@@@@$$$$$$$$@% x%@@@@$$$$$$$$* == + == @$$$$$$ x%@@$$$$$$$$ x%@@$$$$$$@ == + == $$$$$$$@$+ ~$$$$$$$@@$x o$$$$$$$ == + == ~@$$$$$$$@@@@+ $$$$$$$$$@@@@x $$$$$$@~ == + == ·@$$$$$@@= *$$$$$$$@@+ *$$$$$$@· == + == ·@$$$$$$ o%@@@$$$$$$$% o%@@@$$$$$$@· == + == ·@$$$$$$ o*@@@@$$$$$$$$$$% o%@@@@$$$$$$$$$@· == + == ·@$$$$$@@=ox*@@@@$$$$$$$$$$$$$$@@=ox*@@@@$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@+ == + ==++ *@@@@@@@@@@@=~ x%@@@@@@@@@@@%x ~=@@@@@@@@@@@@= ++== + xx==x· o=***+~ x=***=x ~+****+~ ·x==++ + ====xo +o xx xx==== + ======+x o=+=====*+x =+=*====++ ++====== + ++==***%******==++++==***%********++++==************==++ + ++++++++++ ++++++++++ ++++++++++++ + + diff --git a/src/build/framegen/frames/frame_101.txt b/src/build/framegen/frames/frame_101.txt new file mode 100644 index 0000000000..63dc3de2c7 --- /dev/null +++ b/src/build/framegen/frames/frame_101.txt @@ -0,0 +1,41 @@ + + xx+===********===+xx + ++==*%**++~· ·~++**%*==+x + xx**=*+x x+*=**xx + ++*=xo ·ox+====+xo· ox=*++ + **++ x%@@@@@@@@@@@@@@@@@@@@%x ++** + **+o +$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$+ o+** + =*+o x$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$x o+*= + ++++ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ++++ + xx== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==xo + ==+· $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ·+== + ox== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@@ ==xo + +++x @$$$$$% ~*@@@@$$$$$$$$$$$$$@* o*@@@@$$$$$$$$$$$@ x+++ + == *$$$$@% o*@@@@$$$$$$$$@% o%@@@@$$$$$$$$* == + == @$$$$$$ o%@@$$$$$$$$ o%@@$$$$$$@ == + == $$$$$$$@$+ ·$$$$$$$$@$+ ~$$$$$$$ == + == ·@$$$$$$$@@@@+ $$$$$$$$$@@@@x $$$$$$@· == + == ·@$$$$$@@+ *$$$$$$$@@+ %$$$$$$@· == + == ·@$$$$$$ x%@@@$$$$$$$% x%@@@$$$$$$@· == + == ·@$$$$$$ o%@@@@$$$$$$$$$$% x%@@@@$$$$$$$$$@· == + == ·@$$$$$@@=xx%@@@@$$$$$$$$$$$$$$@@=x+%@@@@$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ~@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@o == + ++++ +@@@@@@@@@@$+ ·=@@@@@@@@@@@*~ o%@@@@@@@@@@$x ++++ + ox==+~ x+=+o x+=+x· o+==+o ~+==+x + ++==+x ++ == ++==++ + ++====++ x+*+==**==++ ox=+==***=+* =+==**++ + ++==**********==++xx++**********==++++++************++++ + ++++++xx ++++++xx xx++++xx + + diff --git a/src/build/framegen/frames/frame_102.txt b/src/build/framegen/frames/frame_102.txt new file mode 100644 index 0000000000..a53995bece --- /dev/null +++ b/src/build/framegen/frames/frame_102.txt @@ -0,0 +1,41 @@ + + xx+===********===+xx + +++=****+=o· ·o==****=++x + x+=*=*+x x+*=*=+x + ++**+x ~x++==++x~ x+**++ + **++ x*@@@@@@@@@@@@@@@@@@@@*x ++** + **xx x%@@@@$$$$$$$$$$$$$$$$$$$$@@@@%x xx** + ==+o o$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$o o+== + ++++ +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ++++ + xx== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==xx + ==+· $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ·+== + ox== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@@ ==xo + +++x @$$$$@% ~*@@@@$$$$$$$$$$$$$@* ~*@@@@$$$$$$$$$$$@ x+++ + == *$$$$@$ ~*@@@@$$$$$$$$@% o*@@@@$$$$$$$$* == + == @$$$$$$ o%@@$$$$$$$$ o%@@$$$$$$@ == + == $$$$$$$@$=· ·$$$$$$$$@$+ ~$$$$$$$ == + == ~@$$$$$$$@@@@+ $$$$$$$$$@@@@x $$$$$$@~ == + == ·@$$$$$@@+ *$$$$$$$@@+ %$$$$$$@· == + == ·@$$$$$$ x%@@@$$$$$$$% x$@@@$$$$$$@· == + == ·@$$$$$$ x%@@@@$$$$$$$$$$% x%@@@@$$$$$$$$$@· == + == ·@$$$$$@@=x+%@@@@$$$$$$$$$$$$$$@@=x+%@@@@$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==x· @@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@ ·x== + ++++ ~%@@@@@@@@@%~ o%@@@@@@@@@$+ =@@@@@@@@@@%~ ++++ + ox==+x oo~ ~oo· ~oo~ x+==xo + x+==++ ++** %% ++==++ + ++=*==+*xx ~x++*=**=*===*+x ++*=**==**+*++ x+*===*=++ + ++==********++xx ++==********==++ ++==********==++ + + + diff --git a/src/build/framegen/frames/frame_103.txt b/src/build/framegen/frames/frame_103.txt new file mode 100644 index 0000000000..04ce9a0054 --- /dev/null +++ b/src/build/framegen/frames/frame_103.txt @@ -0,0 +1,41 @@ + + x++====******====++x + ++==***%==xo ox==%***==+x + xx=*=*++ ++*=*=xx + ++**++ ~ox++++xo~ ++**++ + **++ o*$@@@@@@@@@@@@@@@@@@$*o ++** + **+x o%@@@@$$$$$$$$$$$$$$$$$$$$@@@@%o x+** + ==+o ~$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$~ o+== + ++++ +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ++++ + xx== o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ==xo + ==+~ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ~+== + ox== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@@ ==xo + +++x @$$$$$% ~*@@@@$$$$$$$$$$$$$@* o*@@@@$$$$$$$$$$$@ x+++ + ==+ *@$$$@% o*@@@@$$$$$$$$@% o%@@@@$$$$$$$@* x== + == @$$$$$$ o%@@$$$$$$$$ o%@@$$$$$$@ == + == $$$$$$$@$= ·$$$$$$$$@$+ ~$$$$$$$ == + == ·@$$$$$$$@@@@+ $$$$$$$$$@@@@x $$$$$$@· == + == ·@$$$$$@@+ *$$$$$$$@@+ %$$$$$$@· == + == ·@$$$$$$ x%@@@$$$$$$$% x$@@@$$$$$$@· == + == ·@$$$$$$ o%@@@@$$$$$$$$$$% x%@@@@$$$$$$$$$@· == + == ·@$$$$$@@=xx%@@@@$$$$$$$$$$$$$$@@=x+%@@@@$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==+· @@@$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@@@@@@@@$$$$$$$$$@@@ ·+== + ++++ %@@@@@@@@@*· *@@@@@@@@@$x x$@@@@@@@@@* ++++ + ==+x ·· ·· ·· x+== + x+==++ +=%% +%*+ ++==++ + xx==**=*++xxxx*=*=**==**=*++xxxx+=*=**==**=*=+xxxx++*+**==++ + ++++******==++ xx++*******=++++ ++++=*******++++ + + + diff --git a/src/build/framegen/frames/frame_104.txt b/src/build/framegen/frames/frame_104.txt new file mode 100644 index 0000000000..6447b659ba --- /dev/null +++ b/src/build/framegen/frames/frame_104.txt @@ -0,0 +1,41 @@ + + xx++=====**=====++xx + ++++***%**++~· ·~++**%***+++x + xx==*%=+ +=%*==xx + ++**++ ~~oooo~~ ++**++ + ==== ·+%@@@@@@@@@@@@@@@@@@%+· ==== + **+x ~*@@@@@$$$$$$$$$$$$$$$$$$@@@@@*~ x+** + ==+x ·%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@%· x+== + ++=+ x@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x +=++ + xx== ·@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@· ==xo + ==+~ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ~+== + ox== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@@ ==xo + +++x $$$$$@%· o%@@@@$$$$$$$$$$$$$@%· x%@@@@$$$$$$$$$$$$ x+++ + ==+· =@$$$@$ x%@@@@$$$$$$$$@% x%@@@@$$$$$$$@= ·+== + == @$$$$$$ x%@@$$$$$$$$ x$@@$$$$$$@ == + == $$$$$$$@$+ ~$$$$$$$@@$x o$$$$$$$ == + == ~@$$$$$$$@@@@+ $$$$$$$$$@@@@x $$$$$$@~ == + == ·@$$$$$$@= =$$$$$$$@@= *$$$$$$@· == + == ·@$$$$$$ o%@@@$$$$$$$% o%@@@$$$$$$@· == + == ·@$$$$$$ o*@@@@$$$$$$$$$$% o*@@@@$$$$$$$$$@· == + == ·@$$$$$@@=oo*@@@@$$$$$$$$$$$$$$@@+ox*@@@@$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+· $@@$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@@@@@@@@$$$$$$$$$@@$ ·x== + +++= =@@@@@@@@@= +$@@@@@@@@%o ~%@@@@@@@@$= ++++ + ==++ +=== + xx=*=+ =*%% ++%%++ +=**+x + ++**=*=+++++******++**=%*=++++==*=**++**=*==++++++*=**++ + xx++======++++ ++======++++ ++++==**==++xx + + + diff --git a/src/build/framegen/frames/frame_105.txt b/src/build/framegen/frames/frame_105.txt new file mode 100644 index 0000000000..3a119586e9 --- /dev/null +++ b/src/build/framegen/frames/frame_105.txt @@ -0,0 +1,41 @@ + + xx++============++xx + ++++**%%**==xo~~~~ox==**%%**+++x + xx==**== ==**==xx + ++**+= ·~~~~· =+**++ + ===+ x*$@@@@@@@@@@@@@@@@$*x +=== + **++ =@@@@@$$$$$$$$$$$$$$$$$$@@@@@= ++** + ==++ *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ++== + ++=+ ~@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@~ +=++ + xx== @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xx + ==+o *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* o+== + ox== $@$$$@@@@@@$$$$$$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@$ ==xo + ++++ $@$$$@$o +$@@@$$$$$$$$$$$$$$@$~ +$@@@$$$$$$$$$$$@$ x+++ + ==+· +@$$$$$ +$@@@@$$$$$$$$@% +$@@@$$$$$$$$@+ ·+== + == @$$$$$$ +$@@$$$$$$$$ +$@@$$$$$$@ == + == $$$$$$$@%o x$$$$$$$@@%o x$$$$$$$ == + == ·@$$$$$$$@@@@+ $$$$$$$$$@@@@x $$$$$$@· == + == ·@$$$$$$@*~ +$$$$$$$@@*~ =$$$$$$@· == + == ·@$$$$$$ ·*@@@$$$$$$$% ~*@@@$$$$$$@· == + == ·@$$$$$$ ·=@@@@$$$$$$$$$@% ~*@@@@$$$$$$$$$@· == + == ·@$$$$$@@+·~=@@@@$$$$$$$$$$$$$$@$x·~=@@@@$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+· %@@$$$$$$$$$@@@@$@@@@@$$$$$$$$@@@@$$@@@@$$$$$$$$$@@$ ·+== + ++== =$@@@@@@@$= x%@@@@@@@@%~ *@@@@@@@@$+ ==+x + ==++ ++== + ===+ ==*%+x ++%%=+ +=== + ++**=%==++++*=**==++**=*==++++***=**++******++++==%=**++ + ++======++++ ++======++++ ++++======++ + + + diff --git a/src/build/framegen/frames/frame_106.txt b/src/build/framegen/frames/frame_106.txt new file mode 100644 index 0000000000..d472c438fa --- /dev/null +++ b/src/build/framegen/frames/frame_106.txt @@ -0,0 +1,41 @@ + + ++++++====++++++ + xx+=***%%%**==++++==**%%%***=+xx + ox==**=*x ox*=**==xo + ++**+= =+**++ + ==== ~+%$@@@@@@@@@@@@@@$%+~ ==== + ===+ x%@@@@@$$$$$$$$$$$$$$$$@@@@@%x +=== + ===+ +@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@+ +=== + ++=+ %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% +=++ + ox== %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ==xx + ==+x x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x x+== + xx== %@$$$$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$@% ==xo + ++++ %@$$$@@+~o=@@@@$$$$$$$$$$$$$$@@+~o*@@@@$$$$$$$$$$$@% ++++ + ==+· x@$$$$$ ~*@@@@$$$$$$$$$$% o*@@@@$$$$$$$$@x ·+== + == @$$$$$$ ~*@@@$$$$$$$% o%@@@$$$$$$@ == + == $$$$$$$@=· =$$$$$$$@@= *$$$$$$$ == + == ·@$$$$$$$@@@@+ $$$$$$$$$@@@@x $$$$$$@· == + == ·@$$$$$$@$x o$$$$$$$@@$x x$$$$$$@· == + == ·@$$$$$$ x$@@$$$$$$$$ +$@@$$$$$$@· == + == ·@$$$$@$ x%@@@@$$$$$$$$@% x$@@@@$$$$$$$$@· == + == ·@$$$$$@$~ x%@@@@$$$$$$$$$$$$$@%· x%@@@@$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+· %@@@$$$$$$$$@@@$$$@@@@$$$$$$$$@@@@$$$@@@$$$$$$$$@@@% ·+== + x+== +$@@@@@@@$+ o%@@@@@@@@*~ =$@@@@@@@$+ ==xx + ===+ +=== + ==== ==%%++ ++%%== ==== + ++**=***++==%***==++***%**++++*=%**=++==***%==++**%=**++ + x+++==++++ x+++++==++xx ++++++++++ + + + diff --git a/src/build/framegen/frames/frame_107.txt b/src/build/framegen/frames/frame_107.txt new file mode 100644 index 0000000000..0d5034d4fc --- /dev/null +++ b/src/build/framegen/frames/frame_107.txt @@ -0,0 +1,41 @@ + + ++++++++++++++++ + ++*****%*%=******=%*%*****++ + ++**=*+x x+*=**++ x + ++**+= =+**++ + ====ox o=%@@@@@@@@@@@@@@%=o xo==== + ===+ ~*@@@@@@$$$$$$$$$$$$$$@@@@@@*~ +=== + ===+ o$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$o +=== + ++== *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ==++ + ox== *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ==xo + ==+x o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o x+== + ox== =@$$$$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$@= ==xo + ++++ *@$$$@@*x+%@@@@$$$$$$$$$$$$$$@@*x+%@@@@$$$$$$$$$$$@* ++++ + ==x· ~@$$$$$ x%@@@@$$$$$$$$$$% x$@@@@$$$$$$$$@~ ·x== + == @$$$$$$ x$@@@$$$$$$$% +$@@@$$$$$$@ == + == $$$$$$@@+ %@$$$$$$@@x %$$$$$$$ == + == ·@$$$$$$$@@@@+ $$$$$$$$$@@@@x @$$$$$@· == + == ·@$$$$$$@$=· $$$$$$$$@$=· ~$$$$$$@· == + == ·@$$$$$$ ~*@@$$$$$$$$ o%@@$$$$$$@· == + == ·@$$$$@% ~*@@@@$$$$$$$$@% ~*@@@@$$$$$$$$@· == + == ·@$$$$$@* ·=@@@@$$$$$$$$$$$$$@* ~*@@@@$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$@@ == + ==+~ %@@@$$$$$$$@@@@$$$@@@@$$$$$$$$@@@@$$$@@@$$$$$$$$@@@% ·+== + xx== x%@@@@@@@$+ ~%@@@@@@@@*~ =$@@@@@@@$+ ==xx + ===+ +=== + ====x ==%%++ ++%%== ==== + ++***%**====%***++++==*%**====%***==++++***%==+=***=**++ + xx++++++++ ++==++++ ++++++++xx + + + diff --git a/src/build/framegen/frames/frame_108.txt b/src/build/framegen/frames/frame_108.txt new file mode 100644 index 0000000000..0b07a3b320 --- /dev/null +++ b/src/build/framegen/frames/frame_108.txt @@ -0,0 +1,41 @@ + + xx++++++++++++xx + ++==***%*%*%%%%%%*%*%***==++ + ++**=*== ==*=**++ + ++**=*++ ++*=**++ + ++**++ ~+*%@@@@@@@@@@%*+~ ++**++ + ==== o*@@@@@@@$$$$$$$$$$@@@@@@@*o ==== + ==== =@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@= ==== + ++== x@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x ==++ + xx==x· x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ·x==xx + ++++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++++ + ox== o@$$$$$@@$$$$$$$$$$$$$$$$$$$$$$@@$$$$$$$$$$$$$$$@o ==xo + ++++ x@$$$$@$%%@@@@$$$$$$$$$$$$$$$$@$%%@@@@$$$$$$$$$$$$@x ++++ + ==+~ @$$$$$ o%@@@@$$$$$$$$$$$$ o%@@@@$$$$$$$$$@ ~+== + == $$$$$@$ o%@@@$$$$$$$@% x%@@@$$$$$$$$ == + == @$$$$$@% x@$$$$$$$@* x@$$$$$$@ == + == ·@$$$$$$$@@@*· $$$$$$$$@@@@* $$$$$$@· == + == ·@$$$$$$@@$+· $$$$$$$$@@$+ $$$$$$@· == + == ·@$$$$$$x o%@$$$$$$$$o x$@$$$$$$@· == + == ·@$$$$@% o%@@@@$$$$$$$@% x%@@@@$$$$$$$@· == + == ·@$$$$$$x o*@@@@$$$$$$$$$$$$$o o%@@@@$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$@@ == + ==+~ %@@@$$$$$$$@@@@$$$@@@@$$$$$$$$@@@@$$$@@@$$$$$$$$@@@% ·+== + ox== x%@@@@@@@$+ ~*@@@@@@@@*~ =$@@@@@@@$+ ==+x + ++=+ +=++ + ++==ox ==%%++ ++%%== o==++ + x+***%**====%***++++==***%====%***==++++***%====**%***++ + +++++++x ++++++++ ++++++++ + + + diff --git a/src/build/framegen/frames/frame_109.txt b/src/build/framegen/frames/frame_109.txt new file mode 100644 index 0000000000..c187dd596a --- /dev/null +++ b/src/build/framegen/frames/frame_109.txt @@ -0,0 +1,41 @@ + + xx++++++++++++xx + ++==****%%*%%%%%%*%%****==++ + ++****=*++ ++*=****++ + ++=*==+= =+====++ + ++**++ ~+=%$$@@@@$$%=+~ ++**++ + ++==oo +%@@@@@@@@$$$$$$@@@@@@@@%+ oo==++ + ++== o$@@@@$$$$$$$$$$$$$$$$$$$$$$@@@@$o ==++ + ++==~ %@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@% ~==++ + ox==+~ $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ ~+==xo + ++++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++++ + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ++++ ~@$$$$@@@@@@@$$$$$$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$$@~ ++++ + ==+~ @$$$$$~ =@@@@$$$$$$$$$$$$$· ·=@@@@$$$$$$$$$$@ ~+== + == $$$$$@% ·=$@@@$$$$$$$@% ·*@@@@$$$$$$$$ == + == @$$$$$$= ·*@$$$$$$$$+ ~*@$$$$$$@ == + == $$$$$$$@@@*x $$$$$$$$@@@*o $$$$$$$ == + == ·@$$$$$$@@@$+ $$$$$$$$@@@%x $$$$$$@· == + == ·@$$$$$@* =@$$$$$$$@= =@$$$$$$@· == + == ·@$$$$@$ +$@@@$$$$$$$@% +$@@@$$$$$$$@· == + == ·@$$$$$$ +$@@@$$$$$$$$$$$$$ +$@@@$$$$$$$$$$$@· == + == ·@$$$$$$@@$$@@@@$$$$$$$$$$$$$$$$@@$$@@@$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$@@ == + ==+~ %@@@$$$$$$$$@@@$$$@@@@$$$$$$$$@@@@$$$@@@$$$$$$$$@@@% ·+== + xx== +%@@@@@@@$= ~%@@@@@@@@%~ =$@@@@@@@$+ ==xx + ++=+ +=++ + ++==ox ==%%++ ++%%== o==++ + x+***%**====%***++++==***%====%***==++++***%====**%***++ + ++++++++ ++++++++ ++++++++ + + + diff --git a/src/build/framegen/frames/frame_110.txt b/src/build/framegen/frames/frame_110.txt new file mode 100644 index 0000000000..0ecf74c83f --- /dev/null +++ b/src/build/framegen/frames/frame_110.txt @@ -0,0 +1,41 @@ + + ++++++++++++ + ++++==******%%%*******==++++ + ++==**=***++ ++******==++ + ++==**=*+o o+*=**==++ + ++====x ·o++====++o· x====++ + ++==++ +%@@@@@@@@@@@@@@@@@@@@%+ ++==++ + ++==xo +$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$+ ox==++ + ++==+~ x@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x ~+==++ + ==+x =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= x+== + ++== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==++ + ==x· $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ·x== + ++== @$$$$@@@@@@$$$$$$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$$@ ==++ + +++x @$$$$$* ·=@@@@$$$$$$$$$$$$$$= ~=@@@@$$$$$$$$$$$@ x+++ + == *$$$$@$ ·*@@@@$$$$$$$$@% ~*@@@@$$$$$$$$* == + == @$$$$$$ ~*@@$$$$$$$$ ~*@@$$$$$$@ == + == $$$$$$$@@=~ $$$$$$$$@@=· ·$$$$$$$ == + == ~@$$$$$$$@@@@x $$$$$$$$$@@@@o $$$$$$@~ == + == ·@$$$$$@@x %$$$$$$$@@x %$$$$$$@· == + == ·@$$$$$$ +$@@@$$$$$$$% +$@@@$$$$$$@· == + == ·@$$$$$$ +$@@@$$$$$$$$$$$% +$@@@$$$$$$$$$$@· == + == ·@$$$$$@@*++%@@@@$$$$$$$$$$$$$$@@*+=%@@@@$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+· %@@@$$$$$$$$@@@@$$@@@@$$$$$$$$@@@@$$@@@@$$$$$$$$$@@$ ·+== + xx== +$@@@@@@@$= o%@@@@@@@@%o =$@@@@@@@$= ==xx + ++=+ +=++ + ++== ==%%++ ++%%== ==++ + x+===***++==%***++x+==*%**====**%*==xx++***%==++***=**++ + ++++++++ ++++++++ ++++++++ + + + diff --git a/src/build/framegen/frames/frame_111.txt b/src/build/framegen/frames/frame_111.txt new file mode 100644 index 0000000000..f9eafa6a49 --- /dev/null +++ b/src/build/framegen/frames/frame_111.txt @@ -0,0 +1,41 @@ + + ++xxxx++ + xx++==****************==++xx + x+==**==*%=*++oo oo++==%*==**==++ + x+++**=*+= =+*=**+++x + ++====++ ~oooo~ ++====++ + ++===+ +%@@@@@@@@@@@@@@@@@@%+ +===++ + ++==+x ·*@@@@@$$$$$$$$$$$$$$$$$$@@@@@*· x+==++ + x+==+x %@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@% x+==+x + ==++ o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@o +=== + ++== ·@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@· ==++ + ==+~ *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ~+== + ++== $@$$$$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$@$ ==++ + +++x $$$$$@@x ·+$@@@$$$$$$$$$$$$$$@$o ~=@@@@$$$$$$$$$$$$$ x+++ + ==x· =@$$$$$ =$@@@$$$$$$$$$@% =$@@@$$$$$$$$@= ·+== + == @$$$$$$ =$@@$$$$$$$% ·=@@@$$$$$$@ == + == $$$$$$$@%o +$$$$$$$@@*~ +$$$$$$$ == + == ·@$$$$$$$@@@@+ $$$$$$$$$@@@@+ $$$$$$@· == + == ·@$$$$$$@%o x$$$$$$$@@%~ +$$$$$$@· == + == ·@$$$$$$ =@@@$$$$$$$$ ·=@@@$$$$$$@· == + == ·@$$$$$$ =$@@@$$$$$$$$$$% =$@@@$$$$$$$$$@· == + == ·@$$$$$@$x ·+$@@@$$$$$$$$$$$$$$@$o ·=$@@@$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@@$$$$$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+· $@@$$$$$$$$$@@@@$@@@@@$$$$$$$$@@@@@$@@@@$$$$$$$$$@@$ ·+== + xx== =$@@@@@@@@* o%@@@@@@@@%o *@@@@@@@@$= ==xx + ++=+ +=++ + ++== ==%%++ ++%%== ==++ + x+**=%*=++++%***=+++==*%**++++**%*==+++=***%++++==%=**++ + ++++++++ ++++++++ ++++++++ + + + diff --git a/src/build/framegen/frames/frame_112.txt b/src/build/framegen/frames/frame_112.txt new file mode 100644 index 0000000000..75ce50d77e --- /dev/null +++ b/src/build/framegen/frames/frame_112.txt @@ -0,0 +1,41 @@ + + + xx++====********====++xx + ++==**=**%****++++****%**=**==++ + ++=====*+x ++*=====++ + ++====== ======++ + ++====xx o=%$@@@@@@@@@@@@$%=o xx====++ + ++===+ ·*$@@@@@$$$$$$$$$$$$$$@@@@@$*· +===++ + xx===+ o$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$o +===xx + ==++ *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ++== + ++== *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ==++ + ==+x ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ x+== + xx== =@$$$$$@@$$$$$$$$$$$$$$$$$$$$$@@@$$$$$$$$$$$$$$$@= ==xx + ++++ *@$$$$@$*%@@@@$$$$$$$$$$$$$$$@@$*%@@@@$$$$$$$$$$$$@* ++++ + ==+· ~@$$$$$ ~*@@@@$$$$$$$$$$$$ ~*@@@@$$$$$$$$$@~ ·+== + == @$$$$$$ ~*@@@$$$$$$$@% o*@@@$$$$$$$@ == + == $$$$$$@%~ o$$$$$$$$@%· x$$$$$$$$ == + == ·@$$$$$$$@@@%~ $$$$$$$$$@@@%· $$$$$$@· == + == ·@$$$$$$@@%+ $$$$$$$$@@%x $$$$$$@· == + == ·@$$$$$$o x$@$$$$$$$$~ +$@$$$$$$@· == + == ·@$$$$$$ x%@@@@$$$$$$$@% +$@@@$$$$$$$$@· == + == ·@$$$$$$+ x%@@@@$$$$$$$$$$$$$x x%@@@@$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==x· $@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@ ·x== + xx== *@@@@@@@@@%~ +$@@@@@@@@$+ ~%@@@@@@@@@* ==xx + ==++ ++== + ===+ ++%% %%++ +=== + ++**=%+++x++**%*=+++===*==++++==*===++++*%**++++++****++ + ++++====++ ++====++ ++====++++ + + + diff --git a/src/build/framegen/frames/frame_113.txt b/src/build/framegen/frames/frame_113.txt new file mode 100644 index 0000000000..ee9846d6fb --- /dev/null +++ b/src/build/framegen/frames/frame_113.txt @@ -0,0 +1,41 @@ + + + xx++++============++++xx + ++====**=**%%%****%%%**=**====++ + ++====**=*o~ ~o*=**====++ + ======++ ++====== + x+====++ ~+*%$@@@@@@@@$%*+~ ++====++ + xx==== o%@@@@@@@$$$$$$$$$$@@@@@@@%o ====xx + ==== =@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@= ==== + =+== o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@o ==++ + x+==x· x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ·x==+x + ==++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++== + xx== o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ==xx + ++++ x@$$$$@@@@@@@$$$$$$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$$@x ++++ + ==+~ @$$$$$o ·*@@@@$$$$$$$$$$$$$~ ~*@@@@$$$$$$$$$$@ ~+== + == $$$$$@% ~*@@@@$$$$$$$@* ~*@@@@$$$$$$$$ == + == @$$$$$$+ ~*@$$$$$$$$x o*@$$$$$$@ == + == ·@$$$$$$@@@*o $$$$$$$$@@$*~ @$$$$$@· == + == ·@$$$$$$@@@$+ $$$$$$$$@@@$+ @$$$$$@· == + == ·@$$$$$@* +@$$$$$$$@* =@$$$$$$@· == + == ·@$$$$@% +$@@@$$$$$$$@% +$@@@$$$$$$$@· == + == ·@$$$$$$ x%@@@@$$$$$$$$$$$$ +$@@@$$$$$$$$$$$@· == + == ·@$$$$$$@@$$@@@@$$$$$$$$$$$$$$$$@@$$@@@@$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==x· @@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@ ·x== + xx++ ·%@@@@@@@@@%o =@@@@@@@@@@= o%@@@@@@@@@%~ ++++ + ==+x ·~~ ·~~· ~~~ x+== + ==++ x+%% %%++ +=== + ++**=%++ooox==*===++**=%++xoox++%=**++===*==xooo++%=**++ + ++++==++++ x+++====++xx ++++====++ + + + diff --git a/src/build/framegen/frames/frame_114.txt b/src/build/framegen/frames/frame_114.txt new file mode 100644 index 0000000000..396d5a292c --- /dev/null +++ b/src/build/framegen/frames/frame_114.txt @@ -0,0 +1,41 @@ + + + xx++++++++++++++++++ + ++++==****=***%%%%***=****==++++ + ++++=====%==o~ ~o==%=====++++ + ++====+= =+====++ + ====++ ~x=**%%%%**=x~ +===== + ====++ o*$@@@@@@@@@@@@@@@@@@@@$*o ++==== + ====o~ ·*@@@@$$$$$$$$$$$$$$$$$$$$$$@@@@*· ~o==== + ++==x· =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= ·x==++ + xx==+o %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% o+==xx + =+++ *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* +++= + xx==x· @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ·x==xo + ++++ @$$$$@@@@@@$$$$$$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$$@ ++++ + ==+o @$$$$@$~ x%@@@@$$$$$$$$$$$$$@%· x%@@@@$$$$$$$$$$$@ o+== + == %$$$$$$ x%@@@@$$$$$$$$@% x%@@@@$$$$$$$$% == + == @$$$$$$ x%@@$$$$$$$$ +$@@$$$$$$@ == + == $$$$$$$@%+ o$$$$$$$@@%x x$$$$$$$ == + == ·@$$$$$$$@@@@+ $@$$$$$$$@@@@+ $$$$$$@~ == + == ·@$$$$$@@=· =$$$$$$$@@= *$$$$$$@· == + == ·@$$$$$$ ~*@@@$$$$$$$% o*@@@$$$$$$@· == + == ·@$$$$$$ ~*@@@@$$$$$$$$$$% ~*@@@@$$$$$$$$$@· == + == ·@$$$$$@@+~o*@@@@$$$$$$$$$$$$$$@@+~o*@@@@$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==x ·@@$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@~ == + x+++ x$@@@@@@@@@@= o%@@@@@@@@@@%o =@@@@@@@@@@$x +=++ + ==+o ox++o ~x++x~ o++xo o+== + **+x ** ** x+** + ++**==o~ ++*+**++**+*xo ox*+**++**=*++ ~o=+**=+ + ++==***=++++ ++==****==++ x+++****==++ + + + diff --git a/src/build/framegen/frames/frame_115.txt b/src/build/framegen/frames/frame_115.txt new file mode 100644 index 0000000000..f5d198a488 --- /dev/null +++ b/src/build/framegen/frames/frame_115.txt @@ -0,0 +1,41 @@ + + + ++++++++++++++++ + ++++==****************==++++ + ++===****%++~~ ~~++%****===++ + ++=====*+x x+*=====++ + ++====xx ·ox+====+xo· xx====++ + ====++ x%$@@@@@@@@@@@@@@@@@@$%x ++==== + ++==+x x$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$x x+==++ + ++==+o x$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$x o+==++ + ox==++ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ++==xo + +++= x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x =+++ + ==+· $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ·+==xo + ++== @@$$$$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$@@ ==++ + ==+x @$$$$$@*x+%@@@@$$$$$$$$$$$$$$@@*x+%@@@@$$$$$$$$$$$$@ x+== + == *$$$$$$ x%@@@@$$$$$$$$$$% x%@@@@$$$$$$$$$* == + == @$$$$$$ x%@@@$$$$$$@% +$@@@$$$$$$@ == + == $$$$$$@@+ *$$$$$$$@@x %$$$$$$$ == + == ~@$$$$$$$@@@@x $$$$$$$$$@@@@x $$$$$$@~ == + == ·@$$$$$$@@=· $$$$$$$$@$= ~$$$$$$@· == + == ·@$$$$$$ ~*@@$$$$$$$$ o*@@$$$$$$@· == + == ·@$$$$@$ ~*@@@@$$$$$$$$@% ~*@@@@$$$$$$$$@· == + == ·@$$$$$@* ~*@@@@$$$$$$$$$$$$$@* ~*@@@@$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == o@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@x == + ++++ =@@@@@@@@@@@%o +$@@@@@@@@@@$+ o%@@@@@@@@@@@* ++++ + **x~ ~+===+o x====x o+=*=+~ ~+** + **+x ++ ++ x+** + ==**++ ~o*+**=***+= =+**==**+*o~ ++==== + +==****==+++ ++=******=++ ++===*%**==+ + + + diff --git a/src/build/framegen/frames/frame_116.txt b/src/build/framegen/frames/frame_116.txt new file mode 100644 index 0000000000..0d845be93e --- /dev/null +++ b/src/build/framegen/frames/frame_116.txt @@ -0,0 +1,41 @@ + + + ++++++++ + ++++==************==++++ + ++++****=**%=+xo~~~~ox+=***=****++++ + xx++===*== =+*===++xx + ++====++ ·~~~~· ++====++ + ++===+ x*$@@@@@@@@@@@@@@@@$*x +===++ + ++==++ =@@@@@$$$$$$$$$$$$$$$$$$@@@@@= ++==++ + x+===+ *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* +===+x + ==++ ~$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$~ ++== + ++== @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==++ + ==+o *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* o+== + ++== $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ==++ + +++x $@$$$$@@@@@@@$$$$$$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$$@$ x+++ + ==x· +@$$$$$x o%@@@@$$$$$$$$$$$$$o x%@@@@$$$$$$$$$$@+ ·x== + == @$$$$@% o%@@@@$$$$$$$@* x%@@@@$$$$$$$@ == + == $$$$$$$o o%@$$$$$$$$~ x%@$$$$$$$ == + == ·@$$$$$$@@$+ $$$$$$$$@@%+ $$$$$$@· == + == ·@$$$$$$@@@@* $$$$$$$$@@@$* $$$$$$@· == + == ·@$$$$$@* x@$$$$$$$@* x@$$$$$$@· == + == ·@$$$$@% o%@@@$$$$$$$@* x%@@@$$$$$$$@· == + == ·@$$$$$$ o%@@@@$$$$$$$$$$$$ x%@@@@$$$$$$$$$$@· == + == ·@$$$$$$@@%$@@@@$$$$$$$$$$$$$$$$@@%$@@@@$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$@@$$$$$$$$$$$$$$$$$$$$$$@@$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == =@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@* == + +++x $@@@@@$$@@@@@*x~o+%@@@@@$$@@@@@%+o~o=$@@@@$$$@@@@$· x+++ + **~· ~=%$$$%=o +*$$$$*+ o=%$$$%=~ ** + xx**o~ ~·**xx + ox**+* ++****=*+x x+*=****++ *+**xx + ++==*%%%%%**== x++=*%%%%%%*=++x ++**%%%%%*==++ + + + diff --git a/src/build/framegen/frames/frame_117.txt b/src/build/framegen/frames/frame_117.txt new file mode 100644 index 0000000000..591cfaea9c --- /dev/null +++ b/src/build/framegen/frames/frame_117.txt @@ -0,0 +1,41 @@ + + + + ++++====********====++++ + ++==**=**%**++++++++**%**=**==++ + ++=*=*=* *=*=*=++ + x+====+= =+====+x + ++===+ ·+%$@@@@@@@@@@@@@@$%+· +===++ + x+==++ x%@@@@@$$$$$$$$$$$$$$$$@@@@@%x ++==+x + ox==++ +@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@+ ++==xx + ==++ %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ++== + ++== $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ ==++ + ==+x +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ x+== + ++== %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ==++ + ++++ %@$$$$$@@@$$$$$$$$$$$$$$$$$$$$$@@@$$$$$$$$$$$$$$$$@% ++++ + ==+· x@$$$$@@%=*@@@@@$$$$$$$$$$$$$$@@%**@@@@$$$$$$$$$$$$$@x ·+== + == @$$$$$% +$@@@@$$$$$$$$$$% +$@@@@$$$$$$$$$@ == + == $$$$$@* o*@@@$$$$$$@+ o*@@@$$$$$$$ == + == ·@$$$$$@%· x$$$$$$$@* x$$$$$$@· == + == ·@$$$$$$@@@@* *@$$$$$$@@@@= %@$$$$@· == + == ·@$$$$$@$o ~$$$$$$$@%~ o$$$$$$@· == + == ·@$$$$@* ·=$@@$$$$$$@= ·=$@@$$$$$$@· == + == ·@$$$$$% o%@@@@@$$$$$$$$$* x%@@@@$$$$$$$$$@· == + == ·@$$$$$@@*+=$@@@@$$$$$$$$$$$$$$@@*+=$@@@@$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@$$$$$$$$$$$$$$$$$$$$$@@@$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == %@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@% == + +++o o@@@@$$$$$@@@@%*+=*$@@@$$$$$$@@@$*=+=%@@@@$$$$$@@@@x o+++ + ** +%@@@@@%+ o*$@@@@$*o +%@@@@@%+ **xo + x+** **++ + x+**++ ox****== ==****xo =+**++ + +=**%%%%%%%*==++ ++==*%%%%%%*==++ x+==*%%%%%%%**++ + + + diff --git a/src/build/framegen/frames/frame_118.txt b/src/build/framegen/frames/frame_118.txt new file mode 100644 index 0000000000..436dfbe127 --- /dev/null +++ b/src/build/framegen/frames/frame_118.txt @@ -0,0 +1,41 @@ + + + + x+++++========++++++ + ++==*****%%%********%%%*****==++ + ++==**=*=+ ++*=**==++ + ++**+= =+**++ + xx====ox ~=%$@@@@@@@@@@@@$%=~ xo====xx + x+===+ ·=@@@@@@$$$$$$$$$$$$$$@@@@@@=· +===+x + ===+ ~%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@%~ +=== + ++== =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= ==++ + x+== =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ==+x + ==+x ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ x+== + xx== =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xx + ++++ =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ++++ + ==+· ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ·+== + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == $$$$$$@@@@@@@@@@@@@@@@@$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$$ == + == ·@$$$$@$+xxxxxxxxxxxxxx=$@$$$@$+xxxxxxxxxxxxxx+%@$$$$$@· == + == ·@$$$@o x$$$x @$$$$@· == + == ·@$$$@~ o@$@~ @$$$$@· == + == ·@$$$$@%o··············o%@$$$@%o··············~*@$$$$$@· == + == ·@$$$$$@@@@@@@@@@@@@@@@@@$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$$$@@$$$$$$$$$$$$$$$@@ == + ==+~ %@@@$$$$$$$$@@@$$$@@@@$$$$$$$$@@@@$$$@@@$$$$$$$$@@@% ·+== + xx== +$@@@@@@@$+ ~*@@@@@@@@*~ +$@@@@@@@$+ ==xx + ++=+ +=++ + ++**oo ==$%++ ++%%== oo==++ + x+==*%**====****++++++*%**====**%*++++++**=*====*=%*==++ + ++++++ ++++++++ ++++++ + + diff --git a/src/build/framegen/frames/frame_119.txt b/src/build/framegen/frames/frame_119.txt new file mode 100644 index 0000000000..a334097c43 --- /dev/null +++ b/src/build/framegen/frames/frame_119.txt @@ -0,0 +1,41 @@ + + + + ++++++====++++++ + xx++*******%*%%**%%*%*******++xx + xx+=****== ==****=+xx + ++**+*x x*+**++ + ====x+ x*%$@@@@@@@@@@$%*x +x==== + ==== +$@@@@@@$$$$$$$$$$$$@@@@@@$+ ==== + ==++ *@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@* ++== + ++== +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ==++ + xx==x· +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ·o==xx + ==++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++== + ox== +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + ++++ +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ++++ + ==+· @$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$$$$$@@@@@@$$$$$$$$$$@ ·+== + == @$$$$$$$@@@@$%==**$@@@@$$$$$$$$@@@@@%*==*%@@@@$$$$$$$@ == + == @$$$$$@@= =@@$$$$$@%o o$@$$$$$@ == + == ·@$$$$@* + *$$$$@ xo o@$$$$@· == + == ·@$$$$* ·x * $$$@ * = =@$$$@· == + == ·@$$$@x @ * x $$$$ x+ % * +@$$$@· == + == ·@$$$$$ + *$$$@ x o@$$$$@· == + == ·@$$$$@$ ·· o+=$@@$$$$@x ~ ·++%@@$$$$$@· == + == ·@$$$$$$@@= =@@@$$$$$$$@@*o @@@$$$$$$$@· == + == ·@$$$$$$$$@@@@@@@@@@@$$$$$$$$$$$$@@@@@@@@@@@$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==x· $@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@ ·x== + xx== *@@@@@@@@@%o =@@@@@@@@@@= o%@@@@@@@@@%· +++x + ==++ ·~· ~~ ·~· x+== + ==++ x+%% %%+x ++== + ++**=*++xox+==%*==++===%==xoox==%===++==*%==+xoo++*=**++ + +++++===++ +++==+++ +++===++++ + + diff --git a/src/build/framegen/frames/frame_120.txt b/src/build/framegen/frames/frame_120.txt new file mode 100644 index 0000000000..8bd15cf1df --- /dev/null +++ b/src/build/framegen/frames/frame_120.txt @@ -0,0 +1,41 @@ + + + + x++++++++++++++x + ++==*****%%%%%%%%%%*****==++ + ++**=*=* *=*=**++ + xx**=*++ ++*=**++ + ++**x+ ~+*$$@@@@@@@@$$*+~ +x**++ + ==== o%@@@@@@@$$$$$$$$$$@@@@@@@%o ==== + ++== =@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@= ==++ + ++== o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@o ==++ + ox==x· x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ·x==xo + ++++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++++ + == o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + ++++ x@$$$$$$$$@@@@@@@@@$$$$$$$$$$$$$$@@@@@@@@@@$$$$$$$@x ++++ + ==+~ @$$$$$$@@@*x· ·x*$@@$$$$$$$$@@@%=~ o=%@@@$$$$$@ ~+== + == $$$$$$@@+ x@@$$$$$@* $@$$$$$$ == + == @$$$$@% ~~o=$x $$$$$@ ·~~x%= +@$$$$@ == + == ·@$$$$$ % = @$$@ =· %$$$$@· == + == ·@$$$@x @ o *x $$$$ ~+ x @ o@$$$@· == + == ·@$$$@+ $ x · @$$$ ·+ + ~ *$$$$@· == + == ·@$$$$$ * $$$$@~ x· +@$$$$@· == + == ·@$$$$$$ ·o%*xxx=$@$$$$$@~ ~=%+ox+%@@$$$$$@· == + == ·@$$$$$$@= x@@@$$$$$$$@$· @@@$$$$$$$@· == + == ·@$$$$$$$@@@$=x~··~x%$$$$$$$$$$$@@@$*+o··~o*$$$$$$$$$$@· == + == ·@$$$$$$$$$$@@@@@@@@@$$$$$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == o@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@x == + ++++ =@@@@@@@@@@@*o ·+$@@@@@@@@@@$+· o*@@@@@@@@@@@* ++++ + **+~ ~+=*=+o ·x=**=x· o+=*=+~ ·+** + **xx ++ ++ o+**xx + ====++ *=**==**+= =+**==**+= ++==== + +==**%*==+++ ++=******=++ +++=**%%*=== + + diff --git a/src/build/framegen/frames/frame_121.txt b/src/build/framegen/frames/frame_121.txt new file mode 100644 index 0000000000..ad287b6288 --- /dev/null +++ b/src/build/framegen/frames/frame_121.txt @@ -0,0 +1,41 @@ + + + + ++++++++++++ + ++++****%%%%%%%%%%%%****++++ + ++***%*%x~ ~x%*%***++ + x+**==++ ++==**++ + ++**++ ·x*%$$@@@@@@$$%*x· ++**++ + ++==~ o*@@@@@@@@$$$$$$$$@@@@@@@@*o ~==++ + ++== =@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@= ==++ + ++== ~$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$~ ==++ + ==x· o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ·x== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + x+++ o@$$$$$$$@@@@@@@@@@@$$$$$$$$$$$$@@@@@@@@@@@$$$$$$$@o ++xx + ==x~ @$$$$$$@@%x o*@@$$$$$$$$@@$=~ ~=$@@$$$$$@ ~x== + == $$$$$$@$· $@$$$$$@+ *@$$$$$$ == + == @$$$$@* ·~·~x$o %$$$$@ o··o%= ~@$$$$@ == + == ·@$$$$$ * = @$$@ % o *@$$$@· == + == ·@$$$@x @ * *+ $$$$ oo * @ o@$$$@· == + == ·@$$$@+ % @$$$ * %$$$$@· == + == ·@$$$$@ + @$$$@x ~o *@$$$$@· == + == ·@$$$$$@ ·x==x=*%@@$$$$$@x o+=x+=%$@@$$$$$@· == + == ·@$$$$$$@%· x@@$$$$$$$$@@x @@$$$$$$$$@· == + == ·@$$$$$$$@@@@%=++++*$$$$$$$$$$$$$@@@$*=+++=%$$$$$$$$$$@· == + == ·@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == =@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@* == + +++x $@@@@$$$@@@@@=x~o+%@@@@@$$@@@@@%+o~x=@@@@@$$$@@@@$ x+++ + **~· ~=%$$$%=o x*$$$$*x o=%$$$%=~ ** + xx**o~ ~~**xx + xx**+= ++******+x x+******++ *+**xx + ++==*%%%%%%*=+ xx==*%%%%%%*=+xx +=*%%%%%%*==++ + + diff --git a/src/build/framegen/frames/frame_122.txt b/src/build/framegen/frames/frame_122.txt new file mode 100644 index 0000000000..538806586d --- /dev/null +++ b/src/build/framegen/frames/frame_122.txt @@ -0,0 +1,41 @@ + + + + x++++++x + ++==***%%%%%%%%%%***==++ + x+==**=*+o o+*=**==+x + ==**++ ++**== + ++**++ x=%$$@@@@@@$$%=x ++**++ + ++**oo ~=@@@@@@@@$$$$$$$$@@@@@@@@=~ oo**++ + ++== +$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$+ ==++ + ++=* ~$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$~ *=++ + ==x~ o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@o ~+== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ == + ++++ o@$$$$$$$@@@@@@@@@@@$$$$$$$$$$$$@@@@@@@@@@@$$$$$$$@o +++x + ++x~ @$$$$$$@@%x o*@@$$$$$$$$@@$=· ·=$@@$$$$$@ ~x++ + == $$$$$$@$ $@$$$$$@x =@$$$$$$ == + == @$$$$@= ~· o%~ *$$$$$ ~ ·*+ @$$$$@ == + == ·@$$$$% = * @$$@ $ + =@$$$@· == + == ·@$$$@o @ ~$ *+ $$$$ xo @ @ x@$$$@· == + == ·@$$$@= = @$$@ % $$$$$@· == + == ·@$$$$@ ~· x@$$$@= x · $@$$$$@· == + == ·@$$$$$@x *@@@@$$$$$@% o$@@@$$$$$$@· == + == ·@$$$$$$@@= x@$$$$$$$$$@@%o @$$$$$$$$$@· == + == ·@$$$$$$$$@@@@@$$$$@@$$$$$$$$$$$$@@@@@@$$$@@$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$@@$$$$$$$$$$$$$$$$$$$$$@@$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$· == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == $@$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$@@ == + +++o =@@@$$$$$$$@@@@%**$@@@@$$$$$$@@@@$**%@@@@$$$$$$$@@@= ~+++ + ox== ~*@@@@@@@*o +$@@@@@@$+ o*@@@@@@@*~ ==xo + ++== ==++ + ++**++ =*%%== ==%%*= +x**++ + +=***%%%%*%=**+x ++***%%%%%%***++ x+**=%*%%%%***=+ + xxxx xxxx xxxx + diff --git a/src/build/framegen/frames/frame_123.txt b/src/build/framegen/frames/frame_123.txt new file mode 100644 index 0000000000..db5c78f458 --- /dev/null +++ b/src/build/framegen/frames/frame_123.txt @@ -0,0 +1,41 @@ + + + + ++++ + ++==***%*%%%%%%*%***==++ + xx==***%+x x+%***==xx + ==**++ ++**== + ++**++ o=*%$$@@@@$$%*=o ++**++ + ++**oo ·=$@@@@@@@$$$$$$$$@@@@@@@$=· oo**++ + ++== +$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$+ ==++ + x+=* ·$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$· *=xx + ==+~ ~@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@~ ~+== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ == + xx++ ~@$$$$$$$@@@@@@@@@@@$$$$$$$$$$$$@@@@@@@@@@@@$$$$$$@o ++xx + +++~ @$$$$$@@@*o ~*@@@$$$$$$$@@%x +%@@$$$$$@ ~+++ + == $$$$$$@% %@$$$$@@o +@$$$$$$ == + == @$$$$@+ o ~*~ =@$$$$ ·~ =+ @$$$$@ == + == ·$$$$$% ·= * @$$@ $ + =@$$$$· == + == ·@$$$@o @ ~@ *x $$$$ x~ @ @ x@$$$@· == + == ·@$$$@* +~ ·@$$@ % $$$$$@· == + == ·@$$$$@ ~ · =@$$$$* x o ~@@$$$$@· == + == ·@$$$$$@+ *@@@@$$$$$@$ ~@@@@$$$$$$@· == + == ·@$$$$$$@@%o +$$$$$$$$$$@@$+ $$$$$$$$$$@· == + == ·@$$$$$$$$@@@@@@@@@@@$$$$$$$$$$$$$@@@@@@@@@@$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+~ %@@$$$$$$$$$@@@@$$@@@@$$$$$$$$@@@@$$@@@@$$$$$$$$$@@% ·+== + xx== +$@@@@@@@@= o%@@@@@@@@%o =@@@@@@@@$+ ==+x + ++=+ +=++ + ++*=o +=%$++ ++$%=+ o==++ + x+==**=*===*%***++xx++*%*%====%*%*++xx++***%*===***===xx + ++++++xx ++++++++ xx++++++ + diff --git a/src/build/framegen/frames/frame_124.txt b/src/build/framegen/frames/frame_124.txt new file mode 100644 index 0000000000..d7a2e4e1a3 --- /dev/null +++ b/src/build/framegen/frames/frame_124.txt @@ -0,0 +1,41 @@ + + + + + ++++***%%$%%%%$%%***++++ + ++***%+x x+%***++ + ++**++ ++**++ + x+**++ o=*%$$@@@@$$%*=o ++**+x + ++**oo ·=$@@@@@@@$$$$$$$$@@@@@@@$=· oo**++ + ++** +$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$+ **++ + x+** ·$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$· **xx + ==+~ ~@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@~ ~+== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ == + xx++ ~@$$$$$$$@@@@@@@@@@@@$$$$$$$$$$$@@@@@@@@@@@@$$$$$$@~ ++xx + +++~ @$$$$$@@$= +$@@$$$$$$$@@*o o*@@$$$$$@ ~+++ + == $$$$$$@* *@$$$$@@· x@$$$$$$ == + == @$$$$@o o = +@$$$% o· xx @$$$$@ == + == ·$$$$$% ~+ * @$$@ $ * +@$$$$· == + == ·@$$$@o @ o@ *x $$$$ x~ @ @ x@$$$@· == + == ·@$$$@* xo o@$$@ $ $$$$$@· == + == ·@$$$$@~ ~ ~o *@$$$$% o + x@$$$$$@· == + == ·@$$$$$@= *@@@@$$$$$@$ ~@@@@$$$$$$@· == + == ·@$$$$$$@@$x +$$$$$$$$$$$@@=· $$$$$$$$$$@· == + == ·@$$$$$$$$@@@@@@@@@@@$$$$$$$$$$$$$@@@@@@@@@@$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==x· @@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@ ·+== + x+++ ~%@@@@@@@@@$x =@@@@@@@@@@= x$@@@@@@@@@%~ ++++ + ===x ~~~ ·~~· ~~~ x+== + ===+ x+%% %%+x +=== + ++**=%+x··~o==*===++***%=+~··~+=%***++===*==o~··x+%=**++ + ++=====+++ xx++=====+xx +=======++ + diff --git a/src/build/framegen/frames/frame_125.txt b/src/build/framegen/frames/frame_125.txt new file mode 100644 index 0000000000..4a14a3f48f --- /dev/null +++ b/src/build/framegen/frames/frame_125.txt @@ -0,0 +1,41 @@ + + + + + ++==***%%%%%%%%%%***==++ + +=***%+o o+%***=+ + +=**++ ++**=+ + xx**++ o=%%$$@@@@$$%%=o ++**xx + x+**oo ~=$@@@@@@@$$$$$$$$@@@@@@@$=~ oo**+x + ++** +$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$+ **++ + xx** ·$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$· **xx + ==+~ ~@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@~ ~+== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ == + xx++ o@$$$$$$@@@@@@@@@@@@@$$$$$$$$$$$@@@@@@@@@@@@$$$$$$@o ++xx + +++~ @$$$$$@@$+ x%@@$$$$$$$@@*· ~*@@$$$$$@ ~+++ + == $$$$$$@* *@$$$$@$ ~@$$$$$$ == + == @$$$$@o o + x@$$$% x o~ @$$$$@ == + == ·$$$$@* ox * $$$@ $ * +@$$$$· == + == ·@$$$@o @ o@ *o $$$$ x~ @ @ x@$$$@· == + == ·@$$$@* ox o@$$@ $ $$$$$@· == + == ·@$$$$@o ~ ox %@$$$$% ~ = +@$$$$$@· == + == ·@$$$$$@* =@@@@$$$$$@@· ·@@@@$$$$$$@· == + == ·@$$$$$$@@$+ +$$$$$$$$$$$@@*o $$$$$$$$$$@· == + == ·@$$$$$$$$$@@@@@@@@@@$$$$$$$$$$$$$@@@@@@@@@@$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==x ·@@$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@~ == + ++++ x@@@@@@@@@@@= o%@@@@@@@@@@%o =@@@@@@@@@@@x ++xx + ==+o ~x+xo ·x++x· ox+xo o+== + **++ == =* x+** + +=**==o· ++*=**==**=*+o o+*=**==**=*+x ·o=+**=+ + ++==****++++ ++==****==++ ++++****==++ + diff --git a/src/build/framegen/frames/frame_126.txt b/src/build/framegen/frames/frame_126.txt new file mode 100644 index 0000000000..4ed857cd9d --- /dev/null +++ b/src/build/framegen/frames/frame_126.txt @@ -0,0 +1,41 @@ + + + + + ++==**%%%%%%%%%%%%**==++ + +=***%+o o+%***=+ + +=**++ ++**=+ + xx**++ ·x=%$$@@@@@@$$%=x· ++**xx + x+**~ ~*@@@@@@@@$$$$$$$$@@@@@@@@*~ ~**+x + ++** +@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@+ **++ + xx** ~$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$~ **xx + ==x· o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ·x== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + xx++ o@$$$$$$@@@@@@@@@@@@@$$$$$$$$$$$@@@@@@@@@@@@$$$$$$@o ++xx + ++x~ @$$$$$@@%o ~*@@$$$$$$@@$+ =@@$$$$$@ ~x++ + == $$$$$$@= =@$$$$@$ @$$$$$$ == + == @$$$$@· o o o@$$$* x @$$$$@ == + == ·@$$$@* xo * $$$@ % % +@$$$@· == + == ·@$$$@o @ o@ =o $$$$ x~ @ @ +@$$$@· == + == ·@$$$$% ·+ x@$$@ $ @$$$$@· == + == ·@$$$$@x ~ x+ ~$@$$$$$ ·* =@$$$$$@· == + == ·@$$$$$@% =@@@@$$$$$@@o @@@@$$$$$$@· == + == ·@$$$$$$@@@*~ =$$$$$$$$$$$@@%x ·$$$$$$$$$$@· == + == ·@$$$$$$$$$@@@@@@@@@@$$$$$$$$$$$$$@@@@@@@@@@$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@x == + ++++ =@@@@@@@@@@@%o ~+@@@@@@@@@@@@+~ o%@@@@@@@@@@@* ++++ + **x~ ~+***=o ·x=**=x· o=***+o ·x** + **+x ++ ++ ox**oo + ====++ *=**==**+= =+**==**=* ++==== + ++=**%%**=++ ++=**%%**=++ ++=**%%*==++xx + diff --git a/src/build/framegen/frames/frame_127.txt b/src/build/framegen/frames/frame_127.txt new file mode 100644 index 0000000000..a6d93b3ea4 --- /dev/null +++ b/src/build/framegen/frames/frame_127.txt @@ -0,0 +1,41 @@ + + + + + +++=*%%%%%%%%%%%%%%*=+++ + +=*%=*x· ·x*=%*=+ + ++**++ ++**++ + x+**++ ~+*%$@@@@@@@@$%*+~ ++**+x + ++** o*@@@@@@@$$$$$$$$$$@@@@@@@*o **++ + xx** =@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@= **xx + xx** o$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$o **xx + =*x· o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ·x*= + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + =* o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + xx++ x@$$$$$$@@@@@@@@@@@@@$$$$$$$$$$@@@@@@@@@@@@@$$$$$$@x ++xx + +++~ @$$$$$@@*~ *@@$$$$$$@@$x x$@$$$$$@ ~+++ + == $$$$$$@+ +@$$$$@% @$$$$$$ == + == @$$$$@ ·~ ~ ~@$$@* + $$$$$@ == + == ·@$$$@= +~ * $$$@ % $ x@$$$@· == + == ·@$$$@o @ o@ =~ $$$$ xo @ $ +@$$$@· == + == ·@$$$$% + +@$$@ % @$$$$@· == + == ·@$$$$@+ += o$@$$$$$ ~%~ ·*@$$$$$@· == + == ·@$$$$$@$ =@@@$$$$$$$@x @@@@$$$$$$@· == + == ·@$$$$$$$@@%x =$$$$$$$$$$$@@$+· ~$$$$$$$$$$@· == + == ·@$$$$$$$$$@@@@@@@@@@$$$$$$$$$$$$$@@@@@@@@@@$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == +@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@+ == + ++++ *@@@@@@@@@@@$+~ ·o*@@@@@@@@@@@@*o ~+$@@@@@@@@@@@% ++++ + **x· x*%%%*+ o=%%%%=o +*%%%*x ·o** + ox**xo xx xx ~o**xx + ===*+x *+****==++ ++==****+* o+*=** + ++==**%%%**=++ ++=*%%%%*=++ ++==*%%%%*==++ + diff --git a/src/build/framegen/frames/frame_128.txt b/src/build/framegen/frames/frame_128.txt new file mode 100644 index 0000000000..7efbabf4eb --- /dev/null +++ b/src/build/framegen/frames/frame_128.txt @@ -0,0 +1,41 @@ + + + + ++++ + ++=**%*%%%%%%%%%%*%**=++ + x+==*%=* *=%*==+x + ===*++ ++*=== + x+**++ o=%$@@@@@@@@@@$%=o ++**+x + ++** x%@@@@@@$$$$$$$$$$$$@@@@@@%x **++ + ++== *@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@* ==++ + x+** x@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x **+x + **x· +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ·x** + ++++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++++ + =* x@$$$$$$$$$$@@@$$$$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$@x *= + xx++ +@$$$$$$@@@@$$$$$@@@@$$$$$$$$$$@@@@@$$$$@@@@@$$$$$@+ ++xx + +++~ @$$$$$@@= +@@$$$$$$@@%~ ~%@$$$$$@ ~+++ + == @$$$$$@o ~~ o@$$$$@* ~~ $@$$$$@ == + == @$$$$@ o~ ·@$$@= x $$$$$@ == + == ·@$$$@= * *~ $$$@ % @ x@$$$@· == + == ·@$$$@o @ ~@ + $$$$ xo @ % =@$$$@· == + == ·@$$$$% * =@$$@ % @$$$$@· == + == ·@$$$$@= **· +@@$$$$@ o%o o%@$$$$$@· == + == ·@$$$$$@@~ =@@@$$$$$$$@+ @@@@$$$$$$@· == + == ·@$$$$$$$@@$+~ *$$$$$$$$$$$@@@*o x$$$$$$$$$$@· == + == ·@$$$$$$$$$@@@@@@@@@@$$$$$$$$$$$$$$@@@@@@@@@$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == =@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@* == + +++x $@@@@@$@@@@@@=o~o+%@@@@@@@@@@@@%xo~o=@@@@@@$@@@@@$ x+++ + **o· ~=%$$$%=~ x*$$$$*x ~=%$$$%=~ ** + x+**o~ ~~**xx + xx**+= ++****=*+x xx*=****++ ==**+x + ++==*%%%%%**=+ ++++*%%%%%%*++++ ==**%%%%%*==++ + diff --git a/src/build/framegen/frames/frame_129.txt b/src/build/framegen/frames/frame_129.txt new file mode 100644 index 0000000000..837a8bd579 --- /dev/null +++ b/src/build/framegen/frames/frame_129.txt @@ -0,0 +1,41 @@ + + + + x++++++x + +==**%%%%%****%%%%%**==+ + ++===*+= =+*===++ + ===* *=== + x+**x+ ·+*$@@@@@@@@@@@@$*+· +x**++ + ++== +$@@@@@@$$$$$$$$$$$$@@@@@@$+ =*++ + ++== ·%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@%· =*++ + ++== =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= ==++ + **o· =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ·o** + ++++ ·@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@· ++++ + =* +@$$$$$$$$$@@@@@$$$$$$$$$$$$$$$$$$@@@@@@$$$$$$$$@+ *= + x+++ =@$$$$$$@@@@$%%%$$@@@$$$$$$$$$$@@@@$%%%%$@@@@$$$$$@= +++x + +++· ·@$$$$$@$x o$@$$$$$$@@* *@@$$$$@· ·+++ + == @$$$$$@· ~xx ·@$$$$@= ·ox~ $@$$$$@ == + == @$$$$@ +· ~ @$$@+ x $$$$$@ == + == ·@$$$@+ * *~ $$$@ * @ x@$$$@· == + == ·@$$$@x @ ·$ x @$$$ ox $ = =@$$$@· == + == ·@$$$$$ * *$$$@ * ~@$$$$@· == + == ·@$$$$@* ·*%~ ~*@@$$$$@ +%x· +$@$$$$$@· == + == ·@$$$$$@@x +@@@$$$$$$$@* @@@$$$$$$$@· == + == ·@$$$$$$$@@@*x· ~*$$$$$$$$$$$@@@%+~ +$$$$$$$$$$@· == + == ·@$$$$$$$$$$@@@@@@@@@$$$$$$$$$$$$$$@@@@@@@@@$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@* == + +++x ·$@@@@$$$@@@@@*xoo+$@@@@@$$@@@@@$+oox*@@@@@$$$@@@@@~ x+++ + ** ~=%$$$$*o +%$$$$%+ o*$$$$%*o ** + xx**~~ **++ + ++**+= ++==**==x~ ~x==**==++ =+**+x + ++==*%%%%%%*== x+==*%%%%%%*==+x ==**%%%%%*==++ + diff --git a/src/build/framegen/frames/frame_130.txt b/src/build/framegen/frames/frame_130.txt new file mode 100644 index 0000000000..cf834c5fd1 --- /dev/null +++ b/src/build/framegen/frames/frame_130.txt @@ -0,0 +1,41 @@ + + + + ++++++++++++ + ++++***%%%==++++==%%%***++++ + ++**=*+o o+*=**++ + x+**== ==**+x + ++** x*$@@@@@@@@@@@@@@$*x **++ + ++*+ o%@@@@@@$$$$$$$$$$$$$$@@@@@@%o +*++ + ++=+ x$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$x +=++ + ++== %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ==++ + ** %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ** + +++x x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x x+++ + == *@$$$$$$$$@@@@@@@$$$$$$$$$$$$$$$$@@@@@@@@$$$$$$$@* == + xx++ *@$$$$$@@@@$*+++=%@@@@$$$$$$$$$@@@$*=++=%$@@@$$$$$@* ++xx + ==+· o@$$$$$@%· %@$$$$$$@@x +@@$$$$@o ·+== + == @$$$$$@ ·o+=o @$$$$@x ~x=x *@$$$$@ == + == $$$$$@ * x @$$@o oo %$$$$$ == + == ·@$$$@+ $ *x $$$$ = @ o@$$$@· == + == ·@$$$@x @ * ~ @$$$ ~+ * x *@$$$@· == + == ·@$$$$$ * $$$$@ + x@$$$$@· == + == ·@$$$$@$ o**xoo+%@@$$$$@· ~=%+oox*@@$$$$$@· == + == ·@$$$$$@@= x@@@$$$$$$$@$~ @@@$$$$$$$@· == + == ·@$$$$$$$@@@$=xo~~o+%$$$$$$$$$$$@@@@*+o~~ox*$$$$$$$$$$@· == + == ·@$$$$$$$$$$@@@@@@@@@$$$$$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@% == + +++x ~@@@@@$$$@@@@@%+xx=$@@@@$$$$@@@@$=xo+*@@@@@$$$@@@@@o x+++ + ** o*$$@$$%x ·=%$@@$%=· x%$@@$$*x ** + x+** **+x + xx**+= xx****== ==****xx =+**++ + ++==*%%%%***++xx ++==*%*%%*%*==++ xx++*%*%%%%*==++ + diff --git a/src/build/framegen/frames/frame_131.txt b/src/build/framegen/frames/frame_131.txt new file mode 100644 index 0000000000..9b79db3877 --- /dev/null +++ b/src/build/framegen/frames/frame_131.txt @@ -0,0 +1,41 @@ + + + + ++++++++++++ + ++==*%*%==++xxxx++==%*%*==++ + ++**=* *=**++ + ++**+= =+**++ + ++== o=%@@@@@@@@@@@@@@@@%=o ==++ + ===+ +$@@@@@$$$$$$$$$$$$$$$$@@@@@$+ +=== + ++=+ =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= +=++ + ++=+ $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ +=++ + ** $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ ** + +++o =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= o+++ + == $@$$$$$$$@@@@@@@@@$$$$$$$$$$$$$$$@@@@@@@@$$$$$$$@$ == + x+++ %@$$$$$@@@$*xo~ox=$@@@$$$$$$$$@@@@%+o~ox+%@@@$$$$$@% ++++ + ==+· x@$$$$@@* =@$$$$$$@$~ o@@$$$$@x ·+== + == @$$$$$$ ·o+%o $$$$$@~ ·~x*+ =@$$$$@ == + == $$$$$$ * + @$$@· +~ %$$$$$ == + == ·@$$$@x @ ~ *x $$$$ ~+ o @ o@$$$@· == + == ·@$$$@+ $ x @$$$ ·= x · *@$$$@· == + == ·@$$$$$ = $$$$@~ o~ =@$$$$@· == + == ·@$$$$$$ o==o++*$@$$$$$@o ~+*xx+=%@@$$$$$@· == + == ·@$$$$$$@%· x@@$$$$$$$$@@o @@@$$$$$$$@· == + == ·@$$$$$$$@@@@%=+xx+*$$$$$$$$$$$$$@@@$*+xx+=%$$$$$$$$$$@· == + == ·@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@% == + +++x ~@@@@@$$$$@@@@%+xx=$@@@@$$$$@@@@$=xx+%@@@@$$$$@@@@@o x+++ + ** x*$$@@$%x ~=%$@@$%=~ x%$@@@$%x ** + x+** **+x + ++**+= xx****== ==****xx =+**++ + ++==*%%%%*%*=+++ ++==*%*%%*%*==++ x++=***%%%%**=++ + diff --git a/src/build/framegen/frames/frame_132.txt b/src/build/framegen/frames/frame_132.txt new file mode 100644 index 0000000000..fbcdcfb5c6 --- /dev/null +++ b/src/build/framegen/frames/frame_132.txt @@ -0,0 +1,41 @@ + + + + ++++===***==++++ + ==***%==xo ox==%***== + ===*++ ++*=== + ++**++ ·oxx++xxo· ++**++ + ===+ o=$@@@@@@@@@@@@@@@@@@$=o +=== + **++ o%@@@@@$$$$$$$$$$$$$$$$$$@@@@@%o ++** + ==+x ~$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$~ x+== + ++=+ x@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x ++++ + xx== o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ==xx + ==x~ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ~x== + ox== @@$$$$$$$@@@@@@@@@@$$$$$$$$$$$$$@@@@@@@@@@$$$$$$@@ ==xo + +++x @$$$$$$@@$*o ~=$@@$$$$$$$$@@@%x ·x%@@@$$$$$@ x+++ + ==x =$$$$$@@o ~@@$$$$$@* %@$$$$$= +== + == @$$$$@* ·~~+%o %$$$$@ ··~x%= o@$$$$@ == + == $$$$$$ * = @$$@ % ~ *@$$$$ == + == ~@$$$@x @ * *+ $$$$ oo * @ o@$$$@~ == + == ·@$$$@= * @$$$ * %$$$$@· == + == ·@$$$$@ x ~@$$$@+ x %@$$$$@· == + == ·@$$$$$@~ oo·=%$@@$$$$$@= ~o·x%$@@@$$$$$@· == + == ·@$$$$$$@@+ x@$$$$$$$$$@@*· @@$$$$$$$$@· == + == ·@$$$$$$$$@@@@$%%%%@@$$$$$$$$$$$$@@@@@$%%%$@$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == %@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@% == + +++x ~@@@@@$$$$@@@@%+xx=$@@@@$$$$@@@@$=xx+%@@@@$$$$@@@@@o o+++ + ** x*$@@@$%x ~=%$@@$%=~ x%$@@@$%x ** + x+** **+x + ++**+= x+****== ==****+x =+**++ + ++==*%%%%*%*==++ ++==*%*%%%%*==++ xx==*%*%%%%**=++ + diff --git a/src/build/framegen/frames/frame_133.txt b/src/build/framegen/frames/frame_133.txt new file mode 100644 index 0000000000..8aee11d549 --- /dev/null +++ b/src/build/framegen/frames/frame_133.txt @@ -0,0 +1,41 @@ + + + + ++++==**%%%%***=++++ + ++==*%**+x x+**%*==++ + ++**=* *=**++ + ==== ox=******=xo ==== + **++ ·=$@@@@@@@@@@@@@@@@@@@@$=· ++** + **+o =@@@@@$$$$$$$$$$$$$$$$$$$$@@@@@= o+** + **+~ +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ~x** + +++x *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* x+++ + xx== +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ==xx + ==x· @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ·x== + ox== @$$$$$$$@@@@@@@@@@@$$$$$$$$$$$$@@@@@@@@@@@@$$$$$$@ ==xo + +++o @$$$$$@@@%x o*@@@$$$$$$$@@$+· ·=$@@$$$$$@ o+++ + == %$$$$$@$ $@$$$$$@x =@$$$$$% == + == @$$$$@+ ~~ ~%~ *@$$$$ ~ ·== @$$$$@ == + == $$$$$% = * @$$@ % + =@$$$$ == + == ~@$$$@o @ ~@ *x $$$$ xo @ @ x@$$$@~ == + == ·@$$$@= =· @$$@ % $$$$$@· == + == ·@$$$$@ ·~ +@$$$$= o ~ $@$$$$@· == + == ·@$$$$$@x *@@@@$$$$$@% o@@@@$$$$$$@· == + == ·@$$$$$$@@*~ +@$$$$$$$$$@@$x @$$$$$$$$$@· == + == ·@$$$$$$$$@@@@@@@@@@@$$$$$$$$$$$$@@@@@@@@@@@$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@% == + +++x ~@@@@@$$$@@@@@%+xx=$@@@@$$$$@@@@$=xx+%@@@@$$$$@@@@@o x+++ + ** o*$$@@$%x ·=%$@@$%=· x%$@@$$*x ** + x+** **+x + ++**+= xx****== ==****xx =+**++ + ++==*%%%%*%*=+xx ++==*%%%%%%*==++ xx+=*%*%%%%**=++ + diff --git a/src/build/framegen/frames/frame_134.txt b/src/build/framegen/frames/frame_134.txt new file mode 100644 index 0000000000..778a14e192 --- /dev/null +++ b/src/build/framegen/frames/frame_134.txt @@ -0,0 +1,41 @@ + + + + ++++=**%%%%%%%%%%**=++++ + +=***%+x x+%***=+ + ++**+= =+**++ + ox**+= o=*%$@@@@@@$%*=o ++**xo + ++**oo ·=$@@@@@@@$$$$$$$$@@@@@@@$=· oo**+x + x+** x$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$x **+x + ox** ·$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$· **xo + ==+~ ~@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@~ ~+== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ·@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@· == + xx++ ~@$$$$$$@@@@@@@@@@@@@$$$$$$$$$$$@@@@@@@@@@@@$$$$$$@~ ++xx + +++~ @$$$$$@@$x o%@@$$$$$$$@$= =@@$$$$$@ ~+++ + == $$$$$$@= =@$$$$@$ ~@$$$$$$ == + == @$$$$@~ o x x@$$@% x ~· @$$$$@ == + == ·$$$$@* xo * $$$@ $ % +@$$$$· == + == ·@$$$@o @ o@ =o $$$$ x~ @· @ +@$$$@· == + == ·@$$$@% ~x x@$$@ $ @$$$$@· == + == ·@$$$$@x · xx ·%@$$$$% ~ = =@$$$$$@· == + == ·@$$$$$@% *@@@@$$$$$@@~ @@@@$$$$$$@· == + == ·@$$$$$$@@$=· +$$$$$$$$$$$@@%o $$$$$$$$$$@· == + == ·@$$$$$$$$$@@@@@@@@@@$$$$$$$$$$$$$@@@@@@@@@@$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@* == + +++x ·$@@@@$$$@@@@@*xox=$@@@@$$$$@@@@$=oox*@@@@@$$$@@@@@~ x+++ + ** o*%$$$$*x ·+%$$$$%+· x*$$$$$*o ** + x+**~ **+x + xx**+= +x*=**== ==**==x+ =+**+x + ++==*%*%%%**++xx ++==*%%%%%%*==++ xx++**%%%*%*==++ + diff --git a/src/build/framegen/frames/frame_135.txt b/src/build/framegen/frames/frame_135.txt new file mode 100644 index 0000000000..2b1bed79ba --- /dev/null +++ b/src/build/framegen/frames/frame_135.txt @@ -0,0 +1,41 @@ + + + ++++ + ++==*%%%*%****%*%%%*==++ + x+==*%== ==%*==+x + ===*xo ox*=== + ++**x+ x*%$@@@@@@@@@@$%*x +x**++ + ++*= +$@@@@@@$$$$$$$$$$$$@@@@@@$+ =*++ + x+== *@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@* ==+x + xx== +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ==xx + **o· +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ·o** + ++++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++++ + =* +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ *= + xx++ +@$$$$$$@@@@@@$@@@@@@$$$$$$$$$$@@@@@@$$@@@@@@$$$$$@+ ++xx + +++~ @$$$$$@@= +@@$$$$$$@@%o o$@$$$$$@ ~+++ + == @$$$$$@o ~ o@$$$$@% ·· $$$$$$@ == + == @$$$$@ oo ~@$$@= x $$$$$@ == + == ·@$$$@= = *· $$$@ % $ x@$$$@· == + == ·@$$$@o @ ~@ +· $$$$ xo @ % +@$$$@· == + == ·@$$$$% = =@$$@ % @$$$$@· == + == ·@$$$$@= **· x@@$$$$$ o%o ~%@$$$$$@· == + == ·@$$$$$@$· =@@@$$$$$$$@+ @@@@$$$$$$@· == + == ·@$$$$$$$@@$+ *$$$$$$$$$$$@@$=~ o$$$$$$$$$$@· == + == ·@$$$$$$$$$@@@@@@@@@@$$$$$$$$$$$$$$@@@@@@@@@$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@* == + +++x $@@@@@$$@@@@@*xoo+%@@@@@$$@@@@@%+o~x=@@@@@$$@@@@@$· x+++ + **o ~=%$$$%*o +%$$$$%+ o*%$$$%=~ ** + x+**~~ ~**+x + ++**== ++==**==xo o+==**==++ =+**++ + ++==*%%%%%**++xx ++==*%%%%%%*==++ xx++**%%%%%**=++ + diff --git a/src/build/framegen/frames/frame_136.txt b/src/build/framegen/frames/frame_136.txt new file mode 100644 index 0000000000..66c8c4040c --- /dev/null +++ b/src/build/framegen/frames/frame_136.txt @@ -0,0 +1,41 @@ + + + x ++++====++++ + ++==*%*%==++++++++==%*%*==++ + ++**=* *=**++ + x+**+= ·· =+**+x + ++== o=%@@@@@@@@@@@@@@@@%=o ==++ + ===+ +$@@@@@$$$$$$$$$$$$$$$$@@@@@$+ +=== + ++++ =@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@= ++++ + ++=+ $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ +=++ + ** $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ** + +++o =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= o+++ + == $@$$$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$@@@@@@$$$$$$$$@$ == + ++++ $@$$$$$@@@@$*=+=*$@@@@$$$$$$$$$@@@@%=+==%@@@@$$$$$@$ ++++ + ==+· +@$$$$$@$~ ·%@$$$$$$@@x +@@$$$$@x ·+== + == @$$$$$@ ~x=~ @$$$$@x ·o=x *@$$$$@ == + == $$$$$@ = x @$$@o ~x $$$$$$ == + == ·@$$$@+ % *o $$$$ = @ o@$$$@· == + == ·@$$$@x @ * o @$$$ ~x * x *@$$$@· == + == ·@$$$$$ * %$$$@ = x@$$$$@· == + == ·@$$$$@% o**oo~+%@@$$$$@ ·+%xo~x=@@$$$$$@· == + == ·@$$$$$@@= +@@@$$$$$$$@% @@@$$$$$$$@· == + == ·@$$$$$$$@@@$=o~··~x%$$$$$$$$$$$@@@$*x~··~o*$$$$$$$$$$@· == + == ·@$$$$$$$$$$@@@@@@@@@$$$$$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == +@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@= == + ++++ %@@@@@@@@@@@$+o·~x*@@@@@@@@@@@@*o··~+$@@@@@@@@@@@% x+++ + **x· +*%%%*+· o=%%%%=o ·+%%$%*+ ·o** + x+**o~ oo oo oo**+x + ++**+=+~ =+******+x x+******+= ~x==**++ + ++==**%%%***++xx ++==***%%***==++ xx++***%%%**==++ + diff --git a/src/build/framegen/frames/frame_137.txt b/src/build/framegen/frames/frame_137.txt new file mode 100644 index 0000000000..de5f454c6b --- /dev/null +++ b/src/build/framegen/frames/frame_137.txt @@ -0,0 +1,41 @@ + + + ++++==****==++++ + ==***%==xo ox==%***== + ===*++ ++*=== + ++**x+ ·oxx++xxo· +x**++ + ===+ o*$@@@@@@@@@@@@@@@@@@$*o +=== + **++ o%@@@@@$$$$$$$$$$$$$$$$$$@@@@@%o ++** + ==+o ~$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$~ o+== + ++++ +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ++++ + ox== o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ==xo + ==+~ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ~+== + ox== @@$$$$$$$@@@@@@@@@$$$$$$$$$$$$$$$@@@@@@@@$$$$$$$@@ ==xo + +++x @$$$$$$@@@$*xooox=$@@@$$$$$$$$@@@@%+ooox+%@@@$$$$$$@ x+++ + ==x =$$$$$$@= =@$$$$$$@$~ o@@$$$$$* x== + == @$$$$@$ ·o+%o $$$$$@~ ox*+ =@$$$$@ == + == $$$$$$ * + @$$@· +· %$$$$$ == + == ~@$$$@x @ ~ *x $$$$ ~+ o @ o@$$$@~ == + == ·@$$$@+ $ o @$$$ ·= x · *$$$$@· == + == ·@$$$$$ = $$$$@~ o~ =@$$$$@· == + == ·@$$$$$$ ~==o++*$@$$$$$@o ·x*ox+=%@@$$$$$@· == + == ·@$$$$$$@% x@@$$$$$$$$@$o @@@$$$$$$$@· == + == ·@$$$$$$$@@@@%=+xx+*$$$$$$$$$$$$$@@@$*+xx+=%$$$$$$$$$$@· == + == ·@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@+ == + ++++ *@@@@@@@@@@@%x· ~=@@@@@@@@@@@@=~ x%@@@@@@@@@@@* ++++ + **x· o=*%%=x ~+*%%*+~ x=%%*=x ·x** + x+**xo ++ ++ ox**+x + x+**=*xx *+****==++ ++==****+* x+==**+x + ++==********++xx ++==********==++ xx++********==++ + diff --git a/src/build/framegen/frames/frame_138.txt b/src/build/framegen/frames/frame_138.txt new file mode 100644 index 0000000000..284028b3b0 --- /dev/null +++ b/src/build/framegen/frames/frame_138.txt @@ -0,0 +1,41 @@ + + + +++=**%%%%%%%%**=+++ + ++***%==o· ·o==%***++ + ++**+* *+**++ + ==== o+=*%$$$$%*=+o ==== + x+**xx x*@@@@@@@@@@@@@@@@@@@@@@*x xx**xx + xx**~~ ~%@@@@$$$$$$$$$$$$$$$$$$$$$$@@@@%~ ~~**xx + **x· *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ·x** + ==+o %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% o+== + xx== *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ==xx + ==x· @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ·x== + xx== @$$$$$$$$@@@@@@@@@$$$$$$$$$$$$$$@@@@@@@@@@$$$$$$$@ ==xx + +++o @$$$$$$@@@*x· ·o=$@@$$$$$$$$@@@%+~ ~+%@@@$$$$$@ o+++ + == %$$$$$@@x o@@$$$$$@* %@$$$$$% == + == @$$$$@% ·~o+%x %$$$$@ ~~x%* x@$$$$@ == + == $$$$$$ % = @$$@ * ~ *@$$$$ == + == ·@$$$@x @ * *+ $$$$ ox * @ o@$$$@· == + == ·@$$$@+ % @$$@ * %$$$$@· == + == ·@$$$$@ + ·@$$$@x o %@$$$$@· == + == ·@$$$$$@ ~xx~=%$@@$$$$$@= ox~x*%@@@$$$$$@· == + == ·@$$$$$$@$x x@@$$$$$$$$@@= @@$$$$$$$$@· == + == ·@$$$$$$$$@@@@$%**%$@$$$$$$$$$$$$@@@@$%**%$@$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ~@@$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@o == + ++++ +@@@@@@@@@@@=~ x$@@@@@@@@@@$x ·=@@@@@@@@@@@+ ++++ + **+~ o+=+x· o+==+o ·x+=+x ~x** + xx**+x == == x+**+x + xx**==+= ~+*=******+* *+******=*x~ =+==**xx + ++==********++ x+++********+++x ++********==++ + diff --git a/src/build/framegen/frames/frame_139.txt b/src/build/framegen/frames/frame_139.txt new file mode 100644 index 0000000000..c4537217da --- /dev/null +++ b/src/build/framegen/frames/frame_139.txt @@ -0,0 +1,41 @@ + + + ++==**%%*%%%%%%*%%**==++ + ==***%xx xx%***== + ++**++ ++**++ + xx**++ x=*$$@@@@@@$$*=x ++**+x + ++**~o ~*$@@@@@@@$$$$$$$$@@@@@@@$*~ oo**++ + ++** +$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$+ **++ + xx** ~$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$~ **xo + ==+~ ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ ~+== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + x+++ o@$$$$$$$$@@@@@@@@@@$$$$$$$$$$$$$@@@@@@@@@@$$$$$$$@o +++x + +++~ @$$$$$$@@$=o ~+$@@$$$$$$$$@@@%x· ·x%@@@$$$$$@ ~+++ + == $$$$$$@@o ~@@$$$$$@= %@$$$$$$ == + == @$$$$@* ~~~x%x %$$$$@ ·~~o%= o@$$$$@ == + == ·$$$$$$ * = @$$@ * o *@$$$$· == + == ·@$$$@x @ % *+ $$$$ oo % @ o@$$$@· == + == ·@$$$@= * @$$@ * %$$$$@· == + == ·@$$$$@ x ~@$$$@+ o %@$$$$@· == + == ·@$$$$$@~ ~o~~=%$@@$$$$$@* oo~x%$@@@$$$$$@· == + == ·@$$$$$$@@+ x@$$$$$$$$$@@*· @@$$$$$$$$@· == + == ·@$$$$$$$$@@@@$$%%$$@$$$$$$$$$$$$@@@@@$%%%$@$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==+· @@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@ x== + ++=+ o$@@@@@@@@@@+ ~*@@@@@@@@@@*~ +@@@@@@@@@@$x ++++ + ==+x ·oxo~ ~oo~ ~oxo· o+*= + x+**+x =* == ++**xx + x+==**+*+o ++*=**==**=*+x x+*=**==**=*++ ~+*=**==xx + ++++******==++ ++++********++++ ++==******++++ + diff --git a/src/build/framegen/frames/frame_140.txt b/src/build/framegen/frames/frame_140.txt new file mode 100644 index 0000000000..be700a44af --- /dev/null +++ b/src/build/framegen/frames/frame_140.txt @@ -0,0 +1,41 @@ + + xx++++xx + ===*%%%%*%****%*%%%%*=== + ++**=%++ ++%=**++ + ===* *=== + x+**+x ~+*$@@@@@@@@@@@@$*+~ x+**+x + ++== =$@@@@@$$$$$$$$$$$$$$@@@@@$= ==++ + ++*= ~%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@%~ =*++ + x+== =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= ==+x + =*o =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= o*= + ++++ ·@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@· ++++ + == =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= == + x+++ =@$$$$$$$$@@@@@@@@@$$$$$$$$$$$$$$@@@@@@@@@@$$$$$$$@= ++xx + +++· ·@$$$$$$@@@%+~ ~x*@@@$$$$$$$$@@@$=o· ~o=$@@@$$$$$@· ·+++ + == @$$$$$@@x x@@$$$$$@% $@$$$$$@ == + == $$$$$@% ~o=%x $$$$$@ ~~x%= x@$$$$$ == + == ·@$$$$$ * = @$$@ * · *$$$$@· == + == ·@$$$@x @ = *+ $$$$ ox * @ o@$$$@· == + == ·@$$$@+ % @$$@ * %$$$$@· == + == ·@$$$$@ + ·@$$$@x o %@$$$$@· == + == ·@$$$$$@ ~xo~=%$@@$$$$$@= ox~x*%@@@$$$$$@· == + == ·@$$$$$$@$x x@@$$$$$$$$@@* @@$$$$$$$$@· == + == ·@$$$$$$$$@@@@$%%%%$@$$$$$$$$$$$$@@@@$$%%%$@$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==x· $@@$$$$$$$$$@@@@@@@@@@$$$$$$$$@@@@@@@@@@$$$$$$$$$@@$ ·+== + ++++ =@@@@@@@@@*· x$@@@@@@@@$x *@@@@@@@@@= ++++ + ==++ +=== + xx=*=+ ++%%xx xx%%++ +===xx + ==**=*==++++*=*=**==**=%=*++++*=%=**==**=*=*++++==*=**==xx + ++++==****==++ ++==****==++ ++==****==++++ + diff --git a/src/build/framegen/frames/frame_141.txt b/src/build/framegen/frames/frame_141.txt new file mode 100644 index 0000000000..f5780d60f0 --- /dev/null +++ b/src/build/framegen/frames/frame_141.txt @@ -0,0 +1,41 @@ + + xx++++++++xx + x++=*%*%*%========%*%*%*=++x + ++**=*+x x+*=**++ + xx**=* *=**xx + ++**o x*$@@@@@@@@@@@@@@$*x o**++ + ++=+ o*@@@@@$$$$$$$$$$$$$$$$@@@@@*o +=++ + ++=+ x$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$x +=++ + x+== *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ==+x + ** %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ** + +++x x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x x+++ + =* *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* *= + ++++ *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++++ + ==+· o@$$$$$$$@@@@@@@@@@@@@$$$$$$$$$$@@@@@@@@@@@@@$$$$$$$@o ·+== + == @$$$$$$@@%~ ~*@@$$$$$$@@$+ +$@$$$$$$@ == + == $$$$$$@o o@$$$$@* $@$$$$$ == + == ·@$$$$@ x @$$@+ o $$$$$@· == + == ·@$$$@+ % %o $$$$ = @ x@$$$@· == + == ·@$$$@+ $ o @$$$ ·= o *$$$$@· == + == ·@$$$$@ x $$$$@x ~ *@$$$$@· == + == ·@$$$$$@~ · +%$@@$$$$$@= ~%%@@@$$$$$@· == + == ·@$$$$$$@@*· +@@$$$$$$$$@@%o @@$$$$$$$$@· == + == ·@$$$$$$$$@@@@@@@@@@@$$$$$$$$$$$$@@@@@@@@@@@$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$· == + == @@$$$$$$$$$$$$$$$@@$$$$$$$$$$$$$$$$@@$$$$$$$$$$$$$$$@@ == + ==+~ *@@@$$$$$$$@@@@$%%@@@@$$$$$$$$@@@@%%$@@@@$$$$$$$@@@* ~+== + ++== o%@@@@@@@$x ·*@@@@@@@@*· x$@@@@@@@%x ==++ + ===+ +=== + ox====o+ ==%%++ ++%%== xo====+x + ++**=**%****%***==++**=*=******=*=**++==***%****%**=**++ + x++++=====++++ ++++====++++ ++++======+++x + diff --git a/src/build/framegen/frames/frame_142.txt b/src/build/framegen/frames/frame_142.txt new file mode 100644 index 0000000000..1051e2b805 --- /dev/null +++ b/src/build/framegen/frames/frame_142.txt @@ -0,0 +1,41 @@ + + xx++++====++++xx + ++==*%**==++oooo++==**%*==++ + ++**=* *=**=+ + ++**+= ···· =+**++ + ++== x=$@@@@@@@@@@@@@@@@$=x ==++ + ==++ =$@@@@$$$$$$$$$$$$$$$$$$@@@@$= ++== + ===+ *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* +=== + ++=+ ·$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$· +=++ + o ** $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ** o + +++o =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= o+++ + == $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + ==+· +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ·+== + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == $$$$$$@@@@@@@@@@@@@@@@@@$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == ·@$$$$@= ·*@$$$@= +@$$$$$@· == + == ·@$$$@ ~@$@· @$$$$@· == + == ·@$$$$x +$$$+ @$$$$@· == + == ·@$$$$@@*==============*@@$$$@@*===============$@$$$$$@· == + == ·@$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == %@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@% == + ==+x ~@@@@$$$$$@@@@%+x+=$@@@@$$$$@@@@$=+x+%@@@@$$$$$@@@@o o+== + xx== x*$@@@$%x ~=$@@@@$=~ x%$@@@$*x ==xx + ==== ==== + ====+= xx*=**== ==**=*xx =+==== + ++*****%*%%*****==++==***%*%%*%***==++==*****%%%%*****++ + ++++++++++++ x++++++++++x ++++++++++++ + diff --git a/src/build/framegen/frames/frame_143.txt b/src/build/framegen/frames/frame_143.txt new file mode 100644 index 0000000000..a5cb02dd27 --- /dev/null +++ b/src/build/framegen/frames/frame_143.txt @@ -0,0 +1,41 @@ + + ++============++ + ++**%%=*++o~····~o++*=%%**++ + +=**== ==**=+ + ++**++ ·~~oo~~· ++**++ + ===+ +%@@@@@@@@@@@@@@@@@@%+ +=== + ==++ ·*@@@@@$$$$$$$$$$$$$$$$$$@@@@@*· ++== + ===x %@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@% x=== + ++=+ o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@o +=++ + ox== @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xx + ==+~ *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ~+== + == $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + +++x $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ x+++ + ==x· =@$$$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ·x== + == @$$$$$@$**%$@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == $$$$$@ o+*%@@@@$$$$@@@@@@@@@@@@@@@@@@@@$$$$$$ == + == ·@$$$$@ *$$$$% *$$$$$@· == + == ·@$$$$$@ @$$@ @$$$$@· == + == ·@$$$$@ =$$$$% =$$$$$@· == + == ·@$$$$@ ~x=%@@@@$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@· == + == ·@$$$$$@$==%$@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == +@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@= == + ==++ *@@@@@@@@@@@$+~ o*@@@@@@@@@@@@*o ·+$@@@@@@@@@@@% x+== + ++==x· x=%%%*x o=*%%*=o x*%%%*x ·x==++ + ====oo xx xx ~o==== + ====+=++ =+*==*==++ ++=====*+= xx=+==== + ++==**=*********++++==***%****%***==++++************==++ + ++++++++++xx ++++++++++++ x ++++++++++ + diff --git a/src/build/framegen/frames/frame_144.txt b/src/build/framegen/frames/frame_144.txt new file mode 100644 index 0000000000..b0f7b896b3 --- /dev/null +++ b/src/build/framegen/frames/frame_144.txt @@ -0,0 +1,41 @@ + + ++====****====++ + +=***%==+o ox==%***=+ + ===*++ ++*=== + ++**x+ ·ooxxxxoo· +x**++ + ===+ o=$@@@@@@@@@@@@@@@@@@$=o +=== + **++ o%@@@@$$$$$$$$$$$$$$$$$$$$@@@@%o ++** + ==+x ~$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$~ x+== + ++++ +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ++++ + ox== ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ ==xo + ==+~ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ~+== + ox== @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + +++x @$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + ==+ =@$$$$@= o*@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= +== + == @$$$$@+ x%@@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ == + == $$$$$$% =@@$$$$@$+oooooooooooooox%@$$$$$$ == + == ·@$$$$$@@$=~ *@$$$o @$$$$@· == + == ·@$$$$$@@$*x *$$$@o @$$$$@· == + == ·@$$$$$% +@@$$$$@%x~~~~~~~~~~~~~~o*@$$$$$@· == + == ·@$$$$@+ o*@@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$@= ~=$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==x· @@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@ x== + ++++ o$@@@@@@@@@$+ ·*@@@@@@@@@@*· +$@@@@@@@@@$o ++++ + x+==+x ·ooo~ ~oo~ ~ooo· o+==+x + ++==++ +** **+ x+==++ + ++====+=+x ++*========*+x x+*========*++ x+======++ + ++++==********==++++++==********==++++++==********====++ + ++++++++ ++++++++ ++++++++ + diff --git a/src/build/framegen/frames/frame_145.txt b/src/build/framegen/frames/frame_145.txt new file mode 100644 index 0000000000..f096e8c69b --- /dev/null +++ b/src/build/framegen/frames/frame_145.txt @@ -0,0 +1,41 @@ + + ++====****====++ + xx+=***%==x~ ~x==%***=+xx + x+===*++ ++*===+x + ++**+x ~ox++++xo~ x+**++ + ==++ x*$@@@@@@@@@@@@@@@@@@$*x ++== + **+x x%@@@@$$$$$$$$$$$$$$$$$$$$@@@@%x x+** + ==+o o$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$o o+== + ++++ +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ++++ + xx== o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ==xx + ==+· $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ·+== + x+== @@$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==+x + +++x @$$$$@@$*%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + ==x *$$$$$% ~=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$* x== + == @$$$$@* =$@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ == + == $$$$$$@%· %@$$$$@x ~$$$$$$$ == + == ~@$$$$$$$@@@%~ %$$$@ @$$$$@~ == + == ·@$$$$$@@$+ $$$$$= o$$$$$@· == + == ·@$$$$$$ =$@$$$$$@@$%%%%%%%%%%%%%%%@@$$$$$@· == + == ·@$$$$@* ~*@@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@· == + == ·@$$$$$@* o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+· $@@$$$$$$$$$@@@@@@@@@@$$$$$$$$@@@@@@@@@@$$$$$$$$$@@$ ·x== + ++++ =@@@@@@@@@* x$@@@@@@@@$x *@@@@@@@@@= ++++ + oo==++ ++==xx + ++===+ ++%%+o o+%%++ +===++ + ++=====%+=++++*=*========***++++***========*=*++++=+%=====++ + xx++==******====++ ++==********==++ ++====******==++xx + ++++ ++++ ++++ + diff --git a/src/build/framegen/frames/frame_146.txt b/src/build/framegen/frames/frame_146.txt new file mode 100644 index 0000000000..423d3e734c --- /dev/null +++ b/src/build/framegen/frames/frame_146.txt @@ -0,0 +1,41 @@ + + xx++==********===+xx + ++==*%**=+~· ·~+=**%*==++ + x+=*=*+x x+*=*=+x + ++**xo ·ox+====+xo· ox**++ + **++ x*@@@@@@@@@@@@@@@@@@@@*x ++** + **+o x$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$x x+** + ==+o x$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$x o+== + ++++ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ++++ + xx== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==xx + ==+· $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ·+== + ox== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + +++x @$$$$@* ·*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + == *$$$$@% o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$* == + == @$$$$$$ o%@@$$$$$@@@$$$$$$$$$$$$$$$@@$$$$$@ == + == $$$$$$$@@=· ~$$$$$* +$$$$$$ == + == ~@$$$$$$$@@@@* $$$$@ @$$$$@~ == + == ·@$$$$$$@*~ =$$$$$$· %$$$$$@· == + == ·@$$$$$$ ~*@@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@· == + == ·@$$$$$$ ·=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$@$x ·=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == $@$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$@$ == + ==+o +@@@$$$$$$$@@@$***%@@@@$$$$$$@@@@%***$@@@$$$$$$$@@@+ o+== + ++== =$@@@@@$*~ +%@@@@@@%+ ~*$@@@@@$=· ==++ + ===+ +=== + ++====x+ *=**== ==**=* +x====++ + ++=====**%******=========**%****%**=========******%**=====++ + ++++========++xx ++++========++++ xx++========++++ + + diff --git a/src/build/framegen/frames/frame_147.txt b/src/build/framegen/frames/frame_147.txt new file mode 100644 index 0000000000..baf0b2890f --- /dev/null +++ b/src/build/framegen/frames/frame_147.txt @@ -0,0 +1,41 @@ + + ++++==********===+++ + ++==*%**++~· ·~++**%*==++ + xx**=%+x x+%=**xx + ++*=x ~ox+====+xo~ x=*++ + **++ x%@@@@@@@@@@@@@@@@@@@@%x ++** + **+o +$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$+ o+** + =*+o x$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$x o+*= + ++++ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ++++ + ox== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==xo + ==+· $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ·+== + ox== @$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==xo + +++x @$$$$$x x%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + == *$$$$@% +$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$* == + == @$$$$$$o =$@$$$$$@@$%%%%%%%%%%%%%%%@@$$$$$@ == + == $$$$$$$@@$x $$$$$= o$$$$$$ == + == ·@$$$$$$$@@@@= $$$$@ @$$$$@· == + == ·@$$$$$$@= %$$$$$@x ~$$$$$$@· == + == ·@$$$$$$ +$@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$ o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$@@=oo*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@% == + ==+x ~$@@@@$$$$@@@@%+xx=$@@@@$$$$@@@@$=xx+*@@@@$$$$@@@@@~ x+== + ++== o*$@@@$*x ·=%$@@$%=· x*$@@@$*x ==++ + ==== =+== + xx====+= x+*===+= =+====+x =+====xx + ++====*%%%%**=====++====*%*%%*%*====++=====**%%%%*====++ + xx++++====++++ ++++====++++ ++++====++++x+ + + diff --git a/src/build/framegen/frames/frame_148.txt b/src/build/framegen/frames/frame_148.txt new file mode 100644 index 0000000000..a36374d761 --- /dev/null +++ b/src/build/framegen/frames/frame_148.txt @@ -0,0 +1,41 @@ + + ++++==********===+++ + ++==*%**++~· ·~++**%*==++ + xx**=%+x x+%=**xx + ++*=x ~ox+====+xo~ x==++ + **++ +%@@@@@@@@@@@@@@@@@@@@%+ ++** + **+o +$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$+ o+** + ==+~ x$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$x ~+*= + ++++ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ++++ + ox== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==xo + ==+· $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ·+== + ox== @$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==xo + +++x @$$$$$x x%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + == *$$$$@% x$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$* == + == @$$$$$$o =$@$$$$$@@%%%%%%%%%%%%%%%%@@$$$$$@ == + == $$$$$$$@@$x $$$$$= ~$$$$$$ == + == ~@$$$$$$$@@@@+ $$$$@ @$$$$@~ == + == ·@$$$$$@@+ ·$$$$$$@+ o$$$$$$@· == + == ·@$$$$$$ +$@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$ x$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$@@=xx%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@+ == + ==++ *@@@@@@@@@@@%x ~=@@@@@@@@@@@@=~ x%@@@@@@@@@@@% ++== + xx==x· o=***=x ~+****+~ x=***=x ·x==++ + ====xo xx x+ ox==== + ======+x =+=*====++ ++====*=+= x+*===== + ++==***%********++++==************==++++********%***==++ + ++++++++++ +x++++++++x+ ++++++++++ + + diff --git a/src/build/framegen/frames/frame_149.txt b/src/build/framegen/frames/frame_149.txt new file mode 100644 index 0000000000..eaf1e09a4a --- /dev/null +++ b/src/build/framegen/frames/frame_149.txt @@ -0,0 +1,41 @@ + + xx++==********===+xx + ++==*%**++~· ·~++**%*==++ + xx**=*+x x+*=**xx + ++*=xo ·ox+====+xo· ox=*++ + **++ x%@@@@@@@@@@@@@@@@@@@@%x ++** + **+o +$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$+ o+** + =*+o x$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$x o+*= + ++++ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ++++ + ox== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==xo + ==+· $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ·+== + ox== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + +++x @$$$$@= +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + == *$$$$@% +$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$* == + == @$$$$$$· =$@$$$$$@@$%%%%%%%%%%%%%%%@@$$$$$@ == + == $$$$$$$@@*o $$$$$= ~$$$$$$ == + == ·@$$$$$$$@@@$x $$$$@ @$$$$@· == + == ·@$$$$$@@x ·%$$$$$@+ o$$$$$$@· == + == ·@$$$$$$ =$@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@%+=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ~@@$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@o == + ++++ +@@@@@@@@@@@*~ x$@@@@@@@@@@$x ·*@@@@@@@@@@@= ++++ + ox==+o x+=+x· o+==+o ·x+=+x· ~+==+x + ++==+x == == xx==++ + ++====++ ox*+==**==+= =+=***==+*xo ++====++ + ++==**********==++++++************++++++==**********==++ + ++++++++ xx++++xx ++++++++ + + diff --git a/src/build/framegen/frames/frame_150.txt b/src/build/framegen/frames/frame_150.txt new file mode 100644 index 0000000000..9f440069ea --- /dev/null +++ b/src/build/framegen/frames/frame_150.txt @@ -0,0 +1,41 @@ + + xx++==********===+xx + +++=****==o· ·o==****=+++ + x+=*=*+x x+*=*=+x + ++**+x ~x++==++x~ xx**++ + **++ x*@@@@@@@@@@@@@@@@@@@@*x ++** + **xx x%@@@@$$$$$$$$$$$$$$$$$$$$@@@@%x x+** + ==+o o$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$o o+== + ++++ +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ++++ + ox== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==xx + ==+· $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ·+== + ox== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + +++x @$$$$@* =@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + ==x *$$$$@% ·=@@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$* == + == @$$$$$$ ·*@@$$$$$@@$%%%%%%%%%%%%%%%@@$$$$$@ == + == $$$$$$$@@*~ $$$$$= o$$$$$$ == + == ~@$$$$$$$@@@@+ $$$$@ @$$$$@~ == + == ·@$$$$$@@x %$$$$$@x ~$$$$$$@· == + == ·@$$$$$$ +$@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@· == + == ·@$$$$$$ +$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$@@*+=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==x· @@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@ ·x== + ++++ ~%@@@@@@@@@$x ·*@@@@@@@@@@*· x$@@@@@@@@@%o ++++ + ox==+x ~oo· ~oo~ ·ooo o+==xo + ++==++ +%% %%+o ++==++ + ++=*===*xx ~o++*=**==**+*++ ++*+**==**=*++ ~ xx*===*=++ + ++==********++xx ++==********==++ xx++********==++ + + + diff --git a/src/build/framegen/frames/frame_151.txt b/src/build/framegen/frames/frame_151.txt new file mode 100644 index 0000000000..540e3e13c5 --- /dev/null +++ b/src/build/framegen/frames/frame_151.txt @@ -0,0 +1,41 @@ + + x+++===******====++x + ++==***%=*xo ox==%***==++ + xx=*=*++ ++*=*=xx + ++**++ ~ox++++xo~ ++**++ + **++ o*$@@@@@@@@@@@@@@@@@@$*o ++** + **+x o%@@@@$$$$$$$$$$$$$$$$$$$$@@@@%o x+** + ==+o ~$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$~ o+== + ++=+ +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ++++ + ox== o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ==xo + ==+~ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ~+== + ox== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + +++x @$$$$@* ·=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + ==+ *@$$$@% ~*@@@@$$$$$$$$$$@@@@@@@@@@@@@@@$$$$$$@* +== + == @$$$$$$ ~*@@$$$$$@@$%%%%%%%%%%%%%%$@@$$$$$@ == + == $$$$$$$@$=· $$$$$* x$$$$$$ == + == ·@$$$$$$$@@@@+ $$$$@ @$$$$@~ == + == ·@$$$$$@@+ %@$$$$$o ·$$$$$$@· == + == ·@$$$$$$ x$@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@· == + == ·@$$$$$$ x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$@@*x+%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==+· @@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@ ·+== + ++++ %@@@@@@@@@%o =$@@@@@@@@$= ~%@@@@@@@@@%· ++++ + ==+x ·· ·· ··· x+== + x+==++ ++%% %*++ ++*=xx + xx==**=*++xxxx+=*=**==**=*=+xxxx+=%=**==**=*=+xxoo++*=**==xx + ++++******==++xx ++++********++++ xx++==******=+++ + + + diff --git a/src/build/framegen/frames/frame_152.txt b/src/build/framegen/frames/frame_152.txt new file mode 100644 index 0000000000..34e342244b --- /dev/null +++ b/src/build/framegen/frames/frame_152.txt @@ -0,0 +1,41 @@ + + xx++=====**=====++xx + +++=***%**++~· ·~++**%***++++ + xx==*%=+ +=%*==xx + ++**++ ~~oooo~~ ++**++ + ==== ·+%@@@@@@@@@@@@@@@@@@%+· ==== + **+x ~*@@@@@$$$$$$$$$$$$$$$$$$@@@@@*~ x+** + ==+x ·%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@%· x+== + ++=+ x@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x +=++ + ox== ·@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@· ==xo + ==+~ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ~+== + ox== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + +++x $$$$$$%· o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ x+++ + ==+· =@$$$@$ o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ·+== + == @$$$$$$ x%@@$$$$$@@@$$$$$$$$$$$$$$$@@$$$$$@ == + == $$$$$$$@$+ ~$$$$$* +$$$$$$ == + == ~@$$$$$$$@@@@+ $$$$@ @$$$$@~ == + == ·@$$$$$$@= *$$$$$$· %$$$$$@· == + == ·@$$$$$$ o%@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@· == + == ·@$$$$$$ o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$@@=ox*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+· $@@$$$$$$$$$@@@@@@@@@@$$$$$$$$@@@@@@@@@@$$$$$$$$$@@$ ·+== + +++= =$@@@@@@@@* x%@@@@@@@@%x *@@@@@@@@@* ++++ + ==++ +=== + xx=*=+ ++%%+x x+%%=+ +=**+x + ++**=*=+++++=**=**++**=%==++++==%=**++**=**=+++++=*=**++ + xx++======++++ ++========++ ++++==**==++xx + + + diff --git a/src/build/framegen/frames/frame_153.txt b/src/build/framegen/frames/frame_153.txt new file mode 100644 index 0000000000..b81afbddb8 --- /dev/null +++ b/src/build/framegen/frames/frame_153.txt @@ -0,0 +1,41 @@ + + xx++============++xx + ++++**%%=*==xo~~~~ox==**%%**++++ + xx==**== ==**==xx + ++**+= ·~~~~· =+**++ + ===+ x*$@@@@@@@@@@@@@@@@$*x +=== + **++ =@@@@@$$$$$$$$$$$$$$$$$$@@@@@= ++** + ==++ *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* +=== + ++=+ ~@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@~ +=++ + xx== @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + ==+o *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* o+== + ox== $@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ==xo + +++x $@$$$@$o x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ x+++ + ==+· +@$$$$$ +$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ·+== + == @$$$$$$ +$@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$$@%x o$$$$$% =$$$$$$ == + == ·@$$$$$$$@@@@+ $$$$@ @$$$$@· == + == ·@$$$$$$@*~ +$$$$$$ *$$$$$@· == + == ·@$$$$$$ ·*@@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@· == + == ·@$$$$$$ ·=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$@@+·~=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+· %@@$$$$$$$$$@@@@$$@@@@$$$$$$$$@@@@$$@@@@$$$$$$$$$@@$ ·+== + x+== +$@@@@@@@$* o%@@@@@@@@%o *$@@@@@@@$= ==++ + ==++ ++== + ==== ==%%++ ++%%== +=== + ++**=%==++++****==++**=***++++***=**++==****++++==%=**++ + +++=====++++ +++======++x ++++=====+++ + + + diff --git a/src/build/framegen/frames/frame_154.txt b/src/build/framegen/frames/frame_154.txt new file mode 100644 index 0000000000..7f4c1c11d5 --- /dev/null +++ b/src/build/framegen/frames/frame_154.txt @@ -0,0 +1,41 @@ + + ++++++====++++++ + xx+=***%%%**==++++==**%%%***=+xx + ox==**=*xo ox*=**==xo + ++**+= =+**++ + ==== ~+%$@@@@@@@@@@@@@@$%+~ ==== + ===+ x%@@@@@$$$$$$$$$$$$$$$$@@@@@%x +=== + ===+ +@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@+ +=== + ++=+ %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% +=++ + ox== %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ==xo + ==+x x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x x+== + ox== %@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ==xo + ++++ %@$$$@@+~o=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++++ + ==+· x@$$$$$ ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ·+== + == @$$$$$$ ~*@@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$$@=· =$$$$$$ %$$$$$$ == + == ·@$$$$$$$@@@@+ $$$$@ @$$$$@· == + == ·@$$$$$$@$x o$$$$$% +$$$$$@· == + == ·@$$$$$$ x$@@$$$$$@@@$$$$$$$$$$$$$$@@@$$$$$@· == + == ·@$$$$@$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$@$~ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+~ %@@@$$$$$$$$@@@@$$@@@@$$$$$$$$@@@@$$$@@@$$$$$$$$@@@% ·+== + ++== +$@@@@@@@$= ~%@@@@@@@@%~ =$@@@@@@@$+ ==xx + ===+ +=== + ==== ==%%++ ++%%== ==== + ++**=***++==%***==++***%**++++**%***++==***%==++**%=**++ + x+++==++++ x+++====+++x ++++++++++ + + + diff --git a/src/build/framegen/frames/frame_155.txt b/src/build/framegen/frames/frame_155.txt new file mode 100644 index 0000000000..0a9d3d097f --- /dev/null +++ b/src/build/framegen/frames/frame_155.txt @@ -0,0 +1,41 @@ + + ++++++++++++++++ + ++*****%*%=******=%*%*****++ + x ++**=*++ x+*=**++ x + ++**+= =+**++ + ====ox o=%@@@@@@@@@@@@@@%=o xo==== + ===+ ~*@@@@@@$$$$$$$$$$$$$$@@@@@@*~ +=== + ===+ o$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$o +=== + ++== *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ==++ + ox== *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ==xo + ==+x o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o x+== + ox== =@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + ++++ *@$$$@@*x+%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++++ + ==x· ~@$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ·x== + == @$$$$$$ x$@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$@@+ %@$$$$$o ·$$$$$$$ == + == ·@$$$$$$$@@@@+ $$$$@ @$$$$@· == + == ·@$$$$$$@$=· $$$$$* x$$$$$@· == + == ·@$$$$$$ ~*@@$$$$$@@$%%%%%%%%%%%%%%$@@$$$$$@· == + == ·@$$$$@% ~*@@@@$$$$$$$$$$@@@@@@@@@@@@@@@$$$$$$$@· == + == ·@$$$$$@* ·=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$@@ == + ==+~ %@@@$$$$$$$$@@@$$$@@@@$$$$$$$$@@@@$$$@@@$$$$$$$$@@@% ·+== + xx== x%@@@@@@@$= ~*@@@@@@@@*~ =$@@@@@@@$+ ==xx + ===+ +=== + ====o ==%%++ ++%%== ==== + ++***%**====%***++++==***%====%***==++++***%==+=***=**++ + xx++++++++ ++====++ ++++++++xx + + + diff --git a/src/build/framegen/frames/frame_156.txt b/src/build/framegen/frames/frame_156.txt new file mode 100644 index 0000000000..d1c6743b80 --- /dev/null +++ b/src/build/framegen/frames/frame_156.txt @@ -0,0 +1,41 @@ + + xx++++++++++++xx + ++==***%*%*%%%%%%*%*%***==++ + ++**=*== ==*=**++ + ++**=*++ ++*=**++ + ++**++ ~+*%@@@@@@@@@@%*+~ ++**++ + ==== o*@@@@@@@$$$$$$$$$$@@@@@@@*o ==== + ==== =@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@= ==== + ++== x@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x ==++ + xx==x· x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ·x==xx + ++++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++++ + ox== o@$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ==xo + ++++ x@$$$$@$*%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ++++ + ==+~ @$$$$$ o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+== + == $$$$$@$ o%@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@%· x$$$$$$@*· +@$$$$$@ == + == ·@$$$$$$$@@@*· $$$$@~ @$$$$@· == + == ·@$$$$$$@@$+ $$$$$x @$$$$@· == + == ·@$$$$$$o x%@$$$$$@@*===============$@$$$$$@· == + == ·@$$$$@% o%@@@@$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@· == + == ·@$$$$$$x o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$@@ == + ==+~ *@@@$$$$$$$@@@@$$$@@@@$$$$$$$$@@@@$$$@@@@$$$$$$$@@@% ·+== + ox== x%@@@@@@@$+ ~*@@@@@@@@*~ +$@@@@@@@$+ ==+x + ++=+ +=++ + ++==ox ==%%++ ++%%== o==++ + x+***%**====%***++++==***%====%***==++++***%====**%***++ + +++++++x ++++++++ ++++++++ + + + diff --git a/src/build/framegen/frames/frame_157.txt b/src/build/framegen/frames/frame_157.txt new file mode 100644 index 0000000000..03caabe0e9 --- /dev/null +++ b/src/build/framegen/frames/frame_157.txt @@ -0,0 +1,41 @@ + + xx++++++++++++xx + ++==****%%*%%%%%%*%%****==++ + ++****=*++ ++*=****++ + ++=*==+= =+====++ + ++**++ ~+=%$$@@@@$$%=+~ ++**++ + ++==oo +%@@@@@@@@$$$$$$@@@@@@@@%+ oo==++ + ++== o$@@@@$$$$$$$$$$$$$$$$$$$$$$@@@@$o ==++ + ++==~ %@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@% ~==++ + ox==+~ $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ ~+==xo + ++++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++++ + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ++++ ~@$$$$@@$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ++x+ + ==+~ @$$$$$· +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+== + == $$$$$@$ =$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$$= *@$$$$$@$xoooooooooooooox*@$$$$$@ == + == $$$$$$$@@@%x $$$$$o @$$$$$ == + == ·@$$$$$$@@@%x $$$$@o @$$$$@· == + == ·@$$$$$@= =@$$$$$@$xooooooooooooooo*@$$$$$@· == + == ·@$$$$@$ +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$@@ == + ==+~ %@@@$$$$$$$$@@@$$$@@@@$$$$$$$$@@@@$$$@@@$$$$$$$$@@@% ·+== + xx== +%@@@@@@@$= ~%@@@@@@@@%~ =$@@@@@@@$+ ==xx + ++=+ +=++ + ++*=oo ==%%++ ++%%== ==++ + x+***%**====%***++++==***%====%***==++++***%====**%***++ + ++++++++ ++++++++ ++++++++ + + + diff --git a/src/build/framegen/frames/frame_158.txt b/src/build/framegen/frames/frame_158.txt new file mode 100644 index 0000000000..b35f4a8c0b --- /dev/null +++ b/src/build/framegen/frames/frame_158.txt @@ -0,0 +1,41 @@ + + ++++++++++++ + ++++==******%%%%******==++++ + ++==**=***++ ++******==++ + ++==**=*+o o+*=**==++ + ++====x ·o++====++o· x====++ + ++==++ +%@@@@@@@@@@@@@@@@@@@@%+ ++==++ + ++==xo +$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$+ ox==++ + ++==+~ x@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x ~+==++ + ==+x =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= x+== + ++== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==++ + ==x· $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ·x== + ++== @$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==++ + +++x @$$$$$= +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + == *$$$$@% =$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$* == + == @$$$$$$· =$@$$$$$@@%**************%@@$$$$$@ == + == $$$$$$$@@*o $$$$$= ~$$$$$$ == + == ~@$$$$$$$@@@$x $$$$@ @$$$$@~ == + == ·@$$$$$@$o ·$$$$$$@+ o$$$$$$@· == + == ·@$$$$$$ =@@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@%==$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+· %@@@$$$$$$$$@@@@$$@@@@$$$$$$$$@@@@$$@@@@$$$$$$$$$@@$ ·+== + xx== +$@@@@@@@$= o%@@@@@@@@%o =$@@@@@@@$= ==xx + ++=+ +=++ + ++== ==%%++ ++%%== ==++ + x+=*=***++==%***++xx==*%**====**%*==xx++***%==++***=**+x + ++++++++ ++++++++ ++++++++ + + + diff --git a/src/build/framegen/frames/frame_159.txt b/src/build/framegen/frames/frame_159.txt new file mode 100644 index 0000000000..88548ed34d --- /dev/null +++ b/src/build/framegen/frames/frame_159.txt @@ -0,0 +1,41 @@ + + ++xxxx++ + xx++==****************==++xx + ++==**==*%=*++oo oo++*=%*==**==++ + xx++**=*+= =+*=*=++xx + ++====++ ~oooo~ ++====++ + ++===+ +%@@@@@@@@@@@@@@@@@@%+ +===++ + ++==+x ·*@@@@@$$$$$$$$$$$$$$$$$$@@@@@*· x+==++ + x+==+x %@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@% x+==++ + ===+ o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@o +=== + ++== ·@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@· ==++ + ==+~ *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ~+== + ++== $@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ==++ + +++x $$$$$@$~ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ x+++ + ==+· =@$$$@$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ·x== + == @$$$$$$ x%@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$$@$x o$$$$$* +$$$$$$ == + == ·@$$$$$$$@@@@+ $$$$@ @$$$$@· == + == ·@$$$$$$@*· =$$$$$$· %$$$$$@· == + == ·@$$$$$$ ~*@@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@· == + == ·@$$$$$$ ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$@@+~o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+· $@@$$$$$$$$$@@@@$@@@@@$$$$$$$$@@@@@$@@@@$$$$$$$$$@@$ ·+== + xx== =$@@@@@@@@* o%@@@@@@@@%o *@@@@@@@@$= ==+x + ++=+ +=++ + ++== ==%%++ ++%%== ==++ + x+**=%*=++++%***++x+==*%**++++**%*==++++***%++++==%=**++ + ++++++++ ++++++++ ++++++++ + + + diff --git a/src/build/framegen/frames/frame_160.txt b/src/build/framegen/frames/frame_160.txt new file mode 100644 index 0000000000..2e212c62ad --- /dev/null +++ b/src/build/framegen/frames/frame_160.txt @@ -0,0 +1,41 @@ + + + xx++====********====++xx + ++==**=**%****++++****%**=**==++ + ++=====*+x ++%=====++ + ++====== ======++ + ++====xx o=%$@@@@@@@@@@@@$%=o xx====++ + ++===+ ·*$@@@@@$$$$$$$$$$$$$$@@@@@$*· +===++ + xx===+ o$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$o +===xx + ==++ *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ++== + ++== *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ==++ + ==+x ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ x+== + xx== =@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xx + ++++ *@$$$$@*x+%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++++ + ==+· ~@$$$$$ x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ·+== + == @$$$$$$ +$@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$@@+ %$$$$$@x ·$$$$$$$ == + == ·@$$$$$$$@@@@x $$$$@ @$$$$@· == + == ·@$$$$$$@@*· $$$$$= x$$$$$@· == + == ·@$$$$$$ ~*@@$$$$$@@$$$$$$$$$$$$$$%$@@$$$$$@· == + == ·@$$$$$% ~=@@@@$$$$$$$$$$@@@@@@@@@@@@@@$$$$$$$$@· == + == ·@$$$$$@* ·*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==+· $@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@ ·+== + xx== *@@@@@@@@@%~ +$@@@@@@@@$+ ~%@@@@@@@@@* ==xx + ===+ +=== + ===+ ++%% %%++ +=== + ++**=%+++x++**%*=+++===*==+xx+==*===+++=*%**++++++****++ + ++++====++ ++++==++ ++====+++x + + + diff --git a/src/build/framegen/frames/frame_161.txt b/src/build/framegen/frames/frame_161.txt new file mode 100644 index 0000000000..d96fee855f --- /dev/null +++ b/src/build/framegen/frames/frame_161.txt @@ -0,0 +1,41 @@ + + + xx++++============++++xx + ++====**=**%%%****%%%**=**====++ + ++====**=*o~ ~o*=**====++ + ======++ ++====== + x+====++ ~+*%$@@@@@@@@$%*+~ ++====++ + xx==== o%@@@@@@@$$$$$$$$$$@@@@@@@%o ====xx + ==== =@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@= ==== + =+== o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@o ==++ + x+==x· x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ·x==+x + ==++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++== + xx== o@$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ==xx + ++++ x@$$$$@@%%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ++++ + ==+~ @$$$$$ o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+== + == $$$$$@$ o%@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@% x@$$$$$@*· +@$$$$$@ == + == ·@$$$$$$@@@@*· $$$$@~ @$$$$@· == + == ·@$$$$$$@@$+ $$$$$+ @$$$$@· == + == ·@$$$$$$x x%@$$$$$@@*++++++++++++++=$@$$$$$@· == + == ·@$$$$$$ o%@@@@$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@· == + == ·@$$$$$$x o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==+· @@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@ ·x== + xx++ ·%@@@@@@@@@%o =@@@@@@@@@@= o%@@@@@@@@@%~ ++++ + ==+x ·~~ ·~~· ~~~ x+== + ==++ x+%% %%++ ++== + ++**=%++ooox==*===++**=%++xoox++%=**++===*==xooo++%=**++ + ++++==++++ x+++====+++x ++++====++ + + + diff --git a/src/build/framegen/frames/frame_162.txt b/src/build/framegen/frames/frame_162.txt new file mode 100644 index 0000000000..1bac9f41a4 --- /dev/null +++ b/src/build/framegen/frames/frame_162.txt @@ -0,0 +1,41 @@ + + + ++++++++++++++++++++ + ++++==****=***%%%%***=****==++++ + ++++=====%==o~ ~o==%=====++++ + ++====+= =+====++ + =====+ ~x=**%%%%**=x~ +===== + ====++ o*$@@@@@@@@@@@@@@@@@@@@$*o ++==== + ====o~ ·*@@@@$$$$$$$$$$$$$$$$$$$$$$@@@@*· ~o==== + ++==x· =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= ·x==++ + xx==+o %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% o+==xx + =+++ *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* +++= + ox==x· @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ·x==xo + ++++ @$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++++ + ==+o @$$$$$x o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+== + == %$$$$@% o*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$% == + == @$$$$$$x o%@$$$$$@@=++++++++++++++=$@$$$$$@ == + == $$$$$$$@@$=· $$$$@x @$$$$$ == + == ·@$$$$$$@@@@*· $$$$@~ @$$$$@· == + == ·@$$$$$@% x@$$$$$@*~ ·+@$$$$$@· == + == ·@$$$$@% x%@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$ o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@$%%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==x ·@@$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@~ == + x+++ x$@@@@@@@@@@= o%@@@@@@@@@@%o =@@@@@@@@@@$x ++++ + ==+o ox++o ~x++x~ o++xo o+== + **+x ** ** x+** + ++**==o~ ++*+**++**+*xo ox*+**++**=*++ ~o=+**++ + ++==****++++ ++==****==++ x+++=***==++ + + + diff --git a/src/build/framegen/frames/frame_163.txt b/src/build/framegen/frames/frame_163.txt new file mode 100644 index 0000000000..e2828f817e --- /dev/null +++ b/src/build/framegen/frames/frame_163.txt @@ -0,0 +1,41 @@ + + + ++++++++++++++++ + ++++==****************==++++ + ++===****%++~~ ~~++%***====++ + ++=====*+x x+*=====++ + ++====xx ·ox+====+xo· xx====++ + ====++ x%$@@@@@@@@@@@@@@@@@@$%x ++==== + ++==+x x$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$x x+==++ + ++==+o x$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$x o+==++ + ox==++ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ++==xo + +++= x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x =+++ + o ==+· $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ·+==xo + ++== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==++ + ==+x @$$$$@= =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+== + == *$$$$$$ =$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$* == + == @$$$$$$ ·=@@$$$$$@@$%%%%%%%%%%%%%%%@@$$$$$@ == + == $$$$$$$@@*~ $$$$$= o@$$$$$ == + == ~@$$$$$$$@@@@x $$$$@ @$$$$@~ == + == ·@$$$$$@$x %$$$$$@x o$$$$$$@· == + == ·@$$$$$$ +$@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@%+=$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == o@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@x == + ++++ =@@@@@@@@@@@%o +$@@@@@@@@@@$+ o%@@@@@@@@@@@* ++++ + **x~ ~+===+o x====x o+=*=+~ ~+** + **+x ++ ++ x+** + ==**++ ~o*+**==**+= =+**==**+*o~ ++==== + +==****===++ ++=******=++ +++==****==+ + + + diff --git a/src/build/framegen/frames/frame_164.txt b/src/build/framegen/frames/frame_164.txt new file mode 100644 index 0000000000..93246f70b9 --- /dev/null +++ b/src/build/framegen/frames/frame_164.txt @@ -0,0 +1,41 @@ + + + ++++++++ + ++++==************==++++ + ++++****=**%=+xo~~~~ox+=%**=****++++ + xx++===*== =+*===++xx + ++====++ ·~~~~· ++====++ + ++===+ x*$@@@@@@@@@@@@@@@@$*x +===++ + ++==++ =@@@@@$$$$$$$$$$$$$$$$$$@@@@@= ++==++ + x+===+ *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* +===+x + ==++ ~$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$~ ++== + ++== @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==++ + ==+o *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* o+== + ++== $@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ==++ + +++x $@$$$@$o x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ x+++ + ==x· +@$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ·x== + == @$$$$$$ +$@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$@@%x x$$$$$% =$$$$$$ == + == ·@$$$$$$$@@@@= $@$$@ @$$$$@· == + == ·@$$$$$@@*~ +$$$$$% *$$$$$@· == + == ·@$$$$$$ ·=@@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@· == + == ·@$$$$$$ ·=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$@@x·~=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == =@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@* == + +++x $@@@@@$$@@@@@*x~o+%@@@@@$$@@@@@%+o~o=$@@@@$$$@@@@$· x+++ + **~· ~=%$$$%=o +*$$$$*+ o=%$$$%=~ ** + xx**o~ ~·**xx + ox**+* ++****=*+x x+*=****++ *+**xx + ++==*%%%%%**== x++=*%%%%%%*=++x ==**%%%%%*==++ + + + diff --git a/src/build/framegen/frames/frame_165.txt b/src/build/framegen/frames/frame_165.txt new file mode 100644 index 0000000000..35acb52511 --- /dev/null +++ b/src/build/framegen/frames/frame_165.txt @@ -0,0 +1,41 @@ + + + + x+++====********====++++ + ++==**=**%**++++++++**%**=**==++ + ++=*=*=* *=*=*=++ + x+====+= =+====+x + ++===+ ·+%$@@@@@@@@@@@@@@$%+· +===++ + x+==++ x%@@@@@$$$$$$$$$$$$$$$$@@@@@%x ++==+x + xx==++ +@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@+ ++==xx + ==++ %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ++== + ++== $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ ==++ + ==+x +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ x+== + ++== %@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ==++ + ++++ %@$$$@@+~o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++++ + ==+· x@$$$$$ ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ·+== + == @$$$$$$ ~*@@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$@@*· =$$$$$$· *$$$$$$ == + == ·@$$$$$$$@@@@+ $$$$@ @$$$$@· == + == ·@$$$$$$@%x o$$$$$* =$$$$$@· == + == ·@$$$$$$ +$@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@· == + == ·@$$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$@$~ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == %@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@% == + +++o o@@@@$$$$$@@@@%*+=*$@@@$$$$$$@@@$*=+=%@@@@$$$$$@@@@x o+++ + ** +%@@@@@%+ o*$@@@@$*o +%@@@@@%+ **xo + x+** **++ + x+**+= ox****== ==****xo =+**++ + ++**%%%%%%%*==+x ++==*%%%%%%%==++ ++==*%%%%%%%**=+ + + + diff --git a/src/build/framegen/frames/frame_166.txt b/src/build/framegen/frames/frame_166.txt new file mode 100644 index 0000000000..1cb8aec8c0 --- /dev/null +++ b/src/build/framegen/frames/frame_166.txt @@ -0,0 +1,41 @@ + + + + ++++++========++++++ + ++==*****%%%********%%%*****==++ + ++==**=*=+ ++*=**==++ + ++**+= =+**++ + xx====ox ~=%$@@@@@@@@@@@@$%=~ xo====xx + x+===+ ·=@@@@@@$$$$$$$$$$$$$$@@@@@@=· +===+x + ===+ ~%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@%~ +=== + ++== =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= ==++ + x+== =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ==+x + ==+x ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ x+== + xx== =@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xx + ++++ =@$$$$@*++$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ++++ + ==+· ~@$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ·+== + == @$$$$$$ +$@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$@@x %$$$$$@x ~$$$$$$$ == + == ·@$$$$$$$@@@@x $$$$@ @$$$$@· == + == ·@$$$$$$@@=~ $$$$$= o@$$$$@· == + == ·@$$$$$$ ~=@@$$$$$@@$%%%%%%%%%%%%%%$@@$$$$$@· == + == ·@$$$$$$ ·*@@@@$$$$$$$$$$@@@@@@@@@@@@@@@$$$$$$$@· == + == ·@$$$$$@* ·=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$$$@@$$$$$$$$$$$$$$$@@ == + ==+~ %@@@$$$$$$$$@@@$$$@@@@$$$$$$$$@@@@$$$@@@$$$$$$$$@@@% ·+== + xx== +$@@@@@@@$+ ~*@@@@@@@@*~ +$@@@@@@@$+ ==xx + ++=+ +=++ + ++**oo ==$%++ ++$$== oo==++ + x+==*%=*====****++++++*%**====**%*++++++**=*====*=%*==+x + ++++++ ++++++++ ++++++ + + diff --git a/src/build/framegen/frames/frame_167.txt b/src/build/framegen/frames/frame_167.txt new file mode 100644 index 0000000000..f61e8743c1 --- /dev/null +++ b/src/build/framegen/frames/frame_167.txt @@ -0,0 +1,41 @@ + + + + x ++++++===+++++++ x + xx++*******%*%%*%%%*%*******++xx + xx+=****== ==****=+xx + ++**+*x x*+**++ + ====x+ x*%$@@@@@@@@@@$%*x +x==== + ==== +$@@@@@@$$$$$$$$$$$$@@@@@@$+ ==== + ==++ *@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@* ++== + ++== +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ==++ + x+==o· +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ·x==xx + ==++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++== + ox== +@$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + ++++ +@$$$$@%=*$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ++++ + ==+· @$$$$$ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ·+== + == @$$$$@$ ·=@@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ == + == @$$$$$@$o ~$$$$$$@+ o@$$$$$@ == + == ·@$$$$$$$@@@$o $$$$@ @$$$$@· == + == ·@$$$$$$@@*x $$$$@= ~@$$$$@· == + == ·@$$$$$$~ +$@$$$$$@@%***************@@$$$$$@· == + == ·@$$$$@$ +$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@· == + == ·@$$$$$$= +$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==x· $@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@ ·x== + xx== *@@@@@@@@@%o =@@@@@@@@@@= o%@@@@@@@@@%· +++x + ==++ ·~· ~~ ·~· x+== + ==++ x+%% %%+x ++== + ++**=*++xox+==%*==++===%==xoox==%===++==*%==+xoo++*=**++ + +++++===++ +++==+++ +++===++++ + + diff --git a/src/build/framegen/frames/frame_168.txt b/src/build/framegen/frames/frame_168.txt new file mode 100644 index 0000000000..c37a57b0e5 --- /dev/null +++ b/src/build/framegen/frames/frame_168.txt @@ -0,0 +1,41 @@ + + + + xx+++++++++++++x + ++==*****%%%%%%%%%%*****==++ + ++**=*=* *=*=**++ + ++**=*++ ++*=**++ + ++**x+ ~+*$$@@@@@@@@$$*+~ +x**++ + ==== o%@@@@@@@$$$$$$$$$$@@@@@@@%o ==== + ++== =@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@= ==++ + ++== o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@o ==++ + ox==x· x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ·x==xo + ++++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++++ + == o@$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + ++++ x@$$$$@$%%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ++++ + ==+~ @$$$$$ o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+== + == $$$$$$$ o*@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@%· o@$$$$$@*· +@$$$$$@ == + == ·@$$$$$$$@@@*~ $$$$@~ @$$$$@· == + == ·@$$$$$$@@$= $$$$@+ @$$$$@· == + == ·@$$$$$$x x%@$$$$$@@*===============$@$$$$$@· == + == ·@$$$$@% o%@@@@$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@· == + == ·@$$$$$$x o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == o@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@x == + ++++ =@@@@@@@@@@@*o ·+$@@@@@@@@@@$+· o*@@@@@@@@@@@* ++++ + **+~ ~+=*=+o ·x=**=x· o+=*=+~ ·+** + **xx ++ ++ o+**xx + ====++ *=**==**+= =+**==**=* ++==== + +==**%*==+++ ++=******=++ ++=***%%*==+ + + diff --git a/src/build/framegen/frames/frame_169.txt b/src/build/framegen/frames/frame_169.txt new file mode 100644 index 0000000000..39e0d9023e --- /dev/null +++ b/src/build/framegen/frames/frame_169.txt @@ -0,0 +1,41 @@ + + + + ++++++++++++ + ++++****%%%%%%%%%%%%****++++ + ++***%*%x~ ~x%*%***++ + ++**==++ ++==**++ + ++**++ ·x*%$$@@@@@@$$%*x· ++**++ + ++==~ o*@@@@@@@@$$$$$$$$@@@@@@@@*o ~==++ + ++== =@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@= ==++ + ++== ~$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$~ ==++ + ==x· o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ·x== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == o@$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + x+++ o@$$$$@$%%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o +++x + ==x~ @$$$$$ o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~x== + == $$$$$$% x%@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@% x@$$$$$@*~ ·=@$$$$$@ == + == ·@$$$$$$@@@@=· $$$$@~ @$$$$@· == + == ·@$$$$$$@@$=~ $$$$@x @$$$$@· == + == ·@$$$$$$x o%@$$$$$@@=+++++++++++++++$@$$$$$@· == + == ·@$$$$@% o*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$o ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == =@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@* == + +++x $@@@@$$$@@@@@=x~o+%@@@@@$$@@@@@%+o~x=@@@@@$$$@@@@$ x+++ + **~· ~=%$$$%=o x*$$$$*x o=%$$$%=~ ** + xx**o~ ~~**+x + x+**+= ++******+x x+*=****++ *+**+x + ++==*%%%%%%*== xx==*%%%%%%*==xx ==*%%%%%%*==++ + + diff --git a/src/build/framegen/frames/frame_170.txt b/src/build/framegen/frames/frame_170.txt new file mode 100644 index 0000000000..cd5c33eb07 --- /dev/null +++ b/src/build/framegen/frames/frame_170.txt @@ -0,0 +1,41 @@ + + + + +x++++x+ + ++==***%%%%%%%%%%***==++ + x+==**=*+o o+*=**==+x + ==**++ ++**== + ++**++ x=%$$@@@@@@$$%=x ++**++ + ++**oo ~=@@@@@@@@$$$$$$$$@@@@@@@@=~ oo**++ + ++== +$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$+ ==++ + ++=* ~$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$~ *=++ + ==x~ o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@o ~x== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ~@@$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ == + x+++ o@$$$$@@%$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o +++x + ++x~ @$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~x++ + == $$$$$$% +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@* +@$$$$$@%o~~~~~~~~~~~~~~~=@$$$$$@ == + == ·@$$$$$$@@@$= $$$$@~ @$$$$@· == + == ·@$$$$$$@@@*~ $$$$@x @$$$$@· == + == ·@$$$$$$+ ~*@$$$$$@$+xxxxxxxxxxxxxx+%@$$$$$@· == + == ·@$$$$@% ~*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$o ·=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$· == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == $@$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$@@ == + +++o =@@@$$$$$$$@@@@%**$@@@@$$$$$$@@@@$**%@@@@$$$$$$$@@@= ~+++ + xx== ~*@@@@@@@*o +$@@@@@@$+ o*@@@@@@@*~ ==xo + ++== ==++ + ++**++ =*%%== ==%%*= +x**++ + +=**%%%%%*%=**+x ++***%%%%%%***++ ++**=%*%%%%***=+ + xxxx xxxx xxxx + diff --git a/src/build/framegen/frames/frame_171.txt b/src/build/framegen/frames/frame_171.txt new file mode 100644 index 0000000000..fdaa337abe --- /dev/null +++ b/src/build/framegen/frames/frame_171.txt @@ -0,0 +1,41 @@ + + + + ++++ + ++==***%*%%%%%%*%***==++ + xx==***%+x x+%***==+x + ==**++ ++**== + ++**++ o=*%$$@@@@$$%*=o ++**++ + ++**oo ·=$@@@@@@@$$$$$$$$@@@@@@@$=· oo**++ + ++== +$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$+ ==++ + xx=* ·$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$· *=+x + ==+~ ~@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@~ ~+== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ~@@$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ == + xx++ o@$$$$@@$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ++xx + +++~ @$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + == $$$$$$% +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@* +@$$$$$@%o~~~~~~~~~~~~~~~=@$$$$$@ == + == ·$$$$$$$@@@$+ $$$$@~ @$$$$$· == + == ·@$$$$$$@@@*o $$$$@o @$$$$@· == + == ·@$$$$$$+ ~*@$$$$$@$+xxxxxxxxxxxxxx+%@$$$$$@· == + == ·@$$$$@% ·*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$~ ·=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+~ %@@$$$$$$$$$@@@@$$@@@@$$$$$$$$@@@@$$@@@@$$$$$$$$$@@% ·+== + xx== +$@@@@@@@@= o%@@@@@@@@%o =@@@@@@@@$+ ==+x + ++=+ +=++ + ++==o +=%$++ ++$%=+ o==++ + x+==**=*===*%***++xx++*%*%====%*%*++xx++***%*===***===++ + ++++++xx ++++++++ xx+==+++ + diff --git a/src/build/framegen/frames/frame_172.txt b/src/build/framegen/frames/frame_172.txt new file mode 100644 index 0000000000..99d6bf35c9 --- /dev/null +++ b/src/build/framegen/frames/frame_172.txt @@ -0,0 +1,41 @@ + + + + + ++++***%%$%%%%$%%***++++ + ++***%+x x+%***++ + ++**++ ++**++ + x+**++ o=*%$$@@@@$$%*=o ++**+x + ++**oo ·=$@@@@@@@$$$$$$$$@@@@@@@$=· oo**++ + ++** +$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$+ **++ + xx** ·$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$· **xx + ==+~ ~@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@~ ~+== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ~@@$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ == + xx++ ~@$$$$@@$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ++xx + +++~ @$$$$$ +%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + == $$$$$$% +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@* +@$$$$$@%o~~~~~~~~~~~~~~~=@$$$$$@ == + == ·$$$$$$$@@@$+ $$$$@~ @$$$$$· == + == ·@$$$$$$@@@*o $$$$@o @$$$$@· == + == ·@$$$$$@+ ~*@$$$$$@$+xxxxxxxxxxxxxx+%@$$$$$@· == + == ·@$$$$@% ·*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$~ ·=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==x· @@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@ ·x== + ++++ ~%@@@@@@@@@$x =@@@@@@@@@@= x$@@@@@@@@@%~ ++++ + ===x ~~~ ·~~· ~~~ x+== + ===+ x+%% %%+x +=== + ++**=%+x··~o==*===++***%=+o~·~+=%***++===*==o~··x+%=**++ + +++====+++ xx++====++xx ++======++ + diff --git a/src/build/framegen/frames/frame_173.txt b/src/build/framegen/frames/frame_173.txt new file mode 100644 index 0000000000..3c8dff4a21 --- /dev/null +++ b/src/build/framegen/frames/frame_173.txt @@ -0,0 +1,41 @@ + + + + + ++==***%%%%%%%%%%***==++ + +=***%+o o+%***=+ + +=**++ ++**=+ + xx**++ o=%%$$@@@@$$%%=o ++**xx + x+**oo ~=$@@@@@@@$$$$$$$$@@@@@@@$=~ oo**+x + xx** +$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$+ **xx + xx** ·$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$· **xx + ==+~ ~@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@~ ~+== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ~@@$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ == + xx++ o@$$$$@@$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ++xx + +++~ @$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + == $$$$$$% +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@* +@$$$$$@%o~~~~~~~~~~~~~~~=@$$$$$@ == + == ·$$$$$$$@@@$+ $$$$@~ @$$$$$· == + == ·@$$$$$$@@@*o $$$$@x @$$$$@· == + == ·@$$$$$$+ ~*@$$$$$@$+xxxxxxxxxxxxxx+%@$$$$$@· == + == ·@$$$$@% ~*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$o ·=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==x ·@@$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@~ == + ++++ x@@@@@@@@@@@= o%@@@@@@@@@@%o =@@@@@@@@@@@x ++xx + ==+o ~x+xo ·x++x· ox+xo o+== + **++ == =* x+** + +=**==o· ++*=**==**=*+o o+*=**==**=*+x ·o*+**=+ + ++==****++++ ++==****==++ ++++****==++ + diff --git a/src/build/framegen/frames/frame_174.txt b/src/build/framegen/frames/frame_174.txt new file mode 100644 index 0000000000..bd1a0a093f --- /dev/null +++ b/src/build/framegen/frames/frame_174.txt @@ -0,0 +1,41 @@ + + + + + ++==**%%%%%%%%%%%%**==++ + +=***%+o o+****=+ + +=**++ ++**=+ + xx**++ ·x=%$$@@@@@@$$%=x· ++**xx + x+**~ ~*@@@@@@@@$$$$$$$$@@@@@@@@*~ ~**+x + ++** +@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@+ **++ + xx** ~$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$~ **xx + ==x· o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ·x== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ~@$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + xx++ o@$$$$@@%$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ++xx + ++x~ @$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~x++ + == $$$$$$% x%@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@% +@$$$$$@*~··············~=@$$$$$@ == + == ·@$$$$$$@@@$=· $$$$@~ @$$$$@· == + == ·@$$$$$$@@$=~ $$$$@x @$$$$@· == + == ·@$$$$$$+ o*@$$$$$@$=+++++++++++++++%@$$$$$@· == + == ·@$$$$@% ~*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$o ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@x == + ++++ =@@@@@@@@@@@%o ~+@@@@@@@@@@@@+~ o%@@@@@@@@@@@* ++++ + **x~ ~+***=o ·x=**=x· o=***+o ·x** + **+x ++ ++ ox**xx + ====++ *=**==**+= =+**==**=* ++==== + ++=**%%**=++ ++=**%%**=++ ++=**%%*==++xx + diff --git a/src/build/framegen/frames/frame_175.txt b/src/build/framegen/frames/frame_175.txt new file mode 100644 index 0000000000..1765bcfbe7 --- /dev/null +++ b/src/build/framegen/frames/frame_175.txt @@ -0,0 +1,41 @@ + + + + + +++=*%%%%%%%%%%%%%%*=+++ + +=*%=*x· ·x*=%*=+ + ++**++ ++**++ + x+**++ ~+*%$@@@@@@@@$%*+~ ++**+x + ++** o*@@@@@@@$$$$$$$$$$@@@@@@@*o **++ + xx** =@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@= **xx + xx** o$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$o **xx + =*x· o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ·x*= + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + =* o@$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o *= + xx++ x@$$$$@$%%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ++xx + +++~ @$$$$$ o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + == $$$$$$$ x%@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@% x@$$$$$@*~ ·+@$$$$$@ == + == ·@$$$$$$@@@@*~ $$$$@~ @$$$$@· == + == ·@$$$$$$@@$=· $$$$@x @$$$$@· == + == ·@$$$$$$x o%@$$$$$@@=++++++++++++++=$@$$$$$@· == + == ·@$$$$@% o%@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$x o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == +@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@+ == + ++++ *@@@@@@@@@@@$+~ ·o*@@@@@@@@@@@@*o ~+$@@@@@@@@@@@% ++++ + **x· x*%%%*+ o=%%%%=o +*%%%*x ·o** + xx**xo xx xx ~x**xx + ===*+x *+****==++ ++==****+* o+*=** + ++==**%%%**=++ ++=*%%%%*=++ ++==*%%%%*==++ + diff --git a/src/build/framegen/frames/frame_176.txt b/src/build/framegen/frames/frame_176.txt new file mode 100644 index 0000000000..5dbc4d2484 --- /dev/null +++ b/src/build/framegen/frames/frame_176.txt @@ -0,0 +1,41 @@ + + + + ++++ + ++=**%*%%%%%%%%%%*%**=++ + x+==*%=* *=%*==+x + ===*++ ++*=== + x+**++ o=%$@@@@@@@@@@$%=o ++**+x + ++** x%@@@@@@$$$$$$$$$$$$@@@@@@%x **++ + ++== *@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@* ==++ + x+=* x@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x **+x + **x· +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ·x** + ++++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++++ + =* x@$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x *= + xx++ +@$$$$@$**$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ++xx + +++~ @$$$$$ ·*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + == @$$$$@$ ~*@@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ == + == @$$$$$@$~ o$$$$$$@= x@$$$$$@ == + == ·@$$$$$$$@@@%o $$$$@· @$$$$@· == + == ·@$$$$$$@@%x $$$$@+ ·@$$$$@· == + == ·@$$$$$$~ +$@$$$$$@@*==============*@@$$$$$@· == + == ·@$$$$@% x%@@@@$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@· == + == ·@$$$$$$+ x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == =@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@* == + +++x $@@@@@$@@@@@@=o~o+%@@@@@@@@@@@@%xo~o=@@@@@@$@@@@@$ x+++ + **o· ~=%$$$%=~ x*$$$$*x ~=%$$$%=~ ** + x+**o~ ~~**xx + xx**+= ++=***=*+x x+*=***=++ ==**xx + ++==*%%%%%**== ++++*%%%%%%*++++ ==**%%%%%*==++ + diff --git a/src/build/framegen/frames/frame_177.txt b/src/build/framegen/frames/frame_177.txt new file mode 100644 index 0000000000..828afeb638 --- /dev/null +++ b/src/build/framegen/frames/frame_177.txt @@ -0,0 +1,41 @@ + + + + x++++++x + +===*%%%%%****%%%%%*===+ + ++===*+= =+*===++ + ===* *=== + x+**x+ ·+*$@@@@@@@@@@@@$*+· xx**++ + ++*= +$@@@@@@$$$$$$$$$$$$@@@@@@$+ =*++ + ++== ·%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@%· =*++ + ++== =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= ==++ + **o· =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ·o** + ++++ ·@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@· ++++ + =* +@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ *= + x+++ =@$$$$@%==$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= +++x + +++· ·@$$$$$ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· ·+++ + == @$$$$@$ =$@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ == + == @$$$$$@$o ·%$$$$$@+ o$$$$$$@ == + == ·@$$$$$$$@@@$o $$$$@ @$$$$@· == + == ·@$$$$$$@@*o $$$$$= o@$$$$@· == + == ·@$$$$$$· =@@$$$$$@@%**************%@@$$$$$@· == + == ·@$$$$$$ =$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@· == + == ·@$$$$$$= +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@* == + +++x ·$@@@@$$$@@@@@*xoo+$@@@@@$$@@@@@$+oox*@@@@@$$$@@@@@~ x+++ + ** ~=%$$$$*o +%$$$$%+ o*$$$$%*o ** + xx**~~ **++ + ++**+= ++==**==x~ ~x==**==++ =+**+x + ++==*%%%%%%*== x+==*%%%%%%*==++ ==**%%%%%*==++ + diff --git a/src/build/framegen/frames/frame_178.txt b/src/build/framegen/frames/frame_178.txt new file mode 100644 index 0000000000..30b75b9a6e --- /dev/null +++ b/src/build/framegen/frames/frame_178.txt @@ -0,0 +1,41 @@ + + + + ++++++++++++ + ++++***%%%==++++==%%%***++++ + ++**=*+o o+*=**++ + x+**== ==**+x + ++** x*$@@@@@@@@@@@@@@$*x **++ + ++*+ o%@@@@@@$$$$$$$$$$$$$$@@@@@@%o +*++ + ++=+ x$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$x +=++ + ++== %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ==++ + ** %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ** + +++x x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x x+++ + == *@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* == + xx++ *@$$$$@=ox*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++xx + ==+· o@$$$$$ o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ·+== + == @$$$$$$ o%@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$@@= *$$$$$$~ %$$$$$$ == + == ·@$$$$$$$@@@@+ $$$$@ @$$$$@· == + == ·@$$$$$$@$+ ~$$$$$* +@$$$$@· == + == ·@$$$$$$ x%@@$$$$$@@@$$$$$$$$$$$$$$@@@$$$$$@· == + == ·@$$$$@$ o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$@%· o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@% == + +++x ~@@@@@$$$@@@@@%+xx=$@@@@$$$$@@@@$=xo+*@@@@@$$$@@@@@o x+++ + ** o*$$@$$%x ·=%$@@$%=· x%$@@$$*x ** + x+** **+x + xx**+= xx****== ==***=xx =+**++ + ++==*%%%%***++xx ++==*%*%%*%*==++ xx++*%*$%%%*==++ + diff --git a/src/build/framegen/frames/frame_179.txt b/src/build/framegen/frames/frame_179.txt new file mode 100644 index 0000000000..157ef6e500 --- /dev/null +++ b/src/build/framegen/frames/frame_179.txt @@ -0,0 +1,41 @@ + + + + ++++++++++++ + ++==*%*%==++xxxx++==%*%*==++ + ++**=* *=**++ + ++**+= =+**++ + ++== o=%@@@@@@@@@@@@@@@@%=o ==++ + ===+ +$@@@@@$$$$$$$$$$$$$$$$@@@@@$+ +=== + ++=+ =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= +=++ + ++=+ $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ +=++ + ** $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ ** + +++o =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= o+++ + == $@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + x+++ %@$$$@@x ·=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% +++x + ==+· x@$$$$$ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ·+== + == @$$$$$$ ·=@@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$@@*~ +$$$$$% *$$$$$$ == + == ·@$$$$$$$@@@@= $@$$@ @$$$$@· == + == ·@$$$$$$@%o x$$$$$% =$$$$$@· == + == ·@$$$$$$ +$@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@· == + == ·@$$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$@$o +$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@% == + +++x ~@@@@@$$$$@@@@%+xx=$@@@@$$$$@@@@$=xx+%@@@@$$$$@@@@@o x+++ + ** x*$$@@$%x ~=%$@@$%=~ x%$@@@$%x ** + x+** **+x + ++**+= xx****== ==****xx =+**++ + ++==*%%%%*%*=+++ ++==*%*%%*%*==++ +++=*%*%%%%**=++ + diff --git a/src/build/framegen/frames/frame_180.txt b/src/build/framegen/frames/frame_180.txt new file mode 100644 index 0000000000..01fa033f26 --- /dev/null +++ b/src/build/framegen/frames/frame_180.txt @@ -0,0 +1,41 @@ + + + + ++++===***==++++ + ==***%==xo ox==%***== + ===*++ ++*=== + ++**++ ·oxx++xxo· ++**++ + ===+ o=$@@@@@@@@@@@@@@@@@@$=o +=== + **++ o%@@@@@$$$$$$$$$$$$$$$$$$@@@@@%o ++** + ==+x ~$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$~ x+== + ++++ x@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x ++++ + xx== o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ==xx + ==x~ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ~x== + ox== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + +++x @$$$$@% ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + ==x =$$$$@$ ~*@@@@$$$$$$$$$$@@@@@@@@@@@@@@$$$$$$$$= x== + == @$$$$$$ o*@@$$$$$@@@$$$$$$$$$$$$$$$@@$$$$$@ == + == $$$$$$$@$= ·$$$$$* x@$$$$$ == + == ~@$$$$$$$@@@@x $$$$@ @$$$$@· == + == ·@$$$$$@@+ *$$$$$$o %$$$$$@· == + == ·@$$$$$$ x%@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@· == + == ·@$$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@*x+%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == %@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@% == + +++x ~@@@@@$$$$@@@@%+xx=$@@@@$$$$@@@@$=xx+%@@@@$$$$@@@@@o o+++ + ** x*$@@@$%x ~=%$@@$%=~ x%$@@@$%x ** + x+** **++ + x+**+= x+****== ==****xx =+**++ + ++***%%%%*%*==++ ++==*%*%%*%*==++ x+==*%*%%%%**=++ + diff --git a/src/build/framegen/frames/frame_181.txt b/src/build/framegen/frames/frame_181.txt new file mode 100644 index 0000000000..ea015b6282 --- /dev/null +++ b/src/build/framegen/frames/frame_181.txt @@ -0,0 +1,41 @@ + + + + ++++=***%%%%**==++++ + ++==*%**+x x+**%*==++ + ++**=* *=**++ + ==== ox=******=xo ==== + **++ ·=$@@@@@@@@@@@@@@@@@@@@$=· ++** + **+o =@@@@@$$$$$$$$$$$$$$$$$$$$@@@@@= o+** + **x~ +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ~x** + +++x *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* x+++ + xx== +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ==xx + ==x· @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ·x== + ox== @$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==xo + +++o @$$$$$+ x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + == %$$$$@$ +%@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$% == + == @$$$$$$~ +$@$$$$$@@%==============*@@$$$$$@ == + == $$$$$$$@@%x $$$$@+ ·@$$$$$ == + == ~@$$$$$$$@@@%o $$$$@· @$$$$@~ == + == ·@$$$$$@$~ o$$$$$$@= x@$$$$$@· == + == ·@$$$$@$ ~*@@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$ ·*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@$**$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@% == + +++x ~@@@@@$$$@@@@@%+xx=$@@@@$$$$@@@@$=xx+%@@@@$$$$@@@@@o x+++ + ** o*$$@@$%x ·=%$@@$%=· x%$@@$$*x ** + x+** **+x + ++**+= xx****== ==****xx =+**++ + ++==*%%%%*%*=+xx ++==*%%%%%%*==++ xx++*%*%%%%**=++ + diff --git a/src/build/framegen/frames/frame_182.txt b/src/build/framegen/frames/frame_182.txt new file mode 100644 index 0000000000..3545521c6f --- /dev/null +++ b/src/build/framegen/frames/frame_182.txt @@ -0,0 +1,41 @@ + + + + ++++=**%%%%%%%%%%**=++++ + +=***%+x x+%***=+ + ++**+= =+**++ + ox**+= o=*%$@@@@@@$%*=o =+**xo + ++**oo ·=$@@@@@@@$$$$$$$$@@@@@@@$=· oo**++ + x+** x$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$x **+x + ox** ·$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$· **xo + ==+~ ~@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@~ ~+== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ·@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@· == + xx++ ~@$$$$@@$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ++xx + +++~ @$$$$$ x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + == $$$$$@% +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@* +@$$$$$@%x~~~~~~~~~~~~~~o*@$$$$$@ == + == ·$$$$$$$@@@$+ $$$$@~ @$$$$$· == + == ·@$$$$$$@@@*o $$$$@x @$$$$@· == + == ·@$$$$$@+ ~*@$$$$$@$+xxxxxxxxxxxxxxx%@$$$$$@· == + == ·@$$$$@% ·=@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$~ ·=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@* == + +++x ·$@@@@$$$@@@@@*xox=$@@@@$$$$@@@@$=oox*@@@@@$$$@@@@@~ x+++ + ** o*%$$$$*x ·+%$$$$%+· x*$$$$$*o ** + x+**~ **+x + x+**+= +x*=**==x ==**==x+ =+**++ + ++==*%*%%%**++xx ++==*%%%%%%*==++ xx++**%%%*%*==++ + diff --git a/src/build/framegen/frames/frame_183.txt b/src/build/framegen/frames/frame_183.txt new file mode 100644 index 0000000000..5368c629ab --- /dev/null +++ b/src/build/framegen/frames/frame_183.txt @@ -0,0 +1,41 @@ + + + ++++ + ++==*%%%*%****%*%%%*==++ + x+==*%== ==%*==+x + ===*xo ox*=== + ++**x+ x*%$@@@@@@@@@@$%*x +x**++ + ++*= +$@@@@@@$$$$$$$$$$$$@@@@@@$+ =*++ + ++== *@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@* ==+x + xx== +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ==xx + **o· +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ·o** + ++++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++++ + ** +@$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ *= + xx++ +@$$$$@%=*$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ++xx + +++~ @$$$$$ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ·+++ + == @$$$$$$ ·=@@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ == + == @$$$$$@$o ~$$$$$$@+ x@$$$$$@ == + == ·@$$$$$$$@@@$o $$$$@· @$$$$@· == + == ·@$$$$$$@@%o $$$$$+ ~@$$$$@· == + == ·@$$$$$$~ +$@$$$$$@@%**************%@@$$$$$@· == + == ·@$$$$@$ +$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@· == + == ·@$$$$$@= +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@* == + +++x $@@@@@$$@@@@@*xoo+%@@@@@$$@@@@@%+o~x=@@@@@$$@@@@@$· x+++ + **o ~=%$$$%*o +%$$$$%+ o*%$$$%=~ ** + x+**~~ ~**+x + x+**== ++==**==+o ox==**==++ =+**++ + ++==*%%%%%**++xx ++==*%%%%%%*==++ x+++**%%%%%**=++ + diff --git a/src/build/framegen/frames/frame_184.txt b/src/build/framegen/frames/frame_184.txt new file mode 100644 index 0000000000..f92a332be4 --- /dev/null +++ b/src/build/framegen/frames/frame_184.txt @@ -0,0 +1,41 @@ + + + x ++++====++++ + ++==*%*%==++++++++==%*%*==++ + ++**=* *=**++ + x+**+= ·· =+**+x + ++== o=%@@@@@@@@@@@@@@@@%=o ==++ + ===+ +$@@@@@$$$$$$$$$$$$$$$$@@@@@$+ +=== + ++++ =@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@= ++++ + ++=+ $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ +=++ + ** $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ** + +++o =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= o+++ + == $@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + ++++ $@$$$@@x ·+$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + ==+· +@$$$$$ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ·+== + == @$$$$$$ =$@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$$@*o +$$$$$% *$$$$$$ == + == ·@$$$$$$$@@@@+ $$$$@ @$$$$@· == + == ·@$$$$$$@%o x$$$$$% =$$$$$@· == + == ·@$$$$$$ =@@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@· == + == ·@$$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$@$o +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == +@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@= == + ++++ %@@@@@@@@@@@$+o·~x*@@@@@@@@@@@@*o··~+$@@@@@@@@@@@% x+++ + **x· +*%%%*+· o=%%%%=o ·+%%$%*+ ·o** + x+**o~ oo oo oo**++ + x+**+=+~ =+******+x x+******+= ~x=+**++ + ++==**%%%***++xx ++==***%%***==++ xx++***%%%**==++ + diff --git a/src/build/framegen/frames/frame_185.txt b/src/build/framegen/frames/frame_185.txt new file mode 100644 index 0000000000..66aaba24cf --- /dev/null +++ b/src/build/framegen/frames/frame_185.txt @@ -0,0 +1,41 @@ + + + ++++==****==++++ + ==***%==xo ox==%***== + ===*++ ++*=== + ++**x+ ·oxx++xxo· +x**++ + ===+ o*$@@@@@@@@@@@@@@@@@@$*o +=== + **++ o%@@@@@$$$$$$$$$$$$$$$$$$@@@@@%o x+** + ==+o ~$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$~ o+== + ++++ +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ++++ + ox== o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ==xo + ==+~ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ~+== + ox== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + +++x @$$$$@* ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + ==x *$$$$$$ ~*@@@@$$$$$$$$$$@@@@@@@@@@@@@@$$$$$$$$* x== + == @$$$$$$ ~*@@$$$$$@@$%%%%%%%%%%%%%%$@@$$$$$@ == + == $$$$$$$@@= ·$$$$$* x$$$$$$ == + == ~@$$$$$$$@@@@x $$$$@ @$$$$@~ == + == ·@$$$$$@@+ *$$$$$$o ·$$$$$$@· == + == ·@$$$$$$ x$@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@· == + == ·@$$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$@@*x+%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@+ == + ++++ *@@@@@@@@@@@%x· ~=@@@@@@@@@@@@=~ x%@@@@@@@@@@@* ++++ + **x· o=*%%=x ~+*%%*+~ x=%%%=x ·x** + x+**xo ++ ++ ox**+x + x+**=*xx *+****=*++ ++==****+* x+==**++ + ++==********++xx ++==********==++ xx++********==++ + diff --git a/src/build/framegen/frames/frame_186.txt b/src/build/framegen/frames/frame_186.txt new file mode 100644 index 0000000000..d49b01d10e --- /dev/null +++ b/src/build/framegen/frames/frame_186.txt @@ -0,0 +1,41 @@ + + + +++=**%%%%%%%%**=+++ + ++***%==o· ·o==%***++ + ++**+* *+**++ + ==== o+=*%$$$$%*=+o ==== + x+**xx x*@@@@@@@@@@@@@@@@@@@@@@*x xx**+x + xx**~~ ~%@@@@$$$$$$$$$$$$$$$$$$$$$$@@@@%~ ~~**xx + **x· *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ·o** + ==+o %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% o+== + xx== *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ==xx + ==x· @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ·x== + xx== @$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==xx + +++o @$$$$$o ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + == %$$$$@% ~*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$% == + == @$$$$$$+ o*@$$$$$@$=+++++++++++++++%@$$$$$@ == + == $$$$$$$@@@=~ $$$$$x @$$$$$ == + == ·@$$$$$$@@@@= $$$$@~ @$$$$@· == + == ·@$$$$$@% +@$$$$$@%~··············~=@$$$$$@· == + == ·@$$$$@% x%@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@%%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ~@@$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@o == + ++++ +@@@@@@@@@@@=~ x$@@@@@@@@@@$x ·=@@@@@@@@@@@+ ++++ + **+~ o+=+x· o+==+o ·x+=+x ~x** + xx**+x == == x+**+x + xx**==+= ~x*=******+* *+******=*x~ =+==**xx + ++==********++ x+++********+++x ++********==++ + diff --git a/src/build/framegen/frames/frame_187.txt b/src/build/framegen/frames/frame_187.txt new file mode 100644 index 0000000000..a3fe2f9cb9 --- /dev/null +++ b/src/build/framegen/frames/frame_187.txt @@ -0,0 +1,41 @@ + + + ++==**%%*%%%%%%*%%**==++ + ==***%xx xx%***== + ++**++ ++**++ + xx**++ x=*$$@@@@@@$$*=x ++**+x + ++**~o ~*$@@@@@@@$$$$$$$$@@@@@@@$*~ oo**++ + ++** +$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$+ **++ + xx** ~$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$~ **xx + ==+~ ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ ~+== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ~@$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + x+++ o@$$$$@@%$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o +++x + +++~ @$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + == $$$$$$$ x$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@* +@$$$$$@%o··············~=@$$$$$@ == + == ·$$$$$$$@@@$= $$$$@~ @$$$$$· == + == ·@$$$$$$@@$*o $$$$@x @$$$$@· == + == ·@$$$$$$+ ~*@$$$$$@$=xxxxxxxxxxxxxx+%@$$$$$@· == + == ·@$$$$$$ ~*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$o ·=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==+· @@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@ x== + ++=+ o$@@@@@@@@@@+ ~*@@@@@@@@@@*~ +@@@@@@@@@@$x ++++ + ==+x ·oxo~ ~oo~ ~oxo· o+*= + x+**+x =* *= ++**xx + x+==**+*+o ++*=**==**=*+x x+*=**==**=*++ ~+*=**==xx + ++++******==++ ++++********++++ ++==******++++ + diff --git a/src/build/framegen/frames/frame_188.txt b/src/build/framegen/frames/frame_188.txt new file mode 100644 index 0000000000..383a1143f4 --- /dev/null +++ b/src/build/framegen/frames/frame_188.txt @@ -0,0 +1,41 @@ + + xx++++xx + ===*%%%%*%****%*%%%%*=== + ++**=%++ ++%=*=++ + ==** **== + x+**+x ~+*$@@@@@@@@@@@@$*+~ x+**+x + ++== =$@@@@@$$$$$$$$$$$$$$@@@@@$= ==++ + ++*= ~%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@%~ =*++ + x+== =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= ==+x + **o =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= o*= + ++++ ·@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@· ++++ + == =@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= == + x+++ =@$$$$@%+=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ++xx + +++· ·@$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· ·+++ + == @$$$$$$ +$@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ == + == $$$$$$@@x %$$$$$@x ~$$$$$$$ == + == ·@$$$$$$$@@@$x $$$$@ @$$$$@· == + == ·@$$$$$$@@*~ $$$$$= o$$$$$@· == + == ·@$$$$$$ =$@$$$$$@@$%%%%%%%%%%%%%%%@@$$$$$@· == + == ·@$$$$@% =@@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@· == + == ·@$$$$$@* =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==x· $@@$$$$$$$$$@@@@@@@@@@$$$$$$$$@@@@@@@@@@$$$$$$$$$@@$ ·+== + ++++ =@@@@@@@@@*· x$@@@@@@@@$x *@@@@@@@@@= ++++ + ==++ +=== + xx=*=+ ++%%xx xx%%++ +===xx + ==**=*==++++*=*=**==**=%=*++++*=%***==**=*=*++++==*=**==xx + ++++==****==++ ++==****==++ ++==****==++++ + diff --git a/src/build/framegen/frames/frame_189.txt b/src/build/framegen/frames/frame_189.txt new file mode 100644 index 0000000000..6fedc5596a --- /dev/null +++ b/src/build/framegen/frames/frame_189.txt @@ -0,0 +1,41 @@ + + xx++++++++xx + x+==*%*%*%========%*%*%*=++x + ++**=*+x x+*=**++ + xx**=* ==**xx + ++**o x*$@@@@@@@@@@@@@@$*x o**++ + ++=+ o*@@@@@$$$$$$$$$$$$$$$$@@@@@*o +=++ + ++=+ x$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$x +=++ + x+== *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ==+x + ** %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ** + +++x x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x x+++ + =* *@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* *= + ++++ *@$$$@@=ox*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++++ + ==+· o@$$$$$ o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ·+== + == @$$$$$$ o%@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$@@= *$$$$$$~ %$$$$$$ == + == ·@$$$$$$$@@@@+ $$$$@ @$$$$@· == + == ·@$$$$$$@$+ ~$$$$$* +$$$$$@· == + == ·@$$$$$$ o%@@$$$$$@@@$$$$$$$$$$$$$$$@@$$$$$@· == + == ·@$$$$@$ o%@@@@$$$$$$$$$$@$$$$$$$$$$$$@$$$$$$$$@· == + == ·@$$$$$$% o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$· == + == @@$$$$$$$$$$$$$$$@@$$$$$$$$$$$$$$$$@@$$$$$$$$$$$$$$$@@ == + ==+~ *@@@$$$$$$$@@@@$%%@@@@$$$$$$$$@@@@%%$@@@@$$$$$$$@@@* ~+== + ++== o%@@@@@@@$x ·*@@@@@@@@*· x$@@@@@@@%x ==++ + ===+ +=== + ox====o+ ==%%++ ++%%== +o====+x + ++**=**%****%***==++**=*=******=*=**++==***%****%*****++ + xx+++=====++++ ++++====++++ ++++======+++x + diff --git a/src/build/framegen/frames/frame_190.txt b/src/build/framegen/frames/frame_190.txt new file mode 100644 index 0000000000..95209a1f39 --- /dev/null +++ b/src/build/framegen/frames/frame_190.txt @@ -0,0 +1,41 @@ + + xx++++====++++xx + ++==*%**==++oooo++==**%*==++ + ++**=* *=**++ + ++**+= ···· =+**++ + ++== x=$@@@@@@@@@@@@@@@@$=x ==++ + ==++ =$@@@@$$$$$$$$$$$$$$$$$$@@@@$= ++== + ===+ *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* +=== + ++=+ ·$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$· +=++ + ox** $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ **xo + +++o =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= o+++ + == $@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + ++++ $@$$$@$x +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + ==+· +@$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ·+== + == @$$$$$$ =$@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$$@%o x$$$$$% =$$$$$$ == + == ·@$$$$$$$@@@@+ $$$$@ @$$$$@· == + == ·@$$$$$$@%o +$$$$$% *$$$$$@· == + == ·@$$$$$$ =$@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@· == + == ·@$$$$$$ =@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$@$x ·+$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == %@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@% == + ==+x ~@@@@$$$$$@@@@%+x+=$@@@@$$$$@@@@$=+x+%@@@@$$$$$@@@@o o+== + xx== x*$@@@$%x ~=$@@@@$=~ x%$@@@$*x ==xx + ==== ==== + ====+= xx*=**== ==**=*xx =+==== + ++*****%*%%*****==++==***%*%%*%***==++==*****%%%%*****++ + ++++++++++++ x++++++++++x ++++++++++++ + diff --git a/src/build/framegen/frames/frame_191.txt b/src/build/framegen/frames/frame_191.txt new file mode 100644 index 0000000000..7da0266e4d --- /dev/null +++ b/src/build/framegen/frames/frame_191.txt @@ -0,0 +1,41 @@ + + ++====****====++ + ==**%%=*++oo····~o++*=%%**++ + +=**== ==**=+ + ++**++ ·~~oo~~· ++**++ + ===+ +%@@@@@@@@@@@@@@@@@@%+ +=== + ===+ ·*@@@@@$$$$$$$$$$$$$$$$$$@@@@@*· ++== + ===x %@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@% x=== + ++=+ o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@o +=++ + xx== @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xx + ==+~ *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ~+== + == $@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + +++x $$$$$@$~ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ x+++ + ==x· =@$$$@$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ·x== + == @$$$$$$ x$@@$$$$$@@@$$$$$$$$$$$$$$@@@$$$$$@ == + == $$$$$$$@$x o$$$$$% +$$$$$$ == + == ·@$$$$$$$@@@@+ $$$$@ @$$$$@· == + == ·@$$$$$$@=· =$$$$$$ %$$$$$@· == + == ·@$$$$$$ ~*@@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@· == + == ·@$$$$$$ ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$@@+~o=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == +@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@= == + ==++ *@@@@@@@@@@@$+~ o*@@@@@@@@@@@@*o ·+$@@@@@@@@@@@% x+== + ++==x· x=%%%*x o=*%%*=o x*%%%*x ·x==++ + ====oo xx xx ~o==== + ====+=++ =+*==*==++ ++=====*+= ox=+==== + ++==**=*********++++==***%****%***==++++*********=**==++ + ++++++++++xx ++++++++++++ xx++++++++++ + diff --git a/src/build/framegen/frames/frame_192.txt b/src/build/framegen/frames/frame_192.txt new file mode 100644 index 0000000000..f03d9280bf --- /dev/null +++ b/src/build/framegen/frames/frame_192.txt @@ -0,0 +1,41 @@ + + ++====****====++ + +=***%==+o o+==%***=+ + ===*++ ++*=== + ++**x+ ·ooxxxxoo· ++**++ + ===+ o=$@@@@@@@@@@@@@@@@@@$=o +=== + **++ o%@@@@$$$$$$$$$$$$$$$$$$$$@@@@%o x+** + ==+x ~$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$~ x+== + ++++ +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ++++ + ox== ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ ==xo + ==+~ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ~x== + ox== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + +++x @$$$$@% ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + ==+ =@$$$@$ ~*@@@@$$$$$$$$$$@@@@@@@@@@@@@@$$$$$$$@= +== + == @$$$$$$ o%@@$$$$$@@$$$$$$$$$$$$$$$$@@$$$$$@ == + == $$$$$$$@$=· ·$$$$$* x$$$$$$ == + == ·@$$$$$$$@@@@+ $$$$@ @$$$$@· == + == ·@$$$$$@@+ *$$$$$$o $$$$$$@· == + == ·@$$$$$$ x%@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@· == + == ·@$$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$@@=x+%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==x· @@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@ x== + ++++ o$@@@@@@@@@$+ ·*@@@@@@@@@@*· +$@@@@@@@@@$o ++++ + x+==+x ·ooo~ ~oo~ ~ooo· o+==+x + ++==++ +** **+ x+==++ + ++====+=+x ++*========*+x x+*========*++ x+======++ + ++++==********==++++++==********==++++++==********====++ + ++++++++ ++++++++ ++++++++ + diff --git a/src/build/framegen/frames/frame_193.txt b/src/build/framegen/frames/frame_193.txt new file mode 100644 index 0000000000..eea662acae --- /dev/null +++ b/src/build/framegen/frames/frame_193.txt @@ -0,0 +1,41 @@ + + ++====****====++ + x++=***%==x~ ~x==%***=+xx + x+===*++ ++*===+x + ++**+x ~ox++++xo~ x+**++ + ==++ x*$@@@@@@@@@@@@@@@@@@$*x ++== + **+x x%@@@@$$$$$$$$$$$$$$$$$$$$@@@@%x x+** + ==+o o$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$o o+== + ++++ +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ++++ + xx== o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ==xx + ==+· $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ·+== + x+== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + +++x @$$$$@* =@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + ==x *$$$$@% ~=@@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$* x== + == @$$$$$$ ~*@@$$$$$@@$%%%%%%%%%%%%%%$@@$$$$$@ == + == $$$$$$$@@=~ $$$$$= o$$$$$$ == + == ~@$$$$$$$@@@@+ $$$$@ @$$$$@~ == + == ·@$$$$$@@x %@$$$$$x ~$$$$$$@· == + == ·@$$$$$$ +$@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@· == + == ·@$$$$$$ x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$@@*++%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+· $@@$$$$$$$$$@@@@@@@@@@$$$$$$$$@@@@@@@@@@$$$$$$$$$@@$ ·x== + ++++ =@@@@@@@@@* x$@@@@@@@@$x *@@@@@@@@@= ++++ + ox==++ ++==xx + ++===+ ++%%+o o+%%++ +===++ + ++=====%+=++++*=*========***++++***========*=*++++=+%=====++ + xx++==******====++ ++==********==++ ++====******==++xx + ++++ ++++ ++++xx + diff --git a/src/build/framegen/frames/frame_194.txt b/src/build/framegen/frames/frame_194.txt new file mode 100644 index 0000000000..1d1213448f --- /dev/null +++ b/src/build/framegen/frames/frame_194.txt @@ -0,0 +1,41 @@ + + xx++==********===+xx + x+==*%**=+~· ·~+=**%*==++ + x+=*=*+x x+*=*=+x + ++**xo ·ox+====+xo· ox**++ + **++ x*@@@@@@@@@@@@@@@@@@@@*x ++** + **+x x$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$x o+** + ==+o x$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$x o+== + ++++ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ++++ + xx== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==xx + ==+· $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ·+== + ox== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + +++x @$$$$@* =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + == *$$$$@% =@@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$* == + == @$$$$$$ =$@$$$$$@@$%%%%%%%%%%%%%%%@@$$$$$@ == + == $$$$$$$@@*~ $$$$$= o$$$$$$ == + == ~@$$$$$$$@@@$x $$$$@ @$$$$@~ == + == ·@$$$$$@@x %$$$$$@x ~$$$$$$@· == + == ·@$$$$$$ +$@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@%+=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == $@$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$@$ == + ==+o +@@@$$$$$$$@@@$***%@@@@$$$$$$@@@@%***$@@@$$$$$$$@@@+ o+== + ++== =$@@@@@$*~ +%@@@@@@%+ ~*$@@@@@$=· ==++ + ===+ +=== + ++====x+ *=**== =+**=* +x====+x + ++=====**%*****==========**%****%**=========******%**=====+x + ++++========++xx ++++========++++ xx++========++++ + + diff --git a/src/build/framegen/frames/frame_195.txt b/src/build/framegen/frames/frame_195.txt new file mode 100644 index 0000000000..7f66e31460 --- /dev/null +++ b/src/build/framegen/frames/frame_195.txt @@ -0,0 +1,41 @@ + + ++++==********===+++ + x+==*%**++~· ·~+=**%*==++ + xx**=%+x x+%=**xx + ++*=x ~ox+====+xo~ ox=*++ + **++ x%@@@@@@@@@@@@@@@@@@@@%x ++** + **+o +$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$+ o+** + =*+o x$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$x o+*= + ++++ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ++++ + ox== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==xo + ==+· $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ·+== + ox== @$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==xo + +++x @$$$$@= +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + == *$$$$@% +@@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$* == + == @$$$$$$· =$@$$$$$@@%%%%%%%%%%%%%%%%@@$$$$$@ == + == $$$$$$$@@*o $$$$$= ~$$$$$$ == + == ·@$$$$$$$@@@$x $$$$@ @$$$$@· == + == ·@$$$$$@@o ·%$$$$$@+ o$$$$$$@· == + == ·@$$$$$$ =$@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@%==$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@% == + ==+x ~$@@@@$$$$@@@@%+xx=$@@@@$$$$@@@@$=xx+*@@@@$$$$@@@@@~ x+== + ++== o*$@@@$*x ·=%$@@$%=· x*$@@@$*x ==++ + ==== =+== + xx====+= x+*===== ======+x =+====xx + ++====*%%%%**=====++====*%*%%*%*====++=====**%%%%*====++ + xx++++====++++ ++++====++++ ++++====++++x+ + + diff --git a/src/build/framegen/frames/frame_196.txt b/src/build/framegen/frames/frame_196.txt new file mode 100644 index 0000000000..9f6a2941d6 --- /dev/null +++ b/src/build/framegen/frames/frame_196.txt @@ -0,0 +1,41 @@ + + ++++==********===+++ + x+==*%**++~· ·~++**%*==++ + xx**=%+x x+%=**xx + ++==x ~ox+====+xo~ x==++ + **++ +%@@@@@@@@@@@@@@@@@@@@%+ ++** + **+o +$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$+ o+** + ==+~ x$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$x ~+*= + ++++ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ++++ + ox== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==xo + ==+· $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ·+== + ox== @$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==xo + +++x @$$$$@= +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + == *$$$$@% +$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$* == + == @$$$$$$· =$@$$$$$@@%%%%%%%%%%%%%%%%@@$$$$$@ == + == $$$$$$$@@*o $$$$$= ~$$$$$$ == + == ~@$$$$$$$@@@$x $$$$@ @$$$$@~ == + == ·@$$$$$@@o ·$$$$$$@+ o$$$$$$@· == + == ·@$$$$$$ =$@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@%==$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@+ == + ==++ *@@@@@@@@@@@%x ~=@@@@@@@@@@@@=~ x%@@@@@@@@@@@% ++== + xx==+· o=***=x ~+****+~ x=***=x ·x==++ + ====xo xx x+ ox==== + ======+x =+=*====++ ++====*=+= x+*===== + ++==***%********++++==************==++++********%***==++ + ++++++++++ +x++++++++x+ ++++++++++ + + diff --git a/src/build/framegen/frames/frame_197.txt b/src/build/framegen/frames/frame_197.txt new file mode 100644 index 0000000000..d7b48f2c1f --- /dev/null +++ b/src/build/framegen/frames/frame_197.txt @@ -0,0 +1,41 @@ + + x+++==********===+xx + x+==*%**++~· ·~++**%*==++ + xx**=*+x x+*=**xx + ++**xo ·ox+====+xo· ox=*++ + **++ x%@@@@@@@@@@@@@@@@@@@@%x ++** + **+o +$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$+ o+** + =*+o x$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$x o+*= + ++++ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ++++ + ox== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==xo + ==+· $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ·+== + ox== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + +++x @$$$$@* +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + == *$$$$@% =@@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$* == + == @$$$$$$· =$@$$$$$@@$%%%%%%%%%%%%%%%@@$$$$$@ == + == $$$$$$$@@*o $$$$$= ~$$$$$$ == + == ·@$$$$$$$@@@$x $$$$@ @$$$$@· == + == ·@$$$$$@@o ·%$$$$$@+ o$$$$$$@· == + == ·@$$$$$$ =$@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@%+=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ~@@$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@o == + ++++ +@@@@@@@@@@@*~ x$@@@@@@@@@@$x ·*@@@@@@@@@@@= ++++ + ox==+o x+=+x· o+==+o ·x+=+x· ~+==+x + ++==+x == == xx==++ + ++====++ ox*+==**==+= =+=***==+*xo ++====++ + ++==**********==++++++************++++++==**********==++ + ++++++++ xx++++xx ++++++++ + + diff --git a/src/build/framegen/frames/frame_198.txt b/src/build/framegen/frames/frame_198.txt new file mode 100644 index 0000000000..4195da5e9d --- /dev/null +++ b/src/build/framegen/frames/frame_198.txt @@ -0,0 +1,41 @@ + + xx++==********===+xx + x++=****==x· ·x==****=+++ + x+=*=*+x x+*=*=+x + ++**+x ~x++==++x~ x+**++ + **++ x*@@@@@@@@@@@@@@@@@@@@*x ++** + **+x x%@@@@$$$$$$$$$$$$$$$$$$$$@@@@%x xx** + ==+o o$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$o o+== + ++++ +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ++++ + xx== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==xx + ==+· $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ·+== + ox== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + +++x @$$$$@* =@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + == *$$$$@% ·=@@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$* x== + == @$$$$$$ ·*@@$$$$$@@$%%%%%%%%%%%%%%%@@$$$$$@ == + == $$$$$$$@@*~ $$$$$= o$$$$$$ == + == ~@$$$$$$$@@@@+ $$$$@ @$$$$@~ == + == ·@$$$$$@@x %$$$$$@x ~$$$$$$@· == + == ·@$$$$$$ +$@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@· == + == ·@$$$$$$ +$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$@@*+=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==x· @@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@ ·x== + ++++ ~%@@@@@@@@@$x ·*@@@@@@@@@@*· x$@@@@@@@@@%o ++++ + ox==+x ~oo· ~oo~ ·ooo x+==xo + ++==++ +%% %%+o ++==++ + ++=*===*xx ~ ++*=**==**+*++ ++*=**==**=*++ ~ xx*===*=++ + ++==********++xx ++==********==++ xx++********==++ + + + diff --git a/src/build/framegen/frames/frame_199.txt b/src/build/framegen/frames/frame_199.txt new file mode 100644 index 0000000000..d926eed9a2 --- /dev/null +++ b/src/build/framegen/frames/frame_199.txt @@ -0,0 +1,41 @@ + + x+++===******====++x + x+==***%=*xo ox==%***==++ + xx=*=*++ ++*=*=xx + ++**++ ~ox++++xo~ ++**++ + **++ o*$@@@@@@@@@@@@@@@@@@$*o ++** + **+x o%@@@@$$$$$$$$$$$$$$$$$$$$@@@@%o x+** + ==+o ~$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$~ o+== + ++++ +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ++++ + ox== o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ==xo + ==+~ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ~+== + ox== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + +++x @$$$$@* ·=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + ==+ *@$$$@% ~*@@@@$$$$$$$$$$@@@@@@@@@@@@@@@$$$$$$@* x== + == @$$$$$$ ~*@@$$$$$@@$%%%%%%%%%%%%%%$@@$$$$$@ == + == $$$$$$$@$=· $$$$$* x$$$$$$ == + == ·@$$$$$$$@@@@+ $$$$@ @$$$$@· == + == ·@$$$$$@@+ %@$$$$$o ·$$$$$$@· == + == ·@$$$$$$ x$@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@· == + == ·@$$$$$$ x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$@@*x+%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==+· @@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@ ·+== + ++++ %@@@@@@@@@%o =$@@@@@@@@$= ~%@@@@@@@@@%· ++++ + ==+x ·· ·· ··· x+== + x+==++ ++%% %*++ ++*=xx + xx==**=*++xxxx+=*=**==**=%=+xxxx+=*=**==**=*=+xxoo++*=**==xx + ++++******==++xx ++++********++++ xx++==******==++ + + + diff --git a/src/build/framegen/frames/frame_200.txt b/src/build/framegen/frames/frame_200.txt new file mode 100644 index 0000000000..86ebca6d11 --- /dev/null +++ b/src/build/framegen/frames/frame_200.txt @@ -0,0 +1,41 @@ + + xx++=====**=====++xx + +++=***%**++~· ·~++**%***++++ + xx==*%=+ +=%*==xx + ++**++ ~~oooo~~ ++**++ + ==== ·+%@@@@@@@@@@@@@@@@@@%+· ==== + **+x ~*@@@@@$$$$$$$$$$$$$$$$$$@@@@@*~ x+** + ==+x ·%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@%· x+== + ++=+ x@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x +=++ + ox== ·@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@· ==xo + ==+~ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ~+== + ox== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + +++x $$$$$@%· o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ x+++ + ==+· =@$$$@$ o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ·+== + == @$$$$$$ x%@@$$$$$@@@$$$$$$$$$$$$$$$@@$$$$$@ == + == $$$$$$$@$+ ~$$$$$* +$$$$$$ == + == ~@$$$$$$$@@@@+ $$$$@ @$$$$@~ == + == ·@$$$$$$@= *$$$$$$· %$$$$$@· == + == ·@$$$$$$ o%@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@· == + == ·@$$$$$$ o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$@@=ox*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+· $@@$$$$$$$$$@@@@@@@@@@$$$$$$$$@@@@@@@@@@$$$$$$$$$@@$ ·+== + +++= =$@@@@@@@@* x%@@@@@@@@%x *@@@@@@@@@* ++++ + ==++ +=== + xx=*=+ ++%%+x x+%%++ +=**+x + ++**=*=+++++=**=**++**=%==++++==%=**++**=**=+++++=*=**++ + xx++======++++ ++========++ ++++==**==++xx + + + diff --git a/src/build/framegen/frames/frame_201.txt b/src/build/framegen/frames/frame_201.txt new file mode 100644 index 0000000000..65a82d3bc1 --- /dev/null +++ b/src/build/framegen/frames/frame_201.txt @@ -0,0 +1,41 @@ + + xx++============++xx + ++++**%%**==xo~~~~ox==**%%**++++ + xx==**== ==**==xx + ++**+= ·~~~~· =+**++ + ===+ x*$@@@@@@@@@@@@@@@@$*x +=== + **++ =@@@@@$$$$$$$$$$$$$$$$$$@@@@@= ++** + ==++ *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ++== + ++=+ ~@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@~ +=++ + xx== @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + ==+o *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* o+== + ox== $@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ==xo + +++x $@$$$@$o x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ x+++ + ==+· +@$$$$$ +$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ·+== + == @$$$$$$ +$@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$$@%x o$$$$$% =$$$$$$ == + == ·@$$$$$$$@@@@+ $$$$@ @$$$$@· == + == ·@$$$$$$@*~ +$$$$$$ *$$$$$@· == + == ·@$$$$$$ ·*@@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@· == + == ·@$$$$$$ ·=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$@@+·~=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+· %@@$$$$$$$$$@@@@$$@@@@$$$$$$$$@@@@$$@@@@$$$$$$$$$@@$ ·+== + x+== +$@@@@@@@$* o%@@@@@@@@%o *$@@@@@@@$= ==++ + ==++ ++== + ===+ ==%%++ ++%%== +=== + ++**=%==++++****==++**=***++++***=**++==****++++==%***++ + ++======++++ x++======+++ ++++======++ + + + diff --git a/src/build/framegen/frames/frame_202.txt b/src/build/framegen/frames/frame_202.txt new file mode 100644 index 0000000000..d0f7f0f487 --- /dev/null +++ b/src/build/framegen/frames/frame_202.txt @@ -0,0 +1,41 @@ + + ++++++====++++++ + xx+=***%%%**==++++==**%%%***=+xx + ox==**=*x ox*=**==xo + ++**+= =+**++ + ==== ~+%$@@@@@@@@@@@@@@$%+~ ==== + ===+ x%@@@@@$$$$$$$$$$$$$$$$@@@@@%x +=== + ===+ +@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@+ +=== + ++=+ %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% +=++ + ox== %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ==xo + ==+x x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x x+== + ox== %@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ==xo + ++++ %@$$$@@+~o=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++++ + ==+· x@$$$$$ ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ·+== + == @$$$$$$ ~*@@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$$@=· =$$$$$$ %$$$$$$ == + == ·@$$$$$$$@@@@+ $$$$@ @$$$$@· == + == ·@$$$$$$@$x o$$$$$% +$$$$$@· == + == ·@$$$$$$ x$@@$$$$$@@@$$$$$$$$$$$$$$@@@$$$$$@· == + == ·@$$$$@$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$@$~ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+~ %@@@$$$$$$$$@@@@$$@@@@$$$$$$$$@@@@$$$@@@$$$$$$$$@@@% ·+== + ++== +$@@@@@@@$= ~%@@@@@@@@%~ =$@@@@@@@$+ ==xx + ===+ +=== + ==== ==%%++ ++%%+= ==== + ++**=***++==%***==++***%**++++**%***++==***%==++**%=**++ + x+++==++++ x+++====+++x ++++++++++ + + + diff --git a/src/build/framegen/frames/frame_203.txt b/src/build/framegen/frames/frame_203.txt new file mode 100644 index 0000000000..d3e8373453 --- /dev/null +++ b/src/build/framegen/frames/frame_203.txt @@ -0,0 +1,41 @@ + + ++++++++++++++++ + ++*****%*%=******=%*%*****++ + ++**=*++ x+*=**++ x + ++**+= =+**++ + ====ox o=%@@@@@@@@@@@@@@%=o xo==== + ===+ ~*@@@@@@$$$$$$$$$$$$$$@@@@@@*~ +=== + ===+ o$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$o +=== + ++== *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ==++ + ox== *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ==xo + ==+x o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o x+== + ox== =@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + ++++ *@$$$@@*x+%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++++ + ==x· ~@$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ·x== + == @$$$$$$ x$@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$@@+ %@$$$$$o ·$$$$$$$ == + == ·@$$$$$$$@@@@+ $$$$@ @$$$$@· == + == ·@$$$$$$@$=· $$$$$* x$$$$$@· == + == ·@$$$$$$ ~*@@$$$$$@@$%%%%%%%%%%%%%%$@@$$$$$@· == + == ·@$$$$@% ~*@@@@$$$$$$$$$$@@@@@@@@@@@@@@@$$$$$$$@· == + == ·@$$$$$@* ·=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$@@ == + ==+~ %@@@$$$$$$$$@@@$$$@@@@$$$$$$$$@@@@$$$@@@$$$$$$$$@@@% ·+== + xx== x%@@@@@@@$= ~*@@@@@@@@*~ =$@@@@@@@$+ ==xx + ===+ +=== + ====o ==%%++ ++%%== ==== + ++***%**====%***++++==***%====%***==++++***%====***=**++ + xx++++++++ ++====++ ++++=+++xx + + + diff --git a/src/build/framegen/frames/frame_204.txt b/src/build/framegen/frames/frame_204.txt new file mode 100644 index 0000000000..4333a6acd9 --- /dev/null +++ b/src/build/framegen/frames/frame_204.txt @@ -0,0 +1,41 @@ + + xx++++++++++++xx + ++==***%*%*%%%%%%*%*%***==++ + ++**=*== ==*=**++ + ++**=*++ ++*=**++ + ++**++ ~+*%@@@@@@@@@@%*+~ ++**++ + ==== o*@@@@@@@$$$$$$$$$$@@@@@@@*o ==== + ==== =@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@= ==== + ++== x@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x ==++ + xx==x· x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ·x==xx + ++++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++++ + ox== o@$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ==xo + ++++ x@$$$$@$*%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ++++ + ==+~ @$$$$$ o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+== + == $$$$$@$ o%@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@%· x$$$$$$@*· +@$$$$$@ == + == ·@$$$$$$$@@@*· $$$$@~ @$$$$@· == + == ·@$$$$$$@@$+ $$$$$x @$$$$@· == + == ·@$$$$$$o x%@$$$$$@@*===============$@$$$$$@· == + == ·@$$$$@% o%@@@@$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@· == + == ·@$$$$$$x o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$@@ == + ==+~ *@@@$$$$$$$@@@@$$$@@@@$$$$$$$$@@@@$$$@@@@$$$$$$$@@@% ·+== + xx== x%@@@@@@@$+ ~*@@@@@@@@*~ +$@@@@@@@$+ ==+x + ++=+ +=++ + ++==ox ==%%++ ++%%== o==++ + ++***%**====%***++++==***%====%***==++++***%====**%***++ + +++++++x ++++++++ ++++++++ + + + diff --git a/src/build/framegen/frames/frame_205.txt b/src/build/framegen/frames/frame_205.txt new file mode 100644 index 0000000000..ce0f1cf9a3 --- /dev/null +++ b/src/build/framegen/frames/frame_205.txt @@ -0,0 +1,41 @@ + + xx++++++++++++xx + ++==****%%*%%%%%%*%%****==++ + ++****=*++ ++*=****++ + ++====+= =+====++ + ++**++ ~x=%$$@@@@$$%=+~ ++**++ + ++==oo +%@@@@@@@@$$$$$$@@@@@@@@%+ oo==++ + ++== o$@@@@$$$$$$$$$$$$$$$$$$$$$$@@@@$o ==++ + ++==~ %@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@% ~==++ + ox==+~ $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ ~+==xo + ++++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++++ + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ++++ ~@$$$$@@$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ++xx + ==+~ @$$$$$· +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+== + == $$$$$@$ =$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$$= *@$$$$$@$xoooooooooooooox*@$$$$$@ == + == $$$$$$$@@@%x $$$$$o @$$$$$ == + == ·@$$$$$$@@@%x $$$$@o @$$$$@· == + == ·@$$$$$@= =@$$$$$@$xooooooooooooooo*@$$$$$@· == + == ·@$$$$@$ +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$· +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$@@ == + ==+~ %@@@$$$$$$$$@@@$$$@@@@$$$$$$$$@@@@$$$@@@$$$$$$$$@@@% ·+== + xx== +%@@@@@@@$= ~%@@@@@@@@%~ =$@@@@@@@$+ ==xo + ++=+ +=++ + ++*=ox ==%%++ ++%%== o==++ + x+***%**====%***++++==***%====%***==++++***%====**%***++ + ++++++++ ++++++++ ++++++++ + + + diff --git a/src/build/framegen/frames/frame_206.txt b/src/build/framegen/frames/frame_206.txt new file mode 100644 index 0000000000..6c1c344c2a --- /dev/null +++ b/src/build/framegen/frames/frame_206.txt @@ -0,0 +1,41 @@ + + ++++++++++++ + ++++==*******%%*******==++++ + ++==**=***++ ++***=**==++ + ++==**=*+o o+*=**==++ + ++====x ·o++====++o· ====++ + ++==++ +%@@@@@@@@@@@@@@@@@@@@%+ ++==++ + ++==xo +$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$+ ox==++ + ++==+~ x@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x ~+==++ + ==+x =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= x=== + ++== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==++ + ==x· $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ·x== + ++== @$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==++ + +++x @$$$$$= +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + == *$$$$@% =$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$* == + == @$$$$$$· =$@$$$$$@@%**************%@@$$$$$@ == + == $$$$$$$@@*o $$$$$= ~$$$$$$ == + == ~@$$$$$$$@@@$x $$$$@ @$$$$@~ == + == ·@$$$$$@$o ·$$$$$$@+ o$$$$$$@· == + == ·@$$$$$$ =@@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@%==$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+· %@@@$$$$$$$$@@@@$$@@@@$$$$$$$$@@@@$$@@@@$$$$$$$$$@@$ ·+== + xx== +$@@@@@@@$= o%@@@@@@@@%o =$@@@@@@@$= ==xx + ++=+ +=++ + ++== ==%%++ ++%%== ==++ + x+=*=***++==%***++xx==*%**====**%*==xx++***%==++***=**+x + ++++++++ ++++++++ ++++++++ + + + diff --git a/src/build/framegen/frames/frame_207.txt b/src/build/framegen/frames/frame_207.txt new file mode 100644 index 0000000000..167764b96b --- /dev/null +++ b/src/build/framegen/frames/frame_207.txt @@ -0,0 +1,41 @@ + + ++xxxx++ + xx++==****************==++xx + x+==**==*%=*++oo oo++*=%*==**==++ + xx++**=*+= =+*=**++xx + ++====++ ~oooo~ ++====++ + ++===+ +%@@@@@@@@@@@@@@@@@@%+ +===++ + ++==+x ·*@@@@@$$$$$$$$$$$$$$$$$$@@@@@*· x+==++ + x+==+x %@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@% x+==+x + ==++ o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@o +=== + ++== ·@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@· ==++ + ==+~ *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ~+== + ++== $@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ==++ + +++x $$$$$@$~ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ x+++ + ==x· =@$$$@$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ·+== + == @$$$$$$ x%@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$$@$x o$$$$$* +$$$$$$ == + == ·@$$$$$$$@@@@+ $$$$@ @$$$$@· == + == ·@$$$$$$@*· =$$$$$$· %$$$$$@· == + == ·@$$$$$$ ~*@@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@· == + == ·@$$$$$$ ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$@@+~o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+· $@@$$$$$$$$$@@@@$@@@@@$$$$$$$$@@@@@$@@@@$$$$$$$$$@@$ ·+== + xx== =$@@@@@@@@* o%@@@@@@@@%o *@@@@@@@@$= ==+x + ++=+ +=++ + ++== ==%%++ ++%%== ==++ + x+**=%*=++++%***++++==*%**++++**%*==++++***%++++==%=**++ + ++++++++ ++++++++ ++++++++ + + + diff --git a/src/build/framegen/frames/frame_208.txt b/src/build/framegen/frames/frame_208.txt new file mode 100644 index 0000000000..3d3c0e0e66 --- /dev/null +++ b/src/build/framegen/frames/frame_208.txt @@ -0,0 +1,41 @@ + + + xx++====********====++xx + ++==**=**%****++++****%**=**==++ + ++=====%+x x+*=====++ + ++====== ======++ + ++====xx o=%$@@@@@@@@@@@@$%=o xx====++ + ++===+ ·*$@@@@@$$$$$$$$$$$$$$@@@@@$*· +===++ + xx===+ o$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$o +===xo + ==++ *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ++== + ++== *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ==++ + ==+x ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ x+== + xx== =@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xx + ++++ *@$$$$@*x+%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++++ + ==+· ~@$$$$$ x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ·+== + == @$$$$$$ +$@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$@@+ %$$$$$@x ·$$$$$$$ == + == ·@$$$$$$$@@@@x $@$$@ @$$$$@· == + == ·@$$$$$$@@*· $$$$$= x$$$$$@· == + == ·@$$$$$$ ~*@@$$$$$@@$$$$$$$$$$$$$$%$@@$$$$$@· == + == ·@$$$$$% ~=@@@@$$$$$$$$$$@@@@@@@@@@@@@@$$$$$$$$@· == + == ·@$$$$$@* ·*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==x· $@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@ ·+== + xx== *@@@@@@@@@%~ +$@@@@@@@@$+ ~%@@@@@@@@@* ==xx + ===+ ++== + ===+ ++%% $$++ +=== + ++**=%+++x++****=+++===*==++x+==*===+++=*%**++++++****++ + ++++====++ ++====++ ++====+++x + + + diff --git a/src/build/framegen/frames/frame_209.txt b/src/build/framegen/frames/frame_209.txt new file mode 100644 index 0000000000..ad52681100 --- /dev/null +++ b/src/build/framegen/frames/frame_209.txt @@ -0,0 +1,41 @@ + + + xx++++============++++xx + ++====**=**%%%****%%%**=**====++ + ++====**=*o~ ~o*=**====++ + ======++ ++====== + x+====++ ~+*%$@@@@@@@@$%*+~ ++====++ + xx==== o%@@@@@@@$$$$$$$$$$@@@@@@@%o ====xx + ==== =@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@= ==== + =+== o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@o ==++ + x+==x· x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ·x==+x + ==++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++== + xx== o@$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ==xx + ++++ x@$$$$@@%%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ++++ + ==+~ @$$$$$ o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+== + == $$$$$@$ o%@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@% x@$$$$$@*· +@$$$$$@ == + == ·@$$$$$$@@@@*· $$$$@~ @$$$$@· == + == ·@$$$$$$@@$+ $$$$$+ @$$$$@· == + == ·@$$$$$$x x%@$$$$$@@*++++++++++++++=$@$$$$$@· == + == ·@$$$$$$ o%@@@@$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@· == + == ·@$$$$$$x o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==x· @@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@ ·x== + xx++ ·%@@@@@@@@@%o =@@@@@@@@@@= o%@@@@@@@@@%~ ++++ + ==+x ·~~ ·~~· ~~~ x+== + ==++ x+%% %%++ +=== + ++**=%++ooox==*===++**=%++xoox++%=**++===*==xooo++%=**++ + ++++==++++ ++++====+++x ++++==++++ + + + diff --git a/src/build/framegen/frames/frame_210.txt b/src/build/framegen/frames/frame_210.txt new file mode 100644 index 0000000000..d2748c7040 --- /dev/null +++ b/src/build/framegen/frames/frame_210.txt @@ -0,0 +1,41 @@ + + + xx++++++++++++++++++ + ++++==****=***%%%%***=****==++++ + ++++=====%==o~ ~o==%=====++++ + ++====+= =+====++ + =====+ ~x=**%%%%**=x~ +===== + ====++ o*$@@@@@@@@@@@@@@@@@@@@$*o ++==== + ====o~ ·*@@@@$$$$$$$$$$$$$$$$$$$$$$@@@@*· ~o==== + ++==x· =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= ·x==++ + xx==+o %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% o+==xx + =+++ *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* +++= + ox==x· @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ·x==xo + ++++ @$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++++ + ==+o @$$$$$x o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+== + == %$$$$@% o*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$% == + == @$$$$$$x x%@$$$$$@@=++++++++++++++=$@$$$$$@ == + == $$$$$$$@@$=· $$$$@x @$$$$$ == + == ·@$$$$$$@@@@*· $$$$@~ @$$$$@~ == + == ·@$$$$$@% x@$$$$$@*~ ·+@$$$$$@· == + == ·@$$$$@% x%@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$ o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@$%%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==x ·@@$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@~ == + x+++ x$@@@@@@@@@@= o%@@@@@@@@@@%o =@@@@@@@@@@$x ++++ + ==+o ox++o ~x++x~ o++xo o+== + **+x ** ** x+** + ++**==o~ ++*+**++**+*xo ox*=**++**=*++ ~o=+**=+ + ++==***=++++ ++==****==++ x+++=***==++ + + + diff --git a/src/build/framegen/frames/frame_211.txt b/src/build/framegen/frames/frame_211.txt new file mode 100644 index 0000000000..cb7316752c --- /dev/null +++ b/src/build/framegen/frames/frame_211.txt @@ -0,0 +1,41 @@ + + + ++++++++++++++++ + ++++==****************==++++ + ++===****%++~~ ~~++%***====++ + ++=====*+x x+*=====++ + ++====xx ·ox+====+xo· xx====++ + ====++ x%$@@@@@@@@@@@@@@@@@@$%x ++==== + ++==+x x$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$x x+==++ + ++==+o x$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$x o+==++ + ox==++ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ++==xo + +++= x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x =+++ + ox==+· $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ·+==xo + ++== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==++ + ==+x @$$$$@= =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+== + == *$$$$$$ =$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$* == + == @$$$$$$ ·=@@$$$$$@@$%%%%%%%%%%%%%%%@@$$$$$@ == + == $$$$$$$@@*~ $$$$$= o@$$$$$ == + == ~@$$$$$$$@@@@x $$$$@ @$$$$@~ == + == ·@$$$$$@$x %$$$$$@x o$$$$$$@· == + == ·@$$$$$$ +$@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@%+=$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == o@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@x == + ++++ =@@@@@@@@@@@%o +$@@@@@@@@@@$+ o%@@@@@@@@@@@* ++++ + **+~ ~+===+o x====x o+=*=+~ ~+** + **+x ++ ++ xx** + ==**++ ~o*+**==**+= =+**==**=*o~ ++==== + +==****==+++ ++=******=++ ++==*****==+ + + + diff --git a/src/build/framegen/frames/frame_212.txt b/src/build/framegen/frames/frame_212.txt new file mode 100644 index 0000000000..c082df71b1 --- /dev/null +++ b/src/build/framegen/frames/frame_212.txt @@ -0,0 +1,41 @@ + + + ++++++++ + ++++==************==++++ + ++++****=***=+xo~~~~ox+=%**=****++++ + xx++===*== =+*===++xx + ++====++ ·~~~~· ++====++ + ++===+ x*$@@@@@@@@@@@@@@@@$*x +===++ + ++==++ =@@@@@$$$$$$$$$$$$$$$$$$@@@@@= ++==++ + x+===+ *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* +===xx + ==++ ~$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$~ ++== + ++== @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==++ + ==+o *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* o+== + ++== $@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ==++ + +++x $@$$$@$o x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ x+++ + ==x· +@$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ·x== + == @$$$$$$ +$@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$@@%x x$$$$$% =$$$$$$ == + == ·@$$$$$$$@@@@= $@$$@ @$$$$@· == + == ·@$$$$$@@*~ +$$$$$% *$$$$$@· == + == ·@$$$$$$ ·=@@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@· == + == ·@$$$$$$ ·=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$@@x·~=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == =@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@* == + +++x $@@@@@$$@@@@@*x~o+%@@@@@$$@@@@@%+o~o=$@@@@$$$@@@@$· x+++ + **~· ~=%$$$%=o +*$$$$*+ o=%$$$%=~ ** + xx**o~ ~·**xx + xx**+* ++****=*+x x+*=****++ *+**xx + ++==*%%%%%**== x++=*%%%%%%*++++ ==**%%%%%*==++ + + + diff --git a/src/build/framegen/frames/frame_213.txt b/src/build/framegen/frames/frame_213.txt new file mode 100644 index 0000000000..61e1218701 --- /dev/null +++ b/src/build/framegen/frames/frame_213.txt @@ -0,0 +1,41 @@ + + + + x+++====********====++++ + ++==**=**%**++++++++**%**=**==++ + ++=*=*=* *=*=*=++ + x+====+= =+====++ + ++===+ ·+%$@@@@@@@@@@@@@@$%+· +===++ + x+==++ x%@@@@@$$$$$$$$$$$$$$$$@@@@@%x ++==+x + xx==++ +@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@+ ++==xx + ==++ %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ++== + ++== $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ ==++ + ==+x +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ x+== + ++== %@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ==++ + ++++ %@$$$@@+~o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++++ + ==+· x@$$$$$ ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ·+== + == @$$$$$$ ~*@@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$@@*· =$$$$$$· *$$$$$$ == + == ·@$$$$$$$@@@@+ $$$$@ @$$$$@· == + == ·@$$$$$$@%x o$$$$$* =$$$$$@· == + == ·@$$$$$$ +$@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@· == + == ·@$$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$@$~ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == %@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@$ == + +++o o@@@@$$$$$@@@@%*+=*$@@@$$$$$$@@@$*=+=%@@@@$$$$$@@@@x o+++ + ** +%@@@@@%+ o*$@@@@$*o +%@@@@@%+ **xo + x+** **++ + x+**++ ox****== ==****xo =+**++ + +=**%%%%%%%*==+x ++==*%%%%%%%==++ x+==*%%%%%%%**=+ + + + diff --git a/src/build/framegen/frames/frame_214.txt b/src/build/framegen/frames/frame_214.txt new file mode 100644 index 0000000000..7eb9facaa6 --- /dev/null +++ b/src/build/framegen/frames/frame_214.txt @@ -0,0 +1,41 @@ + + + + ++++++========++++++ + ++==*****%%%********%%%*****==++ + ++==**=*=+ ++*=**==++ + ++**+= =+**++ + xx====ox ~=%$@@@@@@@@@@@@$%=~ xo====xx + x+===+ ·=@@@@@@$$$$$$$$$$$$$$@@@@@@=· +===+x + ===+ ~%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@%~ +=== + ++== =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= ==++ + x+== =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ==+x + ==+x ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ x+== + xx== =@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xx + ++++ =@$$$$@*++$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ++++ + ==+· ~@$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ·+== + == @$$$$$$ +$@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$@@x %$$$$$@x ~$$$$$$$ == + == ·@$$$$$$$@@@@x $$$$@ @$$$$@· == + == ·@$$$$$$@@=~ $$$$$= o@$$$$@· == + == ·@$$$$$$ ~=@@$$$$$@@$%%%%%%%%%%%%%%$@@$$$$$@· == + == ·@$$$$$$ ·*@@@@$$$$$$$$$$@@@@@@@@@@@@@@@$$$$$$$@· == + == ·@$$$$$@* ·=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$$$@@$$$$$$$$$$$$$$$@@ == + ==+~ %@@@$$$$$$$$@@@$$$@@@@$$$$$$$$@@@@$$$@@@$$$$$$$$@@@% ·+== + xx== +$@@@@@@@$+ ~*@@@@@@@@*~ +$@@@@@@@$+ ==xx + ++=+ +=++ + ++**oo ==%%++ ++$$== oo==++ + x+==*%*%====****++++++*%**====**%*++++++**=*====*=%*==++ + ++++++ ++++++++ ++++++ + + diff --git a/src/build/framegen/frames/frame_215.txt b/src/build/framegen/frames/frame_215.txt new file mode 100644 index 0000000000..5ca0d40adc --- /dev/null +++ b/src/build/framegen/frames/frame_215.txt @@ -0,0 +1,41 @@ + + + + x ++++++====++++++ x + xx++*******%*%%**%%*%*******++xx + xx+=****== ==****=+xx + ++**+*x x*+**++ + ====x+ x*%$@@@@@@@@@@$%*x +x==== + ==== +$@@@@@@$$$$$$$$$$$$@@@@@@$+ ==== + ==++ *@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@* ++== + ++== +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ==++ + xx==x· +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ·x==xx + ==++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++== + xx== +@$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + ++++ +@$$$$@%=*$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ++++ + ==+· @$$$$$ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ·+== + == @$$$$@$ ·=@@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ == + == @$$$$$@$o ~$$$$$$@+ o@$$$$$@ == + == ·@$$$$$$$@@@$o $$$$@ @$$$$@· == + == ·@$$$$$$@@*x $$$$@= ~@$$$$@· == + == ·@$$$$$$~ +$@$$$$$@@%***************@@$$$$$@· == + == ·@$$$$@$ +$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@· == + == ·@$$$$$$= +$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==x· $@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@ ·x== + xx== *@@@@@@@@@%o =@@@@@@@@@@= o%@@@@@@@@@%· +++x + ==++ ·~· ~~ ·~· x+== + ==++ x+%% %%+x ++== + ++**=*++xox+==%*==++===%==xoox==%===++==*%==+xoo++*=**++ + +++++===++ +++==+++ +++===++++ + + diff --git a/src/build/framegen/frames/frame_216.txt b/src/build/framegen/frames/frame_216.txt new file mode 100644 index 0000000000..0b06bcd29b --- /dev/null +++ b/src/build/framegen/frames/frame_216.txt @@ -0,0 +1,41 @@ + + + + x+++++++++++++xx + ++==*****%%%%%%%%%%*****==++ + ++**=*=* *=*=**++ + ++**=*++ ++*=**++ + ++**x+ ~+*$$@@@@@@@@$$*+~ +x**++ + ==== o%@@@@@@@$$$$$$$$$$@@@@@@@%o ==== + ++== =@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@= ==++ + ++== o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@o ==++ + ox==x· x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ·x==xo + ++++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++++ + == o@$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + ++++ x@$$$$@$%%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ++++ + ==+~ @$$$$$ o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+== + == $$$$$$$ o*@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@%· o@$$$$$@*· +@$$$$$@ == + == ·@$$$$$$$@@@*~ $$$$@~ @$$$$@· == + == ·@$$$$$$@@$= $$$$@+ @$$$$@· == + == ·@$$$$$$x x%@$$$$$@@*===============$@$$$$$@· == + == ·@$$$$@% o%@@@@$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@· == + == ·@$$$$$$x o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == o@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@x == + ++++ =@@@@@@@@@@@*o ·+$@@@@@@@@@@$+· o*@@@@@@@@@@@* ++++ + **+~ ~+=*=+o ·x=**=x· o+=*=+~ ·+** + **xx ++ ++ o+**xx + ====++ *=**==**+= =+**==**+= ++==== + +==**%*=*=++ ++=******=++ +++=**%%*=== + + diff --git a/src/build/framegen/frames/frame_217.txt b/src/build/framegen/frames/frame_217.txt new file mode 100644 index 0000000000..3b406fd58c --- /dev/null +++ b/src/build/framegen/frames/frame_217.txt @@ -0,0 +1,41 @@ + + + + ++++++++++++ + ++++****%%%%%%%%%%%%****++++ + ++***%*%x~ ~x%*%***++ + ++**==++ ++==**++ + ++**++ ·x*%$$@@@@@@$$%*x· ++**++ + ++==~ o*@@@@@@@@$$$$$$$$@@@@@@@@*o ~==++ + ++== =@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@= ==++ + ++== ~$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$~ ==++ + ==x· o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ·x== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == o@$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + ++++ o@$$$$@$%%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o +++x + ==x~ @$$$$$ o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~x== + == $$$$$$% x%@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@% x@$$$$$@*~ ·=@$$$$$@ == + == ·@$$$$$$@@@@=· $$$$@~ @$$$$@· == + == ·@$$$$$$@@$=~ $$$$@x @$$$$@· == + == ·@$$$$$$x o%@$$$$$@@=+++++++++++++++$@$$$$$@· == + == ·@$$$$@% o*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$o ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == =@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@* == + +++x $@@@@$$$@@@@@=x~o+%@@@@@$$@@@@@%+o~x=@@@@@$$$@@@@$ x+++ + **~· ~=%$$$%=o x*$$$$*x o=%$$$%=~ ** + xx**o~ ~~**xx + x+**+= ++******+x x+*=****++ *+**+x + ++==*%%%%%%*== xx==*%%%%%%*==xx ==*%%%%%%*==++ + + diff --git a/src/build/framegen/frames/frame_218.txt b/src/build/framegen/frames/frame_218.txt new file mode 100644 index 0000000000..7c43145a8a --- /dev/null +++ b/src/build/framegen/frames/frame_218.txt @@ -0,0 +1,41 @@ + + + + +x++++++ + ++==***%%%%%%%%%%***==++ + x+==**=*+o o+*=**==+x + ==**++ ++**== + ++**++ x=%$$@@@@@@$$%=x ++**++ + ++**oo ~=@@@@@@@@$$$$$$$$@@@@@@@@=~ oo**++ + ++== +$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$+ ==++ + ++=* ~$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$~ *=++ + ==+~ o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@o ~x== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ~@@$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ == + ++++ o@$$$$@@%$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o +++x + ++x~ @$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~x++ + == $$$$$$% +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@* +@$$$$$@%o~~~~~~~~~~~~~~~=@$$$$$@ == + == ·@$$$$$$@@@$= $$$$@~ @$$$$@· == + == ·@$$$$$$@@@*~ $$$$@x @$$$$@· == + == ·@$$$$$$+ ~*@$$$$$@$+xxxxxxxxxxxxxx+%@$$$$$@· == + == ·@$$$$@% ~*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$o ·=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$· == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == $@$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$@@ == + +++o =@@@$$$$$$$@@@@%**$@@@@$$$$$$@@@@$**%@@@@$$$$$$$@@@= ~+++ + ox== ~*@@@@@@@*o +$@@@@@@$+ o*@@@@@@@*~ ==xo + ++== ==++ + ++**++ =*%%== ==%%*= +x**++ + +=**%%%%%*%=**+x ++***%%%%%%***++ x+**=%*%%%%***=+ + xxxx xxxx xxxx + diff --git a/src/build/framegen/frames/frame_219.txt b/src/build/framegen/frames/frame_219.txt new file mode 100644 index 0000000000..427ca641fd --- /dev/null +++ b/src/build/framegen/frames/frame_219.txt @@ -0,0 +1,41 @@ + + + + ++++ + ++==***%*%%%%%%*%***==++ + xx==***%+x x+%***==xx + ==**++ ++**== + ++**++ o=*%$$@@@@$$%*=o ++**++ + ++**oo ·=$@@@@@@@$$$$$$$$@@@@@@@$=· oo**++ + ++== +$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$+ ==++ + x+=* ·$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$· *=xx + ==+~ ~@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@~ ~+== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ~@@$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ == + xx++ ~@$$$$@@$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ++xx + +++~ @$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + == $$$$$$% +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@* +@$$$$$@%o~~~~~~~~~~~~~~~=@$$$$$@ == + == ·$$$$$$$@@@$+ $$$$@~ @$$$$$· == + == ·@$$$$$$@@@*o $$$$@o @$$$$@· == + == ·@$$$$$$+ ~*@$$$$$@$+xxxxxxxxxxxxxx+%@$$$$$@· == + == ·@$$$$@% ·*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$~ ·=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+· %@@$$$$$$$$$@@@@$$@@@@$$$$$$$$@@@@$$@@@@$$$$$$$$$@@% ·+== + xx== +$@@@@@@@@= o%@@@@@@@@%o =@@@@@@@@$+ ==+x + ++=+ +=++ + ++*=o +=%$++ ++$%=+ o==++ + xx==**=*===*%***++xx++*%*%====%*%*++xx++***%*===***===++ + ++++++xx ++++++++ xx++==++ + diff --git a/src/build/framegen/frames/frame_220.txt b/src/build/framegen/frames/frame_220.txt new file mode 100644 index 0000000000..bf319ce3de --- /dev/null +++ b/src/build/framegen/frames/frame_220.txt @@ -0,0 +1,41 @@ + + + + + ++++***%%$%%%%$%%***++++ + ++***%+x x+%***++ + ++**++ ++**++ + x+**++ o=*%$$@@@@$$%*=o ++**+x + ++**oo ·=$@@@@@@@$$$$$$$$@@@@@@@$=· oo**++ + ++** +$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$+ **++ + xx** ·$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$· **xx + ==+~ ~@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@~ ~+== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ~@@$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ == + xx++ ~@$$$$@@$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ++xx + +++~ @$$$$$ +%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + == $$$$$$% +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@* +@$$$$$@%o~~~~~~~~~~~~~~~=@$$$$$@ == + == ·$$$$$$$@@@$+ $$$$@~ @$$$$$· == + == ·@$$$$$$@@@*o $$$$@o @$$$$@· == + == ·@$$$$$@+ ~*@$$$$$@$+xxxxxxxxxxxxxx+%@$$$$$@· == + == ·@$$$$@% ·*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$~ ·=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==x· @@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@ ·x== + x+++ ~%@@@@@@@@@$x =@@@@@@@@@@= x$@@@@@@@@@%~ +++x + ===x ~~~ ·~~· ~~~ x+== + ===+ x+%% %%+x +=== + ++**=%+x··~o==*===++***%=+o~~o+=%***++===*==o~··x+%=**++ + ++=====+++ xx++====++xx +=======++ + diff --git a/src/build/framegen/frames/frame_221.txt b/src/build/framegen/frames/frame_221.txt new file mode 100644 index 0000000000..8d7db5aa0e --- /dev/null +++ b/src/build/framegen/frames/frame_221.txt @@ -0,0 +1,41 @@ + + + + + ++==***%%%%%%%%%%***==++ + +=***%+o o+%***=+ + +=**++ ++**=+ + xx**++ o=%%$$@@@@$$%%=o ++**xx + x+**oo ~=$@@@@@@@$$$$$$$$@@@@@@@$=~ oo**+x + ++** +$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$+ **xx + xx** ·$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$· **xx + ==+~ ~@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@~ ~+== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ~@@$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ == + xx++ o@$$$$@@$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ++xx + +++~ @$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + == $$$$$$% +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@* +@$$$$$@%o~~~~~~~~~~~~~~~=@$$$$$@ == + == ·$$$$$$$@@@$+ $$$$@~ @$$$$$· == + == ·@$$$$$$@@@*o $$$$@x @$$$$@· == + == ·@$$$$$$+ ~*@$$$$$@$+xxxxxxxxxxxxxx+%@$$$$$@· == + == ·@$$$$@% ~*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$o ·=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==x ·@@$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@~ == + ++++ x@@@@@@@@@@@= o%@@@@@@@@@@%o =@@@@@@@@@@@x ++xx + ==+o ~x+xo ·x++x· ox+xo o+== + **++ == =* x+** + +=**==o· ++*=**==**=*+o o+*=**==**=*+x ·o==**=+ + ++==****++++ ++==****==++ ++++****==++ + diff --git a/src/build/framegen/frames/frame_222.txt b/src/build/framegen/frames/frame_222.txt new file mode 100644 index 0000000000..7551522edd --- /dev/null +++ b/src/build/framegen/frames/frame_222.txt @@ -0,0 +1,41 @@ + + + + + ++==**%%%%%%%%%%%%**==++ + +=***%+o o+****=+ + +=**++ ++**=+ + xx**++ ·x=%$$@@@@@@$$%=x· ++**xx + x+**~ ~*@@@@@@@@$$$$$$$$@@@@@@@@*~ ~**+x + ++** +@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@+ **++ + xx** ~$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$~ **xx + ==x· o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ·x== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ~@$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + xx++ o@$$$$@@%$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ++xx + ++x~ @$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~x++ + == $$$$$$% x%@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@% +@$$$$$@*~··············~=@$$$$$@ == + == ·@$$$$$$@@@$=· $$$$@~ @$$$$@· == + == ·@$$$$$$@@$=~ $$$$@x @$$$$@· == + == ·@$$$$$$+ o*@$$$$$@$=+++++++++++++++%@$$$$$@· == + == ·@$$$$@% ~*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$o ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@x == + ++++ =@@@@@@@@@@@%o ~+@@@@@@@@@@@@+~ o%@@@@@@@@@@@* ++++ + **+~ ~+***=o ·x=**=x· o=***+o ·x** + **+x ++ ++ ox**xx + ====++ *=**==**+= =+**==**=* ++==== + ++=**%%**=++ ++=**%%**=++ ++=**%%%==++xx + diff --git a/src/build/framegen/frames/frame_223.txt b/src/build/framegen/frames/frame_223.txt new file mode 100644 index 0000000000..7ba0c68168 --- /dev/null +++ b/src/build/framegen/frames/frame_223.txt @@ -0,0 +1,41 @@ + + + + + +++=*%%%%%%%%%%%%%%*=+++ + +=*%=*x· ·x*=%*=+ + ++**++ ++**++ + x+**++ ~+*%$@@@@@@@@$%*+~ ++**+x + ++** o*@@@@@@@$$$$$$$$$$@@@@@@@*o **++ + xx** =@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@= **xx + xx** o$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$o **xx + =*x· o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ·x*= + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + =* o@$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o *= + xx++ x@$$$$@$%%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ++xx + +++~ @$$$$$ o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + == $$$$$$$ x%@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@% x@$$$$$@*~ ·+@$$$$$@ == + == ·@$$$$$$@@@@*· $$$$@~ @$$$$@· == + == ·@$$$$$$@@$=· $$$$@x @$$$$@· == + == ·@$$$$$$x o%@$$$$$@@=++++++++++++++=$@$$$$$@· == + == ·@$$$$@% o%@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$x o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == +@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@+ == + ++++ *@@@@@@@@@@@$+~ ·o*@@@@@@@@@@@@*o ~+$@@@@@@@@@@@% ++++ + **x· x*%%%*+ o=%%%%=o +*%%%*x ·o** + ox**xo xx xx ~o**xx + ===*+x *+****==++ ++==****+* o+*=== + ++==**%%%**=++ ++=**%%%*=++ ++=**%%%%*==++ + diff --git a/src/build/framegen/frames/frame_224.txt b/src/build/framegen/frames/frame_224.txt new file mode 100644 index 0000000000..814ec8673f --- /dev/null +++ b/src/build/framegen/frames/frame_224.txt @@ -0,0 +1,41 @@ + + + + ++++ + ++=**%*%%%%%%%%%%*%**=++ + x+==*%=* *=%*==+x + ===*++ ++*=== + x+**++ o=%$@@@@@@@@@@$%=o ++**+x + ++** x%@@@@@@$$$$$$$$$$$$@@@@@@%x **++ + ++== *@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@* ==++ + x+=* x@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x **+x + **x· +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ·x** + ++++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++++ + =* x@$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x *= + xx++ +@$$$$@$**$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ +++x + +++~ @$$$$$ ·*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + == @$$$$@$ ~*@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ == + == @$$$$$@$~ o$$$$$$@= x@$$$$$@ == + == ·@$$$$$$$@@@%o $$$$@· @$$$$@· == + == ·@$$$$$$@@%x $$$$@+ ·@$$$$@· == + == ·@$$$$$$~ +$@$$$$$@@*==============*@@$$$$$@· == + == ·@$$$$@% x%@@@@$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@· == + == ·@$$$$$$+ x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == =@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@* == + +++x $@@@@@$@@@@@@=o~o+%@@@@@@@@@@@@%xo~o=@@@@@@$@@@@@$ x+++ + **o· ~=%$$$%=~ x*$$$$*x ~=%$$$%=~ ** + x+**o~ ~~**xx + xx**+= ++=***=*+x xx*=***=++ ==**+x + ++==*%%%%%**== ++++*%%%%%%*++++ ==**%%%%%*==++ + diff --git a/src/build/framegen/frames/frame_225.txt b/src/build/framegen/frames/frame_225.txt new file mode 100644 index 0000000000..5c50111505 --- /dev/null +++ b/src/build/framegen/frames/frame_225.txt @@ -0,0 +1,41 @@ + + + + x++++++x + +===*%%%%%****%%%%%*===+ + ++===*+= =+*===+x + ===* *=== + ++**xx ·+*$@@@@@@@@@@@@$*+· xx**++ + ++== +$@@@@@@$$$$$$$$$$$$@@@@@@$+ ==++ + ++== ·%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@%· ==++ + x+== =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= ==++ + **o· =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ·o** + ++++ ·@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@· ++++ + =* +@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ *= + x+++ =@$$$$@%==$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= +++x + +++· ·@$$$$$ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· ·+++ + == @$$$$@$ =$@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ == + == @$$$$$@$o ·%$$$$$@+ o$$$$$$@ == + == ·@$$$$$$$@@@$o $$$$@ @$$$$@· == + == ·@$$$$$$@@*o $$$$$= o@$$$$@· == + == ·@$$$$$$· =@@$$$$$@@%**************%@@$$$$$@· == + == ·@$$$$$$ =$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@· == + == ·@$$$$$$= +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@* == + +++x ·$@@@@$$$@@@@@*xoo+$@@@@@$$@@@@@$+oox*@@@@@$$$@@@@@~ x+++ + ** ~=%$$$$*o +%$$$$%+ o*$$$$%*o ** + xx**~~ **++ + ++**+= ++==**==x~ ~x==**==++ =+**+x + ++==*%%%%%%*== x+==*%%%%%%*==+x ==*%%%%%%*==++ + diff --git a/src/build/framegen/frames/frame_226.txt b/src/build/framegen/frames/frame_226.txt new file mode 100644 index 0000000000..d27b633135 --- /dev/null +++ b/src/build/framegen/frames/frame_226.txt @@ -0,0 +1,41 @@ + + + + ++++++++++++ + ++++***%%%==++++==%%%***++++ + ++**=*+o o+*=**++ + x+**== ==**+x + ++** x*$@@@@@@@@@@@@@@$*x **++ + ++*+ o%@@@@@@$$$$$$$$$$$$$$@@@@@@%o +*++ + ++=+ x$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$x +=++ + ++== %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ==++ + ** %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ** + +++x x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x x+++ + == *@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* == + xx++ *@$$$$@=ox*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++xx + ==+· o@$$$$$ o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ·+== + == @$$$$$$ o%@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$@@= *$$$$$$~ %$$$$$$ == + == ·@$$$$$$$@@@@+ $$$$@ @$$$$@· == + == ·@$$$$$$@$+ ~$$$$$* +@$$$$@· == + == ·@$$$$$$ x%@@$$$$$@@@$$$$$$$$$$$$$$@@@$$$$$@· == + == ·@$$$$@$ o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$@%· o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@% == + +++x ~@@@@@$$$@@@@@%+xx=$@@@@$$$$@@@@$=xo+*@@@@@$$$@@@@@o x+++ + ** o*$$@$$%x ·=%$@@$%=· x%$@@$$*x ** + x+** **+x + xx**+= xx****== ==****xx =+**++ + ++==*%%%%***++xx ++==*%*%%*%*==++ xx++*%*%%%%*==++ + diff --git a/src/build/framegen/frames/frame_227.txt b/src/build/framegen/frames/frame_227.txt new file mode 100644 index 0000000000..c94758b19e --- /dev/null +++ b/src/build/framegen/frames/frame_227.txt @@ -0,0 +1,41 @@ + + + + ++++++++++++ + ++==*%*%==++xxxx++==%*%*==++ + ++**=* *=**++ + ++**+= =+**++ + ++== o=%@@@@@@@@@@@@@@@@%=o ==++ + ===+ +$@@@@@$$$$$$$$$$$$$$$$@@@@@$+ +=== + ++=+ =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= +=++ + ++=+ $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ +=++ + ** $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ ** + +++o =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= o+++ + == $@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + ++++ %@$$$@@x ·=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++++ + ==+· x@$$$$$ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ·+== + == @$$$$$$ ·=@@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$@@*~ +$$$$$% *$$$$$$ == + == ·@$$$$$$$@@@@= $@$$@ @$$$$@· == + == ·@$$$$$$@%o x$$$$$% =$$$$$@· == + == ·@$$$$$$ +$@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@· == + == ·@$$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$@$o +$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@% == + +++x ~@@@@@$$$$@@@@%+xx=$@@@@$$$$@@@@$=xx+%@@@@$$$$@@@@@o x+++ + ** x*$$@@$%x ~=%$@@$%=~ x%$@@@$%x ** + x+** **++ + ++**+= xx****== ==****+x =+**++ + ++==*%%%%*%*=+++ ++==*%*%%*%*==++ +++=*%*%%%%**=++ + diff --git a/src/build/framegen/frames/frame_228.txt b/src/build/framegen/frames/frame_228.txt new file mode 100644 index 0000000000..dafe701996 --- /dev/null +++ b/src/build/framegen/frames/frame_228.txt @@ -0,0 +1,41 @@ + + + + ++++===***==++++ + ==***%==xo ox==%***== + ===*++ ++*=== + ++**++ ·oxx++xxo· ++**++ + ===+ o=$@@@@@@@@@@@@@@@@@@$=o +=== + **++ o%@@@@@$$$$$$$$$$$$$$$$$$@@@@@%o ++** + ==+x ~$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$~ o+== + ++=+ x@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x +=++ + xx== o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ==xx + ==x~ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ~x== + ox== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + +++x @$$$$@% ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + ==x =$$$$@$ ~*@@@@$$$$$$$$$$@@@@@@@@@@@@@@$$$$$$$$= x== + == @$$$$$$ o*@@$$$$$@@@$$$$$$$$$$$$$$$@@$$$$$@ == + == $$$$$$$@$= ·$$$$$* x@$$$$$ == + == ·@$$$$$$$@@@@x $$$$@ @$$$$@~ == + == ·@$$$$$@@+ *$$$$$$o %$$$$$@· == + == ·@$$$$$$ x%@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@· == + == ·@$$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@*x+%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == %@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@% == + +++x ~@@@@@$$$$@@@@%+xx=$@@@@$$$$@@@@$=xx+%@@@@$$$$@@@@@o o+++ + ** x*$@@@$%x ~=%$@@$%=~ x%$@@@$%x ** + x+** **++ + ++**+= x+****== ==****+x =+**xx + ++==*%%%%*%*==++ ++==*%*%%%%*==++ ++==*%*%%%%**=++ + diff --git a/src/build/framegen/frames/frame_229.txt b/src/build/framegen/frames/frame_229.txt new file mode 100644 index 0000000000..a7e73094a2 --- /dev/null +++ b/src/build/framegen/frames/frame_229.txt @@ -0,0 +1,41 @@ + + + + ++++==**%%%%***=++++ + ++==*%=*+x x+**%*==++ + ++**=* *=**++ + ==== ox=******=xo ==== + **++ ·=$@@@@@@@@@@@@@@@@@@@@$=· ++** + **+o =@@@@@$$$$$$$$$$$$$$$$$$$$@@@@@= o+** + **x~ +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ~x** + +++x *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* x+++ + xx== +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ==xx + ==x· @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ·x== + ox== @$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==xo + +++o @$$$$$+ x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + == %$$$$@$ +%@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$% == + == @$$$$$$~ +$@$$$$$@@%==============*@@$$$$$@ == + == $$$$$$$@@%x $$$$@+ ·@$$$$$ == + == ~@$$$$$$$@@@%o $$$$@· @$$$$@~ == + == ·@$$$$$@$~ o$$$$$$@= x@$$$$$@· == + == ·@$$$$@$ ~*@@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$ ·*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@$**$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@% == + +++x ~@@@@@$$$@@@@@%+xx=$@@@@$$$$@@@@$=xx+%@@@@$$$$@@@@@o x+++ + ** o*$$@@$%x ·=%$@@$%=· x%$@@$$*x ** + x+** **+x + ++**+= xx****== ==****xx =+**+x + ++==*%%%%*%*=+xx ++==*%%%%%%*==++ xx++*%*%%%%***++ + diff --git a/src/build/framegen/frames/frame_230.txt b/src/build/framegen/frames/frame_230.txt new file mode 100644 index 0000000000..b27d48bccd --- /dev/null +++ b/src/build/framegen/frames/frame_230.txt @@ -0,0 +1,41 @@ + + + + ++++=**%%%%%%%%%%**=++++ + +=***%+x x+%***=+ + ++**+= =+**++ + ox**+= o=*%$@@@@@@$%*=o =+**xo + x+**oo ·=$@@@@@@@$$$$$$$$@@@@@@@$=· oo**+x + x+** x$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$x **+x + ox** ·$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$· **xo + ==+~ ~@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@~ ~+== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ·@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@· == + xx++ ~@$$$$@@$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ++xx + +++~ @$$$$$ x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + == $$$$$@% +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@* +@$$$$$@%x~~~~~~~~~~~~~~o*@$$$$$@ == + == ·$$$$$$$@@@$+ $$$$@~ @$$$$$· == + == ·@$$$$$$@@@*o $$$$@x @$$$$@· == + == ·@$$$$$@+ ~*@$$$$$@$+xxxxxxxxxxxxxxx%@$$$$$@· == + == ·@$$$$@% ·=@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$~ ·=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@* == + +++x ·$@@@@$$$@@@@@*xox=$@@@@$$$$@@@@$=oox*@@@@@$$$@@@@@~ x+++ + ** o*%$$$$*x ·+%$$$$%+· x*$$$$$*o ** + x+**~ **+x + x+**+= +x*=**==x ==**==x+ =+**+x + ++==*%*%%%**++xx ++==*%%%%%%*==++ xx++**%%%*%*==++ + diff --git a/src/build/framegen/frames/frame_231.txt b/src/build/framegen/frames/frame_231.txt new file mode 100644 index 0000000000..7280be71bd --- /dev/null +++ b/src/build/framegen/frames/frame_231.txt @@ -0,0 +1,41 @@ + + + ++++ + ++==*%%%*%****%*%%%*==++ + x+==*%== ==%*==+x + ===*xo ox*=== + ++**x+ x*%$@@@@@@@@@@$%*x +x**++ + ++*= +$@@@@@@$$$$$$$$$$$$@@@@@@$+ =*++ + ++== *@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@* ==+x + xx== +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ==xx + **o· +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ·o** + ++++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++++ + ** +@$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ *= + xx++ +@$$$$@%=*$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ++xx + +++~ @$$$$$ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + == @$$$$$$ ·=@@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ == + == @$$$$$@$o ~$$$$$$@+ x@$$$$$@ == + == ·@$$$$$$$@@@$o $$$$@· @$$$$@· == + == ·@$$$$$$@@%o $$$$$+ ~@$$$$@· == + == ·@$$$$$$~ +$@$$$$$@@%**************%@@$$$$$@· == + == ·@$$$$@$ +$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@· == + == ·@$$$$$@= +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@* == + +++x $@@@@@$$@@@@@*xoo+%@@@@@$$@@@@@%+o~x=@@@@@$$@@@@@$· x+++ + **o ~=%$$$%*o +%$$$$%+ o*%$$$%=~ ** + x+**~~ ~**+x + ++**== ++==***=xo o+==**==++ =+**++ + ++==*%%%%%**++xx ++==*%%%%%%*==++ xx++**%%%%%**=++ + diff --git a/src/build/framegen/frames/frame_232.txt b/src/build/framegen/frames/frame_232.txt new file mode 100644 index 0000000000..9ae60cdee0 --- /dev/null +++ b/src/build/framegen/frames/frame_232.txt @@ -0,0 +1,41 @@ + + + ++++====++++ + ++==*%*%==++++++++==%*%*==++ + ++**=* *=**++ + x+**+= ·· =+**+x + ++== o=%@@@@@@@@@@@@@@@@%=o ==++ + ===+ +$@@@@@$$$$$$$$$$$$$$$$@@@@@$+ +=== + ++++ =@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@= ++++ + ++=+ $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ +=++ + ** $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ** + +++o =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= o+++ + == $@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + ++++ $@$$$@@x ·+$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + ==+· +@$$$$$ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ·+== + == @$$$$$$ =$@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$$@*o +$$$$$% *$$$$$$ == + == ·@$$$$$$$@@@@+ $$$$@ @$$$$@· == + == ·@$$$$$$@%o x$$$$$% =$$$$$@· == + == ·@$$$$$$ =@@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@· == + == ·@$$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$@$o +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == +@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@= == + ++++ %@@@@@@@@@@@$+o·~x*@@@@@@@@@@@@*o··~+$@@@@@@@@@@@% x+++ + **x· +*%%%*+· o=%%%%=o ·+%%$%*+ ·o** + x+**o~ oo oo oo**+x + ++**+=+~ =+******+x x+******+= ~x=+**++ + ++==**%%%***++xx ++==***%%***==++ xx++***%%%**==++ + diff --git a/src/build/framegen/frames/frame_233.txt b/src/build/framegen/frames/frame_233.txt new file mode 100644 index 0000000000..5d6e28d055 --- /dev/null +++ b/src/build/framegen/frames/frame_233.txt @@ -0,0 +1,41 @@ + + + ++++==****==++++ + ==***%==xo ox==%***== + ===*++ ++*=== + ++**x+ ·oxx++xxo· +x**++ + ===+ o*$@@@@@@@@@@@@@@@@@@$*o +=== + **+x o%@@@@@$$$$$$$$$$$$$$$$$$@@@@@%o x+** + ==+o ~$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$~ o+== + ++++ +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ++++ + ox== o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ==xo + ==+~ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ~+== + ox== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + +++x @$$$$@* ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + ==x *$$$$$$ ~*@@@@$$$$$$$$$$@@@@@@@@@@@@@@$$$$$$$$* x== + == @$$$$$$ ~*@@$$$$$@@$%%%%%%%%%%%%%%$@@$$$$$@ == + == $$$$$$$@@= ·$$$$$* x$$$$$$ == + == ~@$$$$$$$@@@@x $$$$@ @$$$$@~ == + == ·@$$$$$@@+ *$$$$$$o ·$$$$$$@· == + == ·@$$$$$$ x$@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@· == + == ·@$$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$@@*x+%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@+ == + ++++ *@@@@@@@@@@@%x· ~=@@@@@@@@@@@@=~ x%@@@@@@@@@@@* ++++ + **x· o=*%%=x ~+*%%*+~ x=%%*=x ·x** + x+**xo ++ ++ ox**+x + x+**=*xx *+****==++ ++==****=* x+==**+x + ++==********++xx ++==********==++ xx++********==++ + diff --git a/src/build/framegen/frames/frame_234.txt b/src/build/framegen/frames/frame_234.txt new file mode 100644 index 0000000000..b4e624e019 --- /dev/null +++ b/src/build/framegen/frames/frame_234.txt @@ -0,0 +1,41 @@ + + + +++=**%%%%%%%%**=+++ + ++***%==o· ·o==%***++ + ++**+* *+**++ + ==== o+=*%$$$$%*=+o ==== + x+**xx x*@@@@@@@@@@@@@@@@@@@@@@*x xx**+x + xx**~~ ~%@@@@$$$$$$$$$$$$$$$$$$$$$$@@@@%~ ~~**xx + **o· *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ·x** + ==+o %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% o+== + xx== *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ==xx + ==x· @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ·x== + xx== @$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==xx + +++o @$$$$$o ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + == %$$$$@% ~*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$% == + == @$$$$$$+ o*@$$$$$@$=+++++++++++++++%@$$$$$@ == + == $$$$$$$@@@=~ $$$$$x @$$$$$ == + == ·@$$$$$$@@@@= $$$$@~ @$$$$@· == + == ·@$$$$$@% +@$$$$$@%~··············~=@$$$$$@· == + == ·@$$$$@% x%@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@%%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ~@@$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@o == + ++++ +@@@@@@@@@@@=~ x$@@@@@@@@@@$x ·=@@@@@@@@@@@+ ++++ + **+~ o+=+x· o+==+o ·x+=+x ~x** + xx**+x == == x+**+x + xx**==+= ~x*=******+* *+******=*x~ =+==**xx + ++==********++ x+++********+++x ++********==++ + diff --git a/src/build/framegen/frames/frame_235.txt b/src/build/framegen/frames/frame_235.txt new file mode 100644 index 0000000000..7a37219244 --- /dev/null +++ b/src/build/framegen/frames/frame_235.txt @@ -0,0 +1,41 @@ + + + ++==**%%*%%%%%%*%%**==++ + ==***%xx xx%***== + ++**++ ++**++ + x+**++ x=*$$@@@@@@$$*=x ++**+x + ++**~o ~*$@@@@@@@$$$$$$$$@@@@@@@$*~ oo**++ + ++** +$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$+ **++ + xx** ~$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$~ **xx + ==+~ ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ ~+== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ~@$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + x+++ o@$$$$@@%$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o +++x + +++~ @$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + == $$$$$$$ x$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@* +@$$$$$@%o··············~=@$$$$$@ == + == ·$$$$$$$@@@$= $$$$@~ @$$$$$· == + == ·@$$$$$$@@$*o $$$$@x @$$$$@· == + == ·@$$$$$$+ ~*@$$$$$@$=xxxxxxxxxxxxxx+%@$$$$$@· == + == ·@$$$$$$ ~*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@· == + == ·@$$$$$$o ·=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ·@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@· == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==x· @@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@ x== + ++++ o$@@@@@@@@@@+ ~*@@@@@@@@@@*~ +@@@@@@@@@@$x ++++ + ==+x ·oxo~ ~oo~ ~oxo· o+*= + x+**+x =* *= ++**xx + x+==**+*+o ++*=**==**=*+x x+*=**==**=*++ ~+*=**==xx + ++++******==++ ++++********++++ ++==******+++x + diff --git a/src/build/framegen/main.zig b/src/build/framegen/main.zig new file mode 100644 index 0000000000..1be33eb117 --- /dev/null +++ b/src/build/framegen/main.zig @@ -0,0 +1,271 @@ +const std = @import("std"); +const fs = std.fs; + +/// Generates a compressed file of all the ghostty frames +pub fn main() !void { + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + + var arg_iter = try std.process.argsWithAllocator(gpa.allocator()); + // Skip the exe name + _ = arg_iter.skip(); + + const output_path = arg_iter.next() orelse return error.MissingOutputPath; + + const out_dir_path = fs.path.dirname(output_path) orelse return error.InvalidOutputPath; + const out_dir = try fs.cwd().openDir(out_dir_path, .{}); + + const compressed_file = try out_dir.createFile(fs.path.basename(output_path), .{}); + + // Join the frames with a null byte. We'll split on this later + const all_frames = try std.mem.join(gpa.allocator(), "\x01", &frames); + var fbs = std.io.fixedBufferStream(all_frames); + + const reader = fbs.reader(); + try std.compress.flate.compress(reader, compressed_file.writer(), .{}); + + const stdout = std.io.getStdOut().writer(); + + try stdout.print( + \\//! This file is auto-generated. Do not edit. + \\ + \\pub const compressed = @embedFile("{s}"); + , .{output_path}); +} + +const frames = [_][]const u8{ + @embedFile("frames/frame_001.txt"), + @embedFile("frames/frame_002.txt"), + @embedFile("frames/frame_003.txt"), + @embedFile("frames/frame_004.txt"), + @embedFile("frames/frame_005.txt"), + @embedFile("frames/frame_006.txt"), + @embedFile("frames/frame_007.txt"), + @embedFile("frames/frame_008.txt"), + @embedFile("frames/frame_009.txt"), + @embedFile("frames/frame_010.txt"), + @embedFile("frames/frame_011.txt"), + @embedFile("frames/frame_012.txt"), + @embedFile("frames/frame_013.txt"), + @embedFile("frames/frame_014.txt"), + @embedFile("frames/frame_015.txt"), + @embedFile("frames/frame_016.txt"), + @embedFile("frames/frame_017.txt"), + @embedFile("frames/frame_018.txt"), + @embedFile("frames/frame_019.txt"), + @embedFile("frames/frame_020.txt"), + @embedFile("frames/frame_021.txt"), + @embedFile("frames/frame_022.txt"), + @embedFile("frames/frame_023.txt"), + @embedFile("frames/frame_024.txt"), + @embedFile("frames/frame_025.txt"), + @embedFile("frames/frame_026.txt"), + @embedFile("frames/frame_027.txt"), + @embedFile("frames/frame_028.txt"), + @embedFile("frames/frame_029.txt"), + @embedFile("frames/frame_030.txt"), + @embedFile("frames/frame_031.txt"), + @embedFile("frames/frame_032.txt"), + @embedFile("frames/frame_033.txt"), + @embedFile("frames/frame_034.txt"), + @embedFile("frames/frame_035.txt"), + @embedFile("frames/frame_036.txt"), + @embedFile("frames/frame_037.txt"), + @embedFile("frames/frame_038.txt"), + @embedFile("frames/frame_039.txt"), + @embedFile("frames/frame_040.txt"), + @embedFile("frames/frame_041.txt"), + @embedFile("frames/frame_042.txt"), + @embedFile("frames/frame_043.txt"), + @embedFile("frames/frame_044.txt"), + @embedFile("frames/frame_045.txt"), + @embedFile("frames/frame_046.txt"), + @embedFile("frames/frame_047.txt"), + @embedFile("frames/frame_048.txt"), + @embedFile("frames/frame_049.txt"), + @embedFile("frames/frame_050.txt"), + @embedFile("frames/frame_051.txt"), + @embedFile("frames/frame_052.txt"), + @embedFile("frames/frame_053.txt"), + @embedFile("frames/frame_054.txt"), + @embedFile("frames/frame_055.txt"), + @embedFile("frames/frame_056.txt"), + @embedFile("frames/frame_057.txt"), + @embedFile("frames/frame_058.txt"), + @embedFile("frames/frame_059.txt"), + @embedFile("frames/frame_060.txt"), + @embedFile("frames/frame_061.txt"), + @embedFile("frames/frame_062.txt"), + @embedFile("frames/frame_063.txt"), + @embedFile("frames/frame_064.txt"), + @embedFile("frames/frame_065.txt"), + @embedFile("frames/frame_066.txt"), + @embedFile("frames/frame_067.txt"), + @embedFile("frames/frame_068.txt"), + @embedFile("frames/frame_069.txt"), + @embedFile("frames/frame_070.txt"), + @embedFile("frames/frame_071.txt"), + @embedFile("frames/frame_072.txt"), + @embedFile("frames/frame_073.txt"), + @embedFile("frames/frame_074.txt"), + @embedFile("frames/frame_075.txt"), + @embedFile("frames/frame_076.txt"), + @embedFile("frames/frame_077.txt"), + @embedFile("frames/frame_078.txt"), + @embedFile("frames/frame_079.txt"), + @embedFile("frames/frame_080.txt"), + @embedFile("frames/frame_081.txt"), + @embedFile("frames/frame_082.txt"), + @embedFile("frames/frame_083.txt"), + @embedFile("frames/frame_084.txt"), + @embedFile("frames/frame_085.txt"), + @embedFile("frames/frame_086.txt"), + @embedFile("frames/frame_087.txt"), + @embedFile("frames/frame_088.txt"), + @embedFile("frames/frame_089.txt"), + @embedFile("frames/frame_090.txt"), + @embedFile("frames/frame_091.txt"), + @embedFile("frames/frame_092.txt"), + @embedFile("frames/frame_093.txt"), + @embedFile("frames/frame_094.txt"), + @embedFile("frames/frame_095.txt"), + @embedFile("frames/frame_096.txt"), + @embedFile("frames/frame_097.txt"), + @embedFile("frames/frame_098.txt"), + @embedFile("frames/frame_099.txt"), + @embedFile("frames/frame_100.txt"), + @embedFile("frames/frame_101.txt"), + @embedFile("frames/frame_102.txt"), + @embedFile("frames/frame_103.txt"), + @embedFile("frames/frame_104.txt"), + @embedFile("frames/frame_105.txt"), + @embedFile("frames/frame_106.txt"), + @embedFile("frames/frame_107.txt"), + @embedFile("frames/frame_108.txt"), + @embedFile("frames/frame_109.txt"), + @embedFile("frames/frame_110.txt"), + @embedFile("frames/frame_111.txt"), + @embedFile("frames/frame_112.txt"), + @embedFile("frames/frame_113.txt"), + @embedFile("frames/frame_114.txt"), + @embedFile("frames/frame_115.txt"), + @embedFile("frames/frame_116.txt"), + @embedFile("frames/frame_117.txt"), + @embedFile("frames/frame_118.txt"), + @embedFile("frames/frame_119.txt"), + @embedFile("frames/frame_120.txt"), + @embedFile("frames/frame_121.txt"), + @embedFile("frames/frame_122.txt"), + @embedFile("frames/frame_123.txt"), + @embedFile("frames/frame_124.txt"), + @embedFile("frames/frame_125.txt"), + @embedFile("frames/frame_126.txt"), + @embedFile("frames/frame_127.txt"), + @embedFile("frames/frame_128.txt"), + @embedFile("frames/frame_129.txt"), + @embedFile("frames/frame_130.txt"), + @embedFile("frames/frame_131.txt"), + @embedFile("frames/frame_132.txt"), + @embedFile("frames/frame_133.txt"), + @embedFile("frames/frame_134.txt"), + @embedFile("frames/frame_135.txt"), + @embedFile("frames/frame_136.txt"), + @embedFile("frames/frame_137.txt"), + @embedFile("frames/frame_138.txt"), + @embedFile("frames/frame_139.txt"), + @embedFile("frames/frame_140.txt"), + @embedFile("frames/frame_141.txt"), + @embedFile("frames/frame_142.txt"), + @embedFile("frames/frame_143.txt"), + @embedFile("frames/frame_144.txt"), + @embedFile("frames/frame_145.txt"), + @embedFile("frames/frame_146.txt"), + @embedFile("frames/frame_147.txt"), + @embedFile("frames/frame_148.txt"), + @embedFile("frames/frame_149.txt"), + @embedFile("frames/frame_150.txt"), + @embedFile("frames/frame_151.txt"), + @embedFile("frames/frame_152.txt"), + @embedFile("frames/frame_153.txt"), + @embedFile("frames/frame_154.txt"), + @embedFile("frames/frame_155.txt"), + @embedFile("frames/frame_156.txt"), + @embedFile("frames/frame_157.txt"), + @embedFile("frames/frame_158.txt"), + @embedFile("frames/frame_159.txt"), + @embedFile("frames/frame_160.txt"), + @embedFile("frames/frame_161.txt"), + @embedFile("frames/frame_162.txt"), + @embedFile("frames/frame_163.txt"), + @embedFile("frames/frame_164.txt"), + @embedFile("frames/frame_165.txt"), + @embedFile("frames/frame_166.txt"), + @embedFile("frames/frame_167.txt"), + @embedFile("frames/frame_168.txt"), + @embedFile("frames/frame_169.txt"), + @embedFile("frames/frame_170.txt"), + @embedFile("frames/frame_171.txt"), + @embedFile("frames/frame_172.txt"), + @embedFile("frames/frame_173.txt"), + @embedFile("frames/frame_174.txt"), + @embedFile("frames/frame_175.txt"), + @embedFile("frames/frame_176.txt"), + @embedFile("frames/frame_177.txt"), + @embedFile("frames/frame_178.txt"), + @embedFile("frames/frame_179.txt"), + @embedFile("frames/frame_180.txt"), + @embedFile("frames/frame_181.txt"), + @embedFile("frames/frame_182.txt"), + @embedFile("frames/frame_183.txt"), + @embedFile("frames/frame_184.txt"), + @embedFile("frames/frame_185.txt"), + @embedFile("frames/frame_186.txt"), + @embedFile("frames/frame_187.txt"), + @embedFile("frames/frame_188.txt"), + @embedFile("frames/frame_189.txt"), + @embedFile("frames/frame_190.txt"), + @embedFile("frames/frame_191.txt"), + @embedFile("frames/frame_192.txt"), + @embedFile("frames/frame_193.txt"), + @embedFile("frames/frame_194.txt"), + @embedFile("frames/frame_195.txt"), + @embedFile("frames/frame_196.txt"), + @embedFile("frames/frame_197.txt"), + @embedFile("frames/frame_198.txt"), + @embedFile("frames/frame_199.txt"), + @embedFile("frames/frame_200.txt"), + @embedFile("frames/frame_201.txt"), + @embedFile("frames/frame_202.txt"), + @embedFile("frames/frame_203.txt"), + @embedFile("frames/frame_204.txt"), + @embedFile("frames/frame_205.txt"), + @embedFile("frames/frame_206.txt"), + @embedFile("frames/frame_207.txt"), + @embedFile("frames/frame_208.txt"), + @embedFile("frames/frame_209.txt"), + @embedFile("frames/frame_210.txt"), + @embedFile("frames/frame_211.txt"), + @embedFile("frames/frame_212.txt"), + @embedFile("frames/frame_213.txt"), + @embedFile("frames/frame_214.txt"), + @embedFile("frames/frame_215.txt"), + @embedFile("frames/frame_216.txt"), + @embedFile("frames/frame_217.txt"), + @embedFile("frames/frame_218.txt"), + @embedFile("frames/frame_219.txt"), + @embedFile("frames/frame_220.txt"), + @embedFile("frames/frame_221.txt"), + @embedFile("frames/frame_222.txt"), + @embedFile("frames/frame_223.txt"), + @embedFile("frames/frame_224.txt"), + @embedFile("frames/frame_225.txt"), + @embedFile("frames/frame_226.txt"), + @embedFile("frames/frame_227.txt"), + @embedFile("frames/frame_228.txt"), + @embedFile("frames/frame_229.txt"), + @embedFile("frames/frame_230.txt"), + @embedFile("frames/frame_231.txt"), + @embedFile("frames/frame_232.txt"), + @embedFile("frames/frame_233.txt"), + @embedFile("frames/frame_234.txt"), + @embedFile("frames/frame_235.txt"), +}; diff --git a/src/build/main.zig b/src/build/main.zig index 8228abfbff..a0e67543f4 100644 --- a/src/build/main.zig +++ b/src/build/main.zig @@ -10,6 +10,7 @@ pub const GitVersion = @import("GitVersion.zig"); pub const GhosttyBench = @import("GhosttyBench.zig"); pub const GhosttyDocs = @import("GhosttyDocs.zig"); pub const GhosttyExe = @import("GhosttyExe.zig"); +pub const GhosttyFrameData = @import("GhosttyFrameData.zig"); pub const GhosttyLib = @import("GhosttyLib.zig"); pub const GhosttyResources = @import("GhosttyResources.zig"); pub const GhosttyXCFramework = @import("GhosttyXCFramework.zig"); @@ -28,3 +29,6 @@ pub const XCFrameworkStep = @import("XCFrameworkStep.zig"); pub const fish_completions = @import("fish_completions.zig").completions; pub const zsh_completions = @import("zsh_completions.zig").completions; pub const bash_completions = @import("bash_completions.zig").completions; + +// Helpers +pub const requireZig = @import("zig.zig").requireZig; diff --git a/src/build/webgen/main_actions.zig b/src/build/webgen/main_actions.zig index f4dffbc139..5002a5bac9 100644 --- a/src/build/webgen/main_actions.zig +++ b/src/build/webgen/main_actions.zig @@ -1,58 +1,8 @@ const std = @import("std"); const help_strings = @import("help_strings"); -const KeybindAction = @import("../../input/Binding.zig").Action; +const helpgen_actions = @import("../../input/helpgen_actions.zig"); pub fn main() !void { const output = std.io.getStdOut().writer(); - try genKeybindActions(output); -} - -pub fn genKeybindActions(writer: anytype) !void { - // Write the header - try writer.writeAll( - \\--- - \\title: Keybinding Action Reference - \\description: Reference of all Ghostty keybinding actions. - \\editOnGithubLink: https://github.com/ghostty-org/ghostty/edit/main/src/input/Binding.zig - \\--- - \\ - \\This is a reference of all Ghostty keybinding actions. - \\ - \\ - ); - - @setEvalBranchQuota(5_000); - - var buffer = std.ArrayList(u8).init(std.heap.page_allocator); - defer buffer.deinit(); - - const fields = @typeInfo(KeybindAction).Union.fields; - inline for (fields) |field| { - if (field.name[0] == '_') continue; - - // Write previously stored doc comment below all related actions - if (@hasDecl(help_strings.KeybindAction, field.name)) { - try writer.writeAll(buffer.items); - try writer.writeAll("\n"); - - buffer.clearRetainingCapacity(); - } - - // Write the field name. - try writer.writeAll("## `"); - try writer.writeAll(field.name); - try writer.writeAll("`\n"); - - if (@hasDecl(help_strings.KeybindAction, field.name)) { - var iter = std.mem.splitScalar( - u8, - @field(help_strings.KeybindAction, field.name), - '\n', - ); - while (iter.next()) |s| { - try buffer.appendSlice(s); - try buffer.appendSlice("\n"); - } - } - } + try helpgen_actions.generate(output, .markdown, true, std.heap.page_allocator); } diff --git a/src/build/webgen/main_commands.zig b/src/build/webgen/main_commands.zig new file mode 100644 index 0000000000..6e6b00c5e3 --- /dev/null +++ b/src/build/webgen/main_commands.zig @@ -0,0 +1,51 @@ +const std = @import("std"); +const Action = @import("../../cli/action.zig").Action; +const help_strings = @import("help_strings"); + +pub fn main() !void { + const output = std.io.getStdOut().writer(); + try genActions(output); +} + +// Note: as a shortcut for defining inline editOnGithubLinks per cli action the user +// is directed to the folder view on Github. This includes a README pointing them to +// the files to edit. +pub fn genActions(writer: anytype) !void { + // Write the header + try writer.writeAll( + \\--- + \\title: Reference + \\description: Reference of all Ghostty action subcommands. + \\editOnGithubLink: https://github.com/ghostty-org/ghostty/tree/main/src/cli + \\--- + \\Ghostty includes a number of utility actions that can be accessed as subcommands. + \\Actions provide utilities to work with config, list keybinds, list fonts, demo themes, + \\and debug. + \\ + ); + + inline for (@typeInfo(Action).Enum.fields) |field| { + const action = std.meta.stringToEnum(Action, field.name).?; + + switch (action) { + .help, .version => try writer.writeAll("## " ++ field.name ++ "\n"), + else => try writer.writeAll("## " ++ field.name ++ "\n"), + } + + if (@hasDecl(help_strings.Action, field.name)) { + var iter = std.mem.splitScalar(u8, @field(help_strings.Action, field.name), '\n'); + var first = true; + while (iter.next()) |s| { + try writer.writeAll(s); + try writer.writeAll("\n"); + first = false; + } + try writer.writeAll("\n```\n"); + switch (action) { + .help, .version => try writer.writeAll("ghostty --" ++ field.name ++ "\n"), + else => try writer.writeAll("ghostty +" ++ field.name ++ "\n"), + } + try writer.writeAll("```\n\n"); + } + } +} diff --git a/src/build/zig.zig b/src/build/zig.zig new file mode 100644 index 0000000000..7e327127d4 --- /dev/null +++ b/src/build/zig.zig @@ -0,0 +1,17 @@ +const std = @import("std"); +const builtin = @import("builtin"); + +/// Require a specific version of Zig to build this project. +pub fn requireZig(comptime required_zig: []const u8) void { + // Fail compilation if the current Zig version doesn't meet requirements. + const current_vsn = builtin.zig_version; + const required_vsn = std.SemanticVersion.parse(required_zig) catch unreachable; + if (current_vsn.major != required_vsn.major or + current_vsn.minor != required_vsn.minor) + { + @compileError(std.fmt.comptimePrint( + "Your Zig version v{} does not meet the required build version of v{}", + .{ current_vsn, required_vsn }, + )); + } +} diff --git a/src/cli/README.md b/src/cli/README.md new file mode 100644 index 0000000000..7a1d99409c --- /dev/null +++ b/src/cli/README.md @@ -0,0 +1,13 @@ +# Subcommand Actions + +This is the cli specific code. It contains cli actions and tui definitions and +argument parsing. + +This README is meant as developer documentation and not as user documentation. +For user documentation, see the main README or [ghostty.org](https://ghostty.org/docs). + +## Updating documentation + +Each cli action is defined in it's own file. Documentation for each action is defined +in the doc comment associated with the `run` function. For example the `run` function +in `list_keybinds.zig` contains the help text for `ghostty +list-keybinds`. diff --git a/src/cli/action.zig b/src/cli/action.zig index a84a400241..f3da2e2efd 100644 --- a/src/cli/action.zig +++ b/src/cli/action.zig @@ -13,6 +13,7 @@ const show_config = @import("show_config.zig"); const validate_config = @import("validate_config.zig"); const crash_report = @import("crash_report.zig"); const show_face = @import("show_face.zig"); +const boo = @import("boo.zig"); /// Special commands that can be invoked via CLI flags. These are all /// invoked by using `+` as a CLI flag. The only exception is @@ -45,11 +46,14 @@ pub const Action = enum { // Validate passed config file @"validate-config", + // Show which font face Ghostty loads a codepoint from. + @"show-face", + // List, (eventually) view, and (eventually) send crash reports. @"crash-report", - // Show which font face Ghostty loads a codepoint from. - @"show-face", + // Boo! + boo, pub const Error = error{ /// Multiple actions were detected. You can specify at most one @@ -151,6 +155,7 @@ pub const Action = enum { .@"validate-config" => try validate_config.run(alloc), .@"crash-report" => try crash_report.run(alloc), .@"show-face" => try show_face.run(alloc), + .boo => try boo.run(alloc), }; } @@ -186,6 +191,7 @@ pub const Action = enum { .@"validate-config" => validate_config.Options, .@"crash-report" => crash_report.Options, .@"show-face" => show_face.Options, + .boo => boo.Options, }; } } diff --git a/src/cli/args.zig b/src/cli/args.zig index 23dcf77331..7385e6a3ea 100644 --- a/src/cli/args.zig +++ b/src/cli/args.zig @@ -8,6 +8,8 @@ const internal_os = @import("../os/main.zig"); const Diagnostic = diags.Diagnostic; const DiagnosticList = diags.DiagnosticList; +const log = std.log.scoped(.cli); + // TODO: // - Only `--long=value` format is accepted. Do we want to allow // `--long value`? Not currently allowed. @@ -38,6 +40,12 @@ pub const Error = error{ /// "DiagnosticList" and any diagnostic messages will be added to that list. /// When diagnostics are present, only allocation errors will be returned. /// +/// If the destination type has a decl "renamed", it must be of type +/// std.StaticStringMap([]const u8) and contains a mapping from the old +/// field name to the new field name. This is used to allow renaming fields +/// while still supporting the old name. If a renamed field is set, parsing +/// will automatically set the new field name. +/// /// Note: If the arena is already non-null, then it will be used. In this /// case, in the case of an error some memory might be leaked into the arena. pub fn parse( @@ -49,6 +57,24 @@ pub fn parse( const info = @typeInfo(T); assert(info == .Struct); + comptime { + // Verify all renamed fields are valid (source does not exist, + // destination does exist). + if (@hasDecl(T, "renamed")) { + for (T.renamed.keys(), T.renamed.values()) |key, value| { + if (@hasField(T, key)) { + @compileLog(key); + @compileError("renamed field source exists"); + } + + if (!@hasField(T, value)) { + @compileLog(value); + @compileError("renamed field destination does not exist"); + } + } + } + } + // Make an arena for all our allocations if we support it. Otherwise, // use an allocator that always fails. If the arena is already set on // the config, then we reuse that. See memory note in parse docs. @@ -367,6 +393,16 @@ pub fn parseIntoField( } } + // Unknown field, is the field renamed? + if (@hasDecl(T, "renamed")) { + for (T.renamed.keys(), T.renamed.values()) |old, new| { + if (mem.eql(u8, old, key)) { + try parseIntoField(T, alloc, dst, new, value); + return; + } + } + } + return error.InvalidField; } @@ -1104,6 +1140,24 @@ test "parseIntoField: tagged union missing tag" { ); } +test "parseIntoField: renamed field" { + const testing = std.testing; + var arena = ArenaAllocator.init(testing.allocator); + defer arena.deinit(); + const alloc = arena.allocator(); + + var data: struct { + a: []const u8, + + const renamed = std.StaticStringMap([]const u8).initComptime(&.{ + .{ "old", "a" }, + }); + } = undefined; + + try parseIntoField(@TypeOf(data), alloc, &data, "old", "42"); + try testing.expectEqualStrings("42", data.a); +} + /// An iterator that considers its location to be CLI args. It /// iterates through an underlying iterator and increments a counter /// to track the current CLI arg index. @@ -1206,9 +1260,11 @@ pub fn LineIterator(comptime ReaderType: type) type { const buf = buf: { while (true) { // Read the full line - var entry = self.r.readUntilDelimiterOrEof(self.entry[2..], '\n') catch { - // TODO: handle errors - unreachable; + var entry = self.r.readUntilDelimiterOrEof(self.entry[2..], '\n') catch |err| switch (err) { + inline else => |e| { + log.warn("cannot read from \"{s}\": {}", .{ self.filepath, e }); + return null; + }, } orelse return null; // Increment our line counter diff --git a/src/cli/boo.zig b/src/cli/boo.zig new file mode 100644 index 0000000000..7ecbf79fbb --- /dev/null +++ b/src/cli/boo.zig @@ -0,0 +1,234 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const args = @import("args.zig"); +const Action = @import("action.zig").Action; +const Allocator = std.mem.Allocator; +const help_strings = @import("help_strings"); +const vaxis = @import("vaxis"); + +const framedata = @import("framedata"); + +const vxfw = vaxis.vxfw; + +pub const Options = struct { + pub fn deinit(self: Options) void { + _ = self; + } + + /// Enables `-h` and `--help` to work. + pub fn help(self: Options) !void { + _ = self; + return Action.help_error; + } +}; + +const Boo = struct { + frame: u8, + framerate: u32, // 30 fps + // We know the size of this at compile time, but we heap allocate the slice to prevent the + // binary from increasing too much in size + buffer: [frame_width * frame_height]vaxis.Cell = undefined, + + ghostty_style: vaxis.Style, + outline_style: vaxis.Style, + + // Width of a single frame + const frame_width = 100; + // Height of a single frame + const frame_height = 41; + + fn widget(self: *Boo) vxfw.Widget { + return .{ + .userdata = self, + .eventHandler = Boo.typeErasedEventHandler, + .drawFn = Boo.typeErasedDrawFn, + }; + } + + fn typeErasedEventHandler(ptr: *anyopaque, ctx: *vxfw.EventContext, event: vxfw.Event) anyerror!void { + const self: *Boo = @ptrCast(@alignCast(ptr)); + switch (event) { + .init, + .tick, + => { + self.updateFrame(); + ctx.redraw = true; + return ctx.tick(self.framerate, self.widget()); + }, + .key_press => |key| { + if (key.matches('c', .{ .ctrl = true }) or + key.matches(vaxis.Key.escape, .{})) + { + ctx.quit = true; + return; + } + }, + else => {}, + } + } + + fn typeErasedDrawFn(ptr: *anyopaque, ctx: vxfw.DrawContext) Allocator.Error!vxfw.Surface { + const self: *Boo = @ptrCast(@alignCast(ptr)); + const max = ctx.max.size(); + + // Warn for screen size + if (max.width < frame_width or max.height < frame_height) { + const text: vxfw.Text = .{ .text = "Screen must be at least 100w x 41h" }; + const center: vxfw.Center = .{ .child = text.widget() }; + return center.draw(ctx); + } + + // Calculate x and y offsets to center the animation frame + const offset_y = (max.height - frame_height) / 2; + const offset_x = (max.width - frame_width) / 2; + + // Create the animation surface + const child: vxfw.Surface = .{ + .size = .{ .width = @intCast(frame_width), .height = @intCast(frame_height) }, + .widget = self.widget(), + .buffer = &self.buffer, + .children = &.{}, + }; + + // Allocate a slice of child surfaces + var children = try ctx.arena.alloc(vxfw.SubSurface, 1); + children[0] = .{ + .origin = .{ .row = @intCast(offset_y), .col = @intCast(offset_x) }, + .surface = child, + }; + + return .{ + .size = max, + .widget = self.widget(), + .buffer = &.{}, + .children = children, + }; + } + + /// Updates our internal buffer with the current frame, then advances the frame index + fn updateFrame(self: *Boo) void { + const frame = frames[self.frame]; + // A frame is characters with html spans. When we encounter a span, we use the outline style + // until the span ends. That is, when we find a '<', we parse until '>'. Then we use the + // outline styule until the next '<', and skip until the next '>' + + const State = enum { + normal, + span, + in_tag, + in_closing_tag, + }; + + var cell_idx: usize = 0; + + var line_iter = std.mem.splitScalar(u8, frame, '\n'); + while (line_iter.next()) |line| { + var state: State = .normal; + var style = self.ghostty_style; + var cp_iter: std.unicode.Utf8Iterator = .{ .bytes = line, .i = 0 }; + while (cp_iter.nextCodepointSlice()) |char| { + switch (state) { + .normal => if (std.mem.eql(u8, "<", char)) { + state = .in_tag; + // We will be entering a span + style = self.outline_style; + continue; + }, + .span => if (std.mem.eql(u8, "<", char)) { + state = .in_tag; + style = self.ghostty_style; + continue; + }, + .in_tag => { + // If we encounter a '/', we are a closing tag + // If we parse all the way to a '>' we are an opening tag: we are now in a span + if (std.mem.eql(u8, "/", char)) + state = .in_closing_tag + else if (std.mem.eql(u8, ">", char)) + state = .span; + continue; + }, + .in_closing_tag => { + // If we are closing a tag, we will enter the normal state + if (std.mem.eql(u8, ">", char)) state = .normal; + continue; + }, + } + self.buffer[cell_idx] = .{ + .char = .{ + .grapheme = char, + .width = 1, + }, + .style = style, + }; + cell_idx += 1; + } + } + std.debug.assert(cell_idx == self.buffer.len); + + // Lastly, update the frame index + self.frame += 1; + if (self.frame == frames.len) self.frame = 0; + } +}; + +/// The `boo` command is used to display the animation from the Ghostty website in the terminal +pub fn run(gpa: Allocator) !u8 { + // Disable on non-desktop systems. + switch (builtin.os.tag) { + .windows, .macos, .linux => {}, + else => return 1, + } + + var opts: Options = .{}; + defer opts.deinit(); + + { + var iter = try args.argsIterator(gpa); + defer iter.deinit(); + try args.parse(Options, gpa, &opts, &iter); + } + + try decompressFrames(gpa); + defer { + gpa.free(frames); + gpa.free(decompressed_data); + } + + var app = try vxfw.App.init(gpa); + defer app.deinit(); + + var boo: Boo = undefined; + boo.frame = 0; + boo.framerate = 1000 / 30; + boo.ghostty_style = .{}; + boo.outline_style = .{ .fg = .{ .index = 4 } }; + @memset(&boo.buffer, .{}); + + try app.run(boo.widget(), .{}); + + return 0; +} + +/// We store a global ref to the decompressed data. All of our frames reference into this data +var decompressed_data: []const u8 = undefined; + +/// Heap allocated list of frames. The underlying frame data references decompressed_data +var frames: []const []const u8 = undefined; + +/// Decompress the frames into a slice of individual frames +fn decompressFrames(gpa: Allocator) !void { + var fbs = std.io.fixedBufferStream(framedata.compressed); + var list = std.ArrayList(u8).init(gpa); + + try std.compress.flate.decompress(fbs.reader(), list.writer()); + decompressed_data = try list.toOwnedSlice(); + + var frame_list = try std.ArrayList([]const u8).initCapacity(gpa, 235); + + var frame_iter = std.mem.splitScalar(u8, decompressed_data, '\x01'); + while (frame_iter.next()) |frame| { + try frame_list.append(frame); + } + frames = try frame_list.toOwnedSlice(); +} diff --git a/src/cli/crash_report.zig b/src/cli/crash_report.zig index dd5fe99cce..ff85097972 100644 --- a/src/cli/crash_report.zig +++ b/src/cli/crash_report.zig @@ -53,7 +53,7 @@ pub fn run(alloc_gpa: Allocator) !u8 { // print a message, otherwise we do nothing. if (reports.items.len == 0) { if (std.posix.isatty(stdout.handle)) { - try stdout.writeAll("No crash reports! 👻"); + try stdout.writeAll("No crash reports! 👻\n"); } return 0; } diff --git a/src/cli/help.zig b/src/cli/help.zig index daadc37ccd..22fe27d8d1 100644 --- a/src/cli/help.zig +++ b/src/cli/help.zig @@ -15,9 +15,11 @@ pub const Options = struct { } }; -/// The `help` command shows general help about Ghostty. You can also specify -/// `--help` or `-h` along with any action such as `+list-themes` to see help -/// for a specific action. +/// The `help` command shows general help about Ghostty. Recognized as either +/// `-h, `--help`, or like other actions `+help`. +/// +/// You can also specify `--help` or `-h` along with any action such as +/// `+list-themes` to see help for a specific action. pub fn run(alloc: Allocator) !u8 { var opts: Options = .{}; defer opts.deinit(); diff --git a/src/cli/list_actions.zig b/src/cli/list_actions.zig index 65b9dcdadc..1d17873cc9 100644 --- a/src/cli/list_actions.zig +++ b/src/cli/list_actions.zig @@ -2,7 +2,7 @@ const std = @import("std"); const args = @import("args.zig"); const Action = @import("action.zig").Action; const Allocator = std.mem.Allocator; -const help_strings = @import("help_strings"); +const helpgen_actions = @import("../input/helpgen_actions.zig"); pub const Options = struct { /// If `true`, print out documentation about the action associated with the @@ -24,7 +24,9 @@ pub const Options = struct { /// actions for Ghostty. These are distinct from the CLI Actions which can /// be listed via `+help` /// -/// The `--docs` argument will print out the documentation for each action. +/// Flags: +/// +/// * `--docs`: will print out the documentation for each action. pub fn run(alloc: Allocator) !u8 { var opts: Options = .{}; defer opts.deinit(); @@ -36,19 +38,7 @@ pub fn run(alloc: Allocator) !u8 { } const stdout = std.io.getStdOut().writer(); - const info = @typeInfo(help_strings.KeybindAction); - inline for (info.Struct.decls) |field| { - try stdout.print("{s}", .{field.name}); - if (opts.docs) { - try stdout.print(":\n", .{}); - var iter = std.mem.splitScalar(u8, std.mem.trimRight(u8, @field(help_strings.KeybindAction, field.name), &std.ascii.whitespace), '\n'); - while (iter.next()) |line| { - try stdout.print(" {s}\n", .{line}); - } - } else { - try stdout.print("\n", .{}); - } - } + try helpgen_actions.generate(stdout, .plaintext, opts.docs, std.heap.page_allocator); return 0; } diff --git a/src/cli/list_fonts.zig b/src/cli/list_fonts.zig index 9d1f34cd1e..e8a010ecd0 100644 --- a/src/cli/list_fonts.zig +++ b/src/cli/list_fonts.zig @@ -44,14 +44,21 @@ pub const Options = struct { /// the sorting will be disabled and the results instead will be shown in the /// same priority order Ghostty would use to pick a font. /// -/// The `--family` argument can be used to filter results to a specific family. -/// The family handling is identical to the `font-family` set of Ghostty -/// configuration values, so this can be used to debug why your desired font may -/// not be loading. +/// Flags: /// -/// The `--bold` and `--italic` arguments can be used to filter results to -/// specific styles. It is not guaranteed that only those styles are returned, -/// it will just prioritize fonts that match those styles. +/// * `--bold`: Filter results to specific bold styles. It is not guaranteed +/// that only those styles are returned. They are only prioritized. +/// +/// * `--italic`: Filter results to specific italic styles. It is not guaranteed +/// that only those styles are returned. They are only prioritized. +/// +/// * `--style`: Filter results based on the style string advertised by a font. +/// It is not guaranteed that only those styles are returned. They are only +/// prioritized. +/// +/// * `--family`: Filter results to a specific font family. The family handling +/// is identical to the `font-family` set of Ghostty configuration values, so +/// this can be used to debug why your desired font may not be loading. pub fn run(alloc: Allocator) !u8 { var iter = try args.argsIterator(alloc); defer iter.deinit(); diff --git a/src/cli/list_keybinds.zig b/src/cli/list_keybinds.zig index ddaf751770..6cd989201c 100644 --- a/src/cli/list_keybinds.zig +++ b/src/cli/list_keybinds.zig @@ -42,11 +42,15 @@ pub const Options = struct { /// changes to the keybinds it will print out the default ones configured for /// Ghostty /// -/// The `--default` argument will print out all the default keybinds configured -/// for Ghostty +/// Flags: /// -/// The `--plain` flag will disable formatting and make the output more -/// friendly for Unix tooling. This is default when not printing to a tty. +/// * `--default`: will print out all the default keybinds +/// +/// * `--docs`: currently does nothing, intended to print out documentation +/// about the action associated with the keybinds +/// +/// * `--plain`: will disable formatting and make the output more +/// friendly for Unix tooling. This is default when not printing to a tty. pub fn run(alloc: Allocator) !u8 { var opts: Options = .{}; defer opts.deinit(); @@ -64,7 +68,9 @@ pub fn run(alloc: Allocator) !u8 { // Despite being under the posix namespace, this also works on Windows as of zig 0.13.0 if (tui.can_pretty_print and !opts.plain and std.posix.isatty(stdout.handle)) { - return prettyPrint(alloc, config.keybind); + var arena = std.heap.ArenaAllocator.init(alloc); + defer arena.deinit(); + return prettyPrint(arena.allocator(), config.keybind); } else { try config.keybind.formatEntryDocs( configpkg.entryFormatter("keybind", stdout.writer()), @@ -75,6 +81,111 @@ pub fn run(alloc: Allocator) !u8 { return 0; } +const TriggerList = std.SinglyLinkedList(Binding.Trigger); + +const ChordBinding = struct { + triggers: TriggerList, + action: Binding.Action, + + // Order keybinds based on various properties + // 1. Longest chord sequence + // 2. Most active modifiers + // 3. Alphabetically by active modifiers + // 4. Trigger key order + // These properties propagate through chorded keypresses + // + // Adapted from Binding.lessThan + pub fn lessThan(_: void, lhs: ChordBinding, rhs: ChordBinding) bool { + const lhs_len = lhs.triggers.len(); + const rhs_len = rhs.triggers.len(); + + std.debug.assert(lhs_len != 0); + std.debug.assert(rhs_len != 0); + + if (lhs_len != rhs_len) { + return lhs_len > rhs_len; + } + + const lhs_count: usize = blk: { + var count: usize = 0; + var maybe_trigger = lhs.triggers.first; + while (maybe_trigger) |trigger| : (maybe_trigger = trigger.next) { + if (trigger.data.mods.super) count += 1; + if (trigger.data.mods.ctrl) count += 1; + if (trigger.data.mods.shift) count += 1; + if (trigger.data.mods.alt) count += 1; + } + break :blk count; + }; + const rhs_count: usize = blk: { + var count: usize = 0; + var maybe_trigger = rhs.triggers.first; + while (maybe_trigger) |trigger| : (maybe_trigger = trigger.next) { + if (trigger.data.mods.super) count += 1; + if (trigger.data.mods.ctrl) count += 1; + if (trigger.data.mods.shift) count += 1; + if (trigger.data.mods.alt) count += 1; + } + + break :blk count; + }; + + if (lhs_count != rhs_count) + return lhs_count > rhs_count; + + { + var l_trigger = lhs.triggers.first; + var r_trigger = rhs.triggers.first; + while (l_trigger != null and r_trigger != null) { + const l_int = l_trigger.?.data.mods.int(); + const r_int = r_trigger.?.data.mods.int(); + + if (l_int != r_int) { + return l_int > r_int; + } + + l_trigger = l_trigger.?.next; + r_trigger = r_trigger.?.next; + } + } + + var l_trigger = lhs.triggers.first; + var r_trigger = rhs.triggers.first; + + while (l_trigger != null and r_trigger != null) { + const lhs_key: c_int = blk: { + switch (l_trigger.?.data.key) { + .translated => |key| break :blk @intFromEnum(key), + .physical => |key| break :blk @intFromEnum(key), + .unicode => |key| break :blk @intCast(key), + } + }; + const rhs_key: c_int = blk: { + switch (r_trigger.?.data.key) { + .translated => |key| break :blk @intFromEnum(key), + .physical => |key| break :blk @intFromEnum(key), + .unicode => |key| break :blk @intCast(key), + } + }; + + l_trigger = l_trigger.?.next; + r_trigger = r_trigger.?.next; + + if (l_trigger == null or r_trigger == null) { + return lhs_key < rhs_key; + } + + if (lhs_key != rhs_key) { + return lhs_key < rhs_key; + } + } + + // The previous loop will always return something on its final iteration so we cannot + // reach this point + unreachable; + } +}; + fn prettyPrint(alloc: Allocator, keybinds: Config.Keybinds) !u8 { // Set up vaxis var tty = try vaxis.Tty.init(); @@ -107,26 +218,11 @@ fn prettyPrint(alloc: Allocator, keybinds: Config.Keybinds) !u8 { const win = vx.window(); - // Get all of our keybinds into a list. We also search for the longest printed keyname so we can - // align things nicely + // Generate a list of bindings, recursively traversing chorded keybindings var iter = keybinds.set.bindings.iterator(); - var bindings = std.ArrayList(Binding).init(alloc); - var widest_key: u16 = 0; - var buf: [64]u8 = undefined; - while (iter.next()) |bind| { - const action = switch (bind.value_ptr.*) { - .leader => continue, // TODO: support this - .leaf => |leaf| leaf.action, - }; - const key = switch (bind.key_ptr.key) { - .translated => |k| try std.fmt.bufPrint(&buf, "{s}", .{@tagName(k)}), - .physical => |k| try std.fmt.bufPrint(&buf, "physical:{s}", .{@tagName(k)}), - .unicode => |c| try std.fmt.bufPrint(&buf, "{u}", .{c}), - }; - widest_key = @max(widest_key, win.gwidth(key)); - try bindings.append(.{ .trigger = bind.key_ptr.*, .action = action }); - } - std.mem.sort(Binding, bindings.items, {}, Binding.lessThan); + const bindings, const widest_chord = try iterateBindings(alloc, &iter, &win); + + std.mem.sort(ChordBinding, bindings, {}, ChordBinding.lessThan); // Set up styles for each modifier const super_style: vaxis.Style = .{ .fg = .{ .index = 1 } }; @@ -134,41 +230,41 @@ fn prettyPrint(alloc: Allocator, keybinds: Config.Keybinds) !u8 { const alt_style: vaxis.Style = .{ .fg = .{ .index = 3 } }; const shift_style: vaxis.Style = .{ .fg = .{ .index = 4 } }; - var longest_col: u16 = 0; - // Print the list - for (bindings.items) |bind| { + for (bindings) |bind| { win.clear(); var result: vaxis.Window.PrintResult = .{ .col = 0, .row = 0, .overflow = false }; - const trigger = bind.trigger; - if (trigger.mods.super) { - result = win.printSegment(.{ .text = "super", .style = super_style }, .{ .col_offset = result.col }); - result = win.printSegment(.{ .text = " + " }, .{ .col_offset = result.col }); - } - if (trigger.mods.ctrl) { - result = win.printSegment(.{ .text = "ctrl ", .style = ctrl_style }, .{ .col_offset = result.col }); - result = win.printSegment(.{ .text = " + " }, .{ .col_offset = result.col }); - } - if (trigger.mods.alt) { - result = win.printSegment(.{ .text = "alt ", .style = alt_style }, .{ .col_offset = result.col }); - result = win.printSegment(.{ .text = " + " }, .{ .col_offset = result.col }); - } - if (trigger.mods.shift) { - result = win.printSegment(.{ .text = "shift", .style = shift_style }, .{ .col_offset = result.col }); - result = win.printSegment(.{ .text = " + " }, .{ .col_offset = result.col }); - } - - const key = switch (trigger.key) { - .translated => |k| try std.fmt.allocPrint(alloc, "{s}", .{@tagName(k)}), - .physical => |k| try std.fmt.allocPrint(alloc, "physical:{s}", .{@tagName(k)}), - .unicode => |c| try std.fmt.allocPrint(alloc, "{u}", .{c}), - }; - // We don't track the key print because we index the action off the *widest* key so we get - // nice alignment no matter what was printed for mods - _ = win.printSegment(.{ .text = key }, .{ .col_offset = result.col }); + var maybe_trigger = bind.triggers.first; + while (maybe_trigger) |trigger| : (maybe_trigger = trigger.next) { + if (trigger.data.mods.super) { + result = win.printSegment(.{ .text = "super", .style = super_style }, .{ .col_offset = result.col }); + result = win.printSegment(.{ .text = " + " }, .{ .col_offset = result.col }); + } + if (trigger.data.mods.ctrl) { + result = win.printSegment(.{ .text = "ctrl ", .style = ctrl_style }, .{ .col_offset = result.col }); + result = win.printSegment(.{ .text = " + " }, .{ .col_offset = result.col }); + } + if (trigger.data.mods.alt) { + result = win.printSegment(.{ .text = "alt ", .style = alt_style }, .{ .col_offset = result.col }); + result = win.printSegment(.{ .text = " + " }, .{ .col_offset = result.col }); + } + if (trigger.data.mods.shift) { + result = win.printSegment(.{ .text = "shift", .style = shift_style }, .{ .col_offset = result.col }); + result = win.printSegment(.{ .text = " + " }, .{ .col_offset = result.col }); + } + const key = switch (trigger.data.key) { + .translated => |k| try std.fmt.allocPrint(alloc, "{s}", .{@tagName(k)}), + .physical => |k| try std.fmt.allocPrint(alloc, "physical:{s}", .{@tagName(k)}), + .unicode => |c| try std.fmt.allocPrint(alloc, "{u}", .{c}), + }; + result = win.printSegment(.{ .text = key }, .{ .col_offset = result.col }); - if (longest_col < result.col) longest_col = result.col; + // Print a separator between chorded keys + if (trigger.next != null) { + result = win.printSegment(.{ .text = " > ", .style = .{ .bold = true, .fg = .{ .index = 6 } } }, .{ .col_offset = result.col }); + } + } const action = try std.fmt.allocPrint(alloc, "{}", .{bind.action}); // If our action has an argument, we print the argument in a different color @@ -177,12 +273,69 @@ fn prettyPrint(alloc: Allocator, keybinds: Config.Keybinds) !u8 { .{ .text = action[0..idx] }, .{ .text = action[idx .. idx + 1], .style = .{ .dim = true } }, .{ .text = action[idx + 1 ..], .style = .{ .fg = .{ .index = 5 } } }, - }, .{ .col_offset = longest_col + widest_key + 2 }); + }, .{ .col_offset = widest_chord + 3 }); } else { - _ = win.printSegment(.{ .text = action }, .{ .col_offset = longest_col + widest_key + 2 }); + _ = win.printSegment(.{ .text = action }, .{ .col_offset = widest_chord + 3 }); } try vx.prettyPrint(writer); } try buf_writer.flush(); return 0; } + +fn iterateBindings(alloc: Allocator, iter: anytype, win: *const vaxis.Window) !struct { []ChordBinding, u16 } { + var widest_chord: u16 = 0; + var bindings = std.ArrayList(ChordBinding).init(alloc); + while (iter.next()) |bind| { + const width = blk: { + var buf = std.ArrayList(u8).init(alloc); + const t = bind.key_ptr.*; + + if (t.mods.super) try std.fmt.format(buf.writer(), "super + ", .{}); + if (t.mods.ctrl) try std.fmt.format(buf.writer(), "ctrl + ", .{}); + if (t.mods.alt) try std.fmt.format(buf.writer(), "alt + ", .{}); + if (t.mods.shift) try std.fmt.format(buf.writer(), "shift + ", .{}); + + switch (t.key) { + .translated => |k| try std.fmt.format(buf.writer(), "{s}", .{@tagName(k)}), + .physical => |k| try std.fmt.format(buf.writer(), "physical:{s}", .{@tagName(k)}), + .unicode => |c| try std.fmt.format(buf.writer(), "{u}", .{c}), + } + + break :blk win.gwidth(buf.items); + }; + + switch (bind.value_ptr.*) { + .leader => |leader| { + + // Recursively iterate on the set of bindings for this leader key + var n_iter = leader.bindings.iterator(); + const sub_bindings, const max_width = try iterateBindings(alloc, &n_iter, win); + + // Prepend the current keybind onto the list of sub-binds + for (sub_bindings) |*nb| { + const prepend_node = try alloc.create(TriggerList.Node); + prepend_node.* = TriggerList.Node{ .data = bind.key_ptr.* }; + nb.triggers.prepend(prepend_node); + } + + // Add the longest sub-bind width to the current bind width along with a padding + // of 5 for the ' > ' spacer + widest_chord = @max(widest_chord, width + max_width + 5); + try bindings.appendSlice(sub_bindings); + }, + .leaf => |leaf| { + const node = try alloc.create(TriggerList.Node); + node.* = TriggerList.Node{ .data = bind.key_ptr.* }; + const triggers = TriggerList{ + .first = node, + }; + + widest_chord = @max(widest_chord, width); + try bindings.append(.{ .triggers = triggers, .action = leaf.action }); + }, + } + } + + return .{ try bindings.toOwnedSlice(), widest_chord }; +} diff --git a/src/cli/list_themes.zig b/src/cli/list_themes.zig index 22e22a972d..8ebac4487a 100644 --- a/src/cli/list_themes.zig +++ b/src/cli/list_themes.zig @@ -91,6 +91,7 @@ const ThemeListElement = struct { /// Flags: /// /// * `--path`: Show the full path to the theme. +/// /// * `--plain`: Force a plain listing of themes. pub fn run(gpa_alloc: std.mem.Allocator) !u8 { var opts: Options = .{}; @@ -191,6 +192,7 @@ const Preview = struct { normal, help, search, + save, }, color_scheme: vaxis.Color.Scheme, text_input: vaxis.widgets.TextInput, @@ -376,6 +378,8 @@ const Preview = struct { self.mode = .help; if (key.matches('/', .{})) self.mode = .search; + if (key.matchesAny(&.{ vaxis.Key.enter, vaxis.Key.kp_enter }, .{})) + self.mode = .save; if (key.matchesAny(&.{ 'x', '/' }, .{ .ctrl = true })) { self.text_input.buf.clearRetainingCapacity(); try self.updateFiltered(); @@ -430,6 +434,12 @@ const Preview = struct { try self.text_input.update(.{ .key_press = key }); try self.updateFiltered(); }, + .save => { + if (key.matches('q', .{})) + self.should_quit = true; + if (key.matchesAny(&.{ vaxis.Key.escape, vaxis.Key.enter, vaxis.Key.kp_enter }, .{})) + self.mode = .normal; + }, } }, .color_scheme => |color_scheme| self.color_scheme = color_scheme, @@ -673,7 +683,7 @@ const Preview = struct { .{ .keys = "End", .help = "Go to the end of the list." }, .{ .keys = "/", .help = "Start search." }, .{ .keys = "^X, ^/", .help = "Clear search." }, - .{ .keys = "⏎", .help = "Close search window." }, + .{ .keys = "⏎", .help = "Save theme or close search window." }, }; for (key_help, 0..) |help, captured_i| { @@ -724,6 +734,51 @@ const Preview = struct { child.fill(.{ .style = self.ui_standard() }); self.text_input.drawWithStyle(child, self.ui_standard()); }, + .save => { + const theme = self.themes[self.filtered.items[self.current]]; + + const width = 90; + const height = 12; + const child = win.child( + .{ + .x_off = win.width / 2 -| width / 2, + .y_off = win.height / 2 -| height / 2, + .width = width, + .height = height, + .border = .{ + .where = .all, + .style = self.ui_standard(), + }, + }, + ); + + child.fill(.{ .style = self.ui_standard() }); + + const save_instructions = [_][]const u8{ + "To apply this theme, add the following line to your Ghostty configuration:", + "", + try std.fmt.allocPrint(alloc, "theme = {s}", .{theme.theme}), + "", + "Save the configuration file and then reload it to apply the new theme.", + "For more details on configuration and themes, visit the Ghostty documentation:", + "", + "https://ghostty.org/docs/config/reference", + }; + + for (save_instructions, 0..) |instruction, captured_i| { + const i: u16 = @intCast(captured_i); + _ = child.printSegment( + .{ + .text = instruction, + .style = self.ui_standard(), + }, + .{ + .row_offset = i + 1, + .col_offset = 2, + }, + ); + } + }, } } diff --git a/src/cli/validate_config.zig b/src/cli/validate_config.zig index 1615ef66b3..5bc6ff4062 100644 --- a/src/cli/validate_config.zig +++ b/src/cli/validate_config.zig @@ -23,10 +23,13 @@ pub const Options = struct { /// The `validate-config` command is used to validate a Ghostty config file. /// -/// When executed without any arguments, this will load the config from the default location. +/// When executed without any arguments, this will load the config from the default +/// location. /// -/// The `--config-file` argument can be passed to validate a specific target config -/// file in a non-default location. +/// Flags: +/// +/// * `--config-file`: can be passed to validate a specific target config file in +/// a non-default location pub fn run(alloc: std.mem.Allocator) !u8 { var opts: Options = .{}; defer opts.deinit(); diff --git a/src/cli/version.zig b/src/cli/version.zig index b001525896..f6d2ea9df7 100644 --- a/src/cli/version.zig +++ b/src/cli/version.zig @@ -10,7 +10,8 @@ const gtk = if (build_config.app_runtime == .gtk) @import("../apprt/gtk/c.zig"). pub const Options = struct {}; -/// The `version` command is used to display information about Ghostty. +/// The `version` command is used to display information about Ghostty. Recognized as +/// either `+version` or `--version`. pub fn run(alloc: Allocator) !u8 { _ = alloc; @@ -50,19 +51,15 @@ pub fn run(alloc: Allocator) !u8 { gtk.gtk_get_minor_version(), gtk.gtk_get_micro_version(), }); - if (comptime build_options.adwaita) { - try stdout.print(" - libadwaita : enabled\n", .{}); - try stdout.print(" build : {s}\n", .{ - gtk.ADW_VERSION_S, - }); - try stdout.print(" runtime : {}.{}.{}\n", .{ - gtk.adw_get_major_version(), - gtk.adw_get_minor_version(), - gtk.adw_get_micro_version(), - }); - } else { - try stdout.print(" - libadwaita : disabled\n", .{}); - } + try stdout.print(" - libadwaita : enabled\n", .{}); + try stdout.print(" build : {s}\n", .{ + gtk.ADW_VERSION_S, + }); + try stdout.print(" runtime : {}.{}.{}\n", .{ + gtk.adw_get_major_version(), + gtk.adw_get_minor_version(), + gtk.adw_get_micro_version(), + }); if (comptime build_options.x11) { try stdout.print(" - libX11 : enabled\n", .{}); } else { diff --git a/src/config.zig b/src/config.zig index 75dbaae02b..a8ffe2ec7b 100644 --- a/src/config.zig +++ b/src/config.zig @@ -27,6 +27,7 @@ pub const OptionAsAlt = Config.OptionAsAlt; pub const RepeatableCodepointMap = Config.RepeatableCodepointMap; pub const RepeatableFontVariation = Config.RepeatableFontVariation; pub const RepeatableString = Config.RepeatableString; +pub const RepeatableStringMap = @import("config/RepeatableStringMap.zig"); pub const RepeatablePath = Config.RepeatablePath; pub const ShellIntegrationFeatures = Config.ShellIntegrationFeatures; pub const WindowPaddingColor = Config.WindowPaddingColor; diff --git a/src/config/Config.zig b/src/config/Config.zig index 2f38676c56..9e6f17ef4e 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -34,6 +34,7 @@ const KeyValue = @import("key.zig").Value; const ErrorList = @import("ErrorList.zig"); const MetricModifier = fontpkg.Metrics.Modifier; const help_strings = @import("help_strings"); +const RepeatableStringMap = @import("RepeatableStringMap.zig"); const log = std.log.scoped(.config); @@ -42,6 +43,16 @@ const c = @cImport({ @cInclude("unistd.h"); }); +/// Renamed fields, used by cli.parse +pub const renamed = std.StaticStringMap([]const u8).initComptime(&.{ + // Ghostty 1.1 introduced background-blur support for Linux which + // doesn't support a specific radius value. The renaming is to let + // one field be used for both platforms (macOS retained the ability + // to set a radius). + .{ "background-blur-radius", "background-blur" }, + .{ "adw-toolbar-style", "gtk-toolbar-style" }, +}); + /// The font families to use. /// /// You can generate the list of valid values using the CLI: @@ -248,6 +259,28 @@ const c = @cImport({ /// This is currently only supported on macOS. @"font-thicken-strength": u8 = 255, +/// What color space to use when performing alpha blending. +/// +/// This affects the appearance of text and of any images with transparency. +/// Additionally, custom shaders will receive colors in the configured space. +/// +/// Valid values: +/// +/// * `native` - Perform alpha blending in the native color space for the OS. +/// On macOS this corresponds to Display P3, and on Linux it's sRGB. +/// +/// * `linear` - Perform alpha blending in linear space. This will eliminate +/// the darkening artifacts around the edges of text that are very visible +/// when certain color combinations are used (e.g. red / green), but makes +/// dark text look much thinner than normal and light text much thicker. +/// This is also sometimes known as "gamma correction". +/// (Currently only supported on macOS. Has no effect on Linux.) +/// +/// * `linear-corrected` - Same as `linear`, but with a correction step applied +/// for text that makes it look nearly or completely identical to `native`, +/// but without any of the darkening artifacts. +@"alpha-blending": AlphaBlending = .native, + /// All of the configurations behavior adjust various metrics determined by the /// font. The values can be integers (1, -1, etc.) or a percentage (20%, -15%, /// etc.). In each case, the values represent the amount to change the original @@ -256,7 +289,7 @@ const c = @cImport({ /// For example, a value of `1` increases the value by 1; it does not set it to /// literally 1. A value of `20%` increases the value by 20%. And so on. /// -/// There is little to no validation on these values so the wrong values (i.e. +/// There is little to no validation on these values so the wrong values (e.g. /// `-100%`) can cause the terminal to be unusable. Use with caution and reason. /// /// Some values are clamped to minimum or maximum values. This can make it @@ -286,14 +319,14 @@ const c = @cImport({ /// See the notes about adjustments in `adjust-cell-width`. @"adjust-underline-thickness": ?MetricModifier = null, /// Distance in pixels or percentage adjustment from the top of the cell to the top of the strikethrough. -/// Increase to move strikethrough DOWN, decrease to move underline UP. +/// Increase to move strikethrough DOWN, decrease to move strikethrough UP. /// See the notes about adjustments in `adjust-cell-width`. @"adjust-strikethrough-position": ?MetricModifier = null, /// Thickness in pixels or percentage adjustment of the strikethrough. /// See the notes about adjustments in `adjust-cell-width`. @"adjust-strikethrough-thickness": ?MetricModifier = null, /// Distance in pixels or percentage adjustment from the top of the cell to the top of the overline. -/// Increase to move overline DOWN, decrease to move underline UP. +/// Increase to move overline DOWN, decrease to move overline UP. /// See the notes about adjustments in `adjust-cell-width`. @"adjust-overline-position": ?MetricModifier = null, /// Thickness in pixels or percentage adjustment of the overline. @@ -441,7 +474,7 @@ foreground: Color = .{ .r = 0xFF, .g = 0xFF, .b = 0xFF }, /// The minimum contrast ratio between the foreground and background colors. /// The contrast ratio is a value between 1 and 21. A value of 1 allows for no -/// contrast (i.e. black on black). This value is the contrast ratio as defined +/// contrast (e.g. black on black). This value is the contrast ratio as defined /// by the [WCAG 2.0 specification](https://www.w3.org/TR/WCAG20/). /// /// If you want to avoid invisible text (same color as background), a value of @@ -604,7 +637,7 @@ palette: Palette = .{}, /// /// Supported on macOS and on some Linux desktop environments, including: /// -/// * KDE Plasma (Wayland only) +/// * KDE Plasma (Wayland and X11) /// /// Warning: the exact blur intensity is _ignored_ under KDE Plasma, and setting /// this setting to either `true` or any positive blur intensity value would @@ -623,7 +656,7 @@ palette: Palette = .{}, /// need to set environment-specific settings and/or install third-party plugins /// in order to support background blur, as there isn't a unified interface for /// doing so. -@"background-blur-radius": BackgroundBlur = .false, +@"background-blur": BackgroundBlur = .false, /// The opacity level (opposite of transparency) of an unfocused split. /// Unfocused splits by default are slightly faded out to make it easier to see @@ -696,7 +729,7 @@ command: ?[]const u8 = null, /// injecting any configured shell integration into the command's /// environment. With `-e` its highly unlikely that you're executing a /// shell and forced shell integration is likely to cause problems -/// (i.e. by wrapping your command in a shell, setting env vars, etc.). +/// (e.g. by wrapping your command in a shell, setting env vars, etc.). /// This is a safety measure to prevent unexpected behavior. If you want /// shell integration with a `-e`-executed command, you must either /// name your binary appropriately or source the shell integration script @@ -704,6 +737,42 @@ command: ?[]const u8 = null, /// @"initial-command": ?[]const u8 = null, +/// Extra environment variables to pass to commands launched in a terminal +/// surface. The format is `env=KEY=VALUE`. +/// +/// `env = foo=bar` +/// `env = bar=baz` +/// +/// Setting `env` to an empty string will reset the entire map to default +/// (empty). +/// +/// `env =` +/// +/// Setting a key to an empty string will remove that particular key and +/// corresponding value from the map. +/// +/// `env = foo=bar` +/// `env = foo=` +/// +/// will result in `foo` not being passed to the launched commands. +/// +/// Setting a key multiple times will overwrite previous entries. +/// +/// `env = foo=bar` +/// `env = foo=baz` +/// +/// will result in `foo=baz` being passed to the launched commands. +/// +/// These environment variables will override any existing environment +/// variables set by Ghostty. For example, if you set `GHOSTTY_RESOURCES_DIR` +/// then the value you set here will override the value Ghostty typically +/// automatically injects. +/// +/// These environment variables _will not_ be passed to commands run by Ghostty +/// for other purposes, like `open` or `xdg-open` used to open URLs in your +/// browser. +env: RepeatableStringMap = .{}, + /// If true, keep the terminal open after the command exits. Normally, the /// terminal window closes when the running command (such as a shell) exits. /// With this true, the terminal window will stay open until any keypress is @@ -744,7 +813,7 @@ command: ?[]const u8 = null, /// Match a regular expression against the terminal text and associate clicking /// it with an action. This can be used to match URLs, file paths, etc. Actions -/// can be opening using the system opener (i.e. `open` or `xdg-open`) or +/// can be opening using the system opener (e.g. `open` or `xdg-open`) or /// executing any arbitrary binding action. /// /// Links that are configured earlier take precedence over links that are @@ -764,6 +833,11 @@ link: RepeatableLink = .{}, /// `link`). If you want to customize URL matching, use `link` and disable this. @"link-url": bool = true, +/// Whether to start the window in a maximized state. This setting applies +/// to new windows and does not apply to tabs, splits, etc. However, this setting +/// will apply to all new windows, not just the first one. +maximize: bool = false, + /// Start new windows in fullscreen. This setting applies to new windows and /// does not apply to tabs, splits, etc. However, this setting will apply to all /// new windows, not just the first one. @@ -845,7 +919,7 @@ class: ?[:0]const u8 = null, /// Valid keys are currently only listed in the /// [Ghostty source code](https://github.com/ghostty-org/ghostty/blob/d6e76858164d52cff460fedc61ddf2e560912d71/src/input/key.zig#L255). /// This is a documentation limitation and we will improve this in the future. -/// A common gotcha is that numeric keys are written as words: i.e. `one`, +/// A common gotcha is that numeric keys are written as words: e.g. `one`, /// `two`, `three`, etc. and not `1`, `2`, `3`. This will also be improved in /// the future. /// @@ -888,7 +962,7 @@ class: ?[:0]const u8 = null, /// * Ghostty will wait an indefinite amount of time for the next key in /// the sequence. There is no way to specify a timeout. The only way to /// force the output of a prefix key is to assign another keybind to -/// specifically output that key (i.e. `ctrl+a>ctrl+a=text:foo`) or +/// specifically output that key (e.g. `ctrl+a>ctrl+a=text:foo`) or /// press an unbound key which will send both keys to the program. /// /// * If a prefix in a sequence is previously bound, the sequence will @@ -918,13 +992,13 @@ class: ?[:0]const u8 = null, /// including `physical:`-prefixed triggers without specifying the /// prefix. /// -/// * `csi:text` - Send a CSI sequence. i.e. `csi:A` sends "cursor up". +/// * `csi:text` - Send a CSI sequence. e.g. `csi:A` sends "cursor up". /// -/// * `esc:text` - Send an escape sequence. i.e. `esc:d` deletes to the +/// * `esc:text` - Send an escape sequence. e.g. `esc:d` deletes to the /// end of the word to the right. /// /// * `text:text` - Send a string. Uses Zig string literal syntax. -/// i.e. `text:\x15` sends Ctrl-U. +/// e.g. `text:\x15` sends Ctrl-U. /// /// * All other actions can be found in the documentation or by using the /// `ghostty +list-actions` command. @@ -950,12 +1024,12 @@ class: ?[:0]const u8 = null, /// keybinds only apply to the focused terminal surface. If this is true, /// then the keybind will be sent to all terminal surfaces. This only /// applies to actions that are surface-specific. For actions that -/// are already global (i.e. `quit`), this prefix has no effect. +/// are already global (e.g. `quit`), this prefix has no effect. /// /// * `global:` - Make the keybind global. By default, keybinds only work /// within Ghostty and under the right conditions (application focused, /// sometimes terminal focused, etc.). If you want a keybind to work -/// globally across your system (i.e. even when Ghostty is not focused), +/// globally across your system (e.g. even when Ghostty is not focused), /// specify this prefix. This prefix implies `all:`. Note: this does not /// work in all environments; see the additional notes below for more /// information. @@ -979,6 +1053,12 @@ class: ?[:0]const u8 = null, /// performable (acting identically to not having a keybind set at /// all). /// +/// Performable keybinds will not appear as menu shortcuts in the +/// application menu. This is because the menu shortcuts force the +/// action to be performed regardless of the state of the terminal. +/// Performable keybinds will still work, they just won't appear as +/// a shortcut label in the menu. +/// /// Keybind triggers are not unique per prefix combination. For example, /// `ctrl+a` and `global:ctrl+a` are not two separate keybinds. The keybind /// set later will overwrite the keybind set earlier. In this case, the @@ -1056,7 +1136,7 @@ keybind: Keybinds = .{}, /// any of the heuristics that disable extending noted below. /// /// The "extend" value will be disabled in certain scenarios. On primary -/// screen applications (i.e. not something like Neovim), the color will not +/// screen applications (e.g. not something like Neovim), the color will not /// be extended vertically if any of the following are true: /// /// * The nearest row has any cells that have the default background color. @@ -1096,21 +1176,52 @@ keybind: Keybinds = .{}, /// configuration `font-size` will be used. @"window-inherit-font-size": bool = true, +/// Configure a preference for window decorations. This setting specifies +/// a _preference_; the actual OS, desktop environment, window manager, etc. +/// may override this preference. Ghostty will do its best to respect this +/// preference but it may not always be possible. +/// /// Valid values: /// -/// * `true` -/// * `false` - windows won't have native decorations, i.e. titlebar and -/// borders. On macOS this also disables tabs and tab overview. +/// * `none` - All window decorations will be disabled. Titlebar, +/// borders, etc. will not be shown. On macOS, this will also disable +/// tabs (enforced by the system). +/// +/// * `auto` - Automatically decide to use either client-side or server-side +/// decorations based on the detected preferences of the current OS and +/// desktop environment. This option usually makes Ghostty look the most +/// "native" for your desktop. +/// +/// * `client` - Prefer client-side decorations. +/// +/// * `server` - Prefer server-side decorations. This is only relevant +/// on Linux with GTK, either on X11, or Wayland on a compositor that +/// supports the `org_kde_kwin_server_decoration` protocol (e.g. KDE Plasma, +/// but almost any non-GNOME desktop supports this protocol). +/// +/// If `server` is set but the environment doesn't support server-side +/// decorations, client-side decorations will be used instead. +/// +/// The default value is `auto`. +/// +/// For the sake of backwards compatibility and convenience, this setting also +/// accepts boolean true and false values. If set to `true`, this is equivalent +/// to `auto`. If set to `false`, this is equivalent to `none`. +/// This is convenient for users who live primarily on systems that don't +/// differentiate between client and server-side decorations (e.g. macOS and +/// Windows). /// /// The "toggle_window_decorations" keybind action can be used to create -/// a keybinding to toggle this setting at runtime. +/// a keybinding to toggle this setting at runtime. This will always toggle +/// back to "auto" if the current value is "none" (this is an issue +/// that will be fixed in the future). /// /// Changing this configuration in your configuration and reloading will /// only affect new windows. Existing windows will not be affected. /// /// macOS: To hide the titlebar without removing the native window borders /// or rounded corners, use `macos-titlebar-style = hidden` instead. -@"window-decoration": bool = true, +@"window-decoration": WindowDecoration = .auto, /// The font that will be used for the application's window and tab titles. /// @@ -1120,6 +1231,15 @@ keybind: Keybinds = .{}, /// required to be a fixed-width font. @"window-title-font-family": ?[:0]const u8 = null, +/// The text that will be displayed in the subtitle of the window. Valid values: +/// +/// * `false` - Disable the subtitle. +/// * `working-directory` - Set the subtitle to the working directory of the +/// surface. +/// +/// This feature is only supported on GTK. +@"window-subtitle": WindowSubtitle = .false, + /// The theme to use for the windows. Valid values: /// /// * `auto` - Determine the theme based on the configured terminal @@ -1130,8 +1250,7 @@ keybind: Keybinds = .{}, /// * `light` - Use the light theme regardless of system theme. /// * `dark` - Use the dark theme regardless of system theme. /// * `ghostty` - Use the background and foreground colors specified in the -/// Ghostty configuration. This is only supported on Linux builds with -/// Adwaita and `gtk-adwaita` enabled. +/// Ghostty configuration. This is only supported on Linux builds. /// /// On macOS, if `macos-titlebar-style` is "tabs", the window theme will be /// automatically set based on the luminosity of the terminal background color. @@ -1141,12 +1260,16 @@ keybind: Keybinds = .{}, /// This is currently only supported on macOS and Linux. @"window-theme": WindowTheme = .auto, -/// The colorspace to use for the terminal window. The default is `srgb` but -/// this can also be set to `display-p3` to use the Display P3 colorspace. +/// The color space to use when interpreting terminal colors. "Terminal colors" +/// refers to colors specified in your configuration and colors produced by +/// direct-color SGR sequences. /// -/// Changing this value at runtime will only affect new windows. +/// Valid values: +/// +/// * `srgb` - Interpret colors in the sRGB color space. This is the default. +/// * `display-p3` - Interpret colors in the Display P3 color space. /// -/// This setting is only supported on macOS. +/// This setting is currently only supported on macOS. @"window-colorspace": WindowColorspace = .srgb, /// The initial window size. This size is in terminal grid cells by default. @@ -1324,7 +1447,7 @@ keybind: Keybinds = .{}, @"resize-overlay-duration": Duration = .{ .duration = 750 * std.time.ns_per_ms }, /// If true, when there are multiple split panes, the mouse selects the pane -/// that is focused. This only applies to the currently focused window; i.e. +/// that is focused. This only applies to the currently focused window; e.g. /// mousing over a split in an unfocused window will not focus that split /// and bring the window to front. /// @@ -1368,7 +1491,7 @@ keybind: Keybinds = .{}, /// and a minor amount of user interaction). @"title-report": bool = false, -/// The total amount of bytes that can be used for image data (i.e. the Kitty +/// The total amount of bytes that can be used for image data (e.g. the Kitty /// image protocol) per terminal screen. The maximum value is 4,294,967,295 /// (4GiB). The default is 320MB. If this is set to zero, then all image /// protocols will be disabled. @@ -1378,24 +1501,19 @@ keybind: Keybinds = .{}, @"image-storage-limit": u32 = 320 * 1000 * 1000, /// Whether to automatically copy selected text to the clipboard. `true` -/// will prefer to copy to the selection clipboard if supported by the -/// OS, otherwise it will copy to the system clipboard. +/// will prefer to copy to the selection clipboard, otherwise it will copy to +/// the system clipboard. /// /// The value `clipboard` will always copy text to the selection clipboard -/// (for supported systems) as well as the system clipboard. This is sometimes -/// a preferred behavior on Linux. +/// as well as the system clipboard. /// -/// Middle-click paste will always use the selection clipboard on Linux -/// and the system clipboard on macOS. Middle-click paste is always enabled -/// even if this is `false`. +/// Middle-click paste will always use the selection clipboard. Middle-click +/// paste is always enabled even if this is `false`. /// -/// The default value is true on Linux and false on macOS. macOS copy on -/// select behavior is not typical for applications so it is disabled by -/// default. On Linux, this is a standard behavior so it is enabled by -/// default. +/// The default value is true on Linux and macOS. @"copy-on-select": CopyOnSelect = switch (builtin.os.tag) { .linux => .true, - .macos => .false, + .macos => .true, else => .false, }, @@ -1561,6 +1679,23 @@ keybind: Keybinds = .{}, /// Set it to false for the quick terminal to remain open even when it loses focus. @"quick-terminal-autohide": bool = true, +/// This configuration option determines the behavior of the quick terminal +/// when switching between macOS spaces. macOS spaces are virtual desktops +/// that can be manually created or are automatically created when an +/// application is in full-screen mode. +/// +/// Valid values are: +/// +/// * `move` - When switching to another space, the quick terminal will +/// also moved to the current space. +/// +/// * `remain` - The quick terminal will stay only in the space where it +/// was originally opened and will not follow when switching to another +/// space. +/// +/// The default value is `move`. +@"quick-terminal-space-behavior": QuickTerminalSpaceBehavior = .move, + /// Whether to enable shell integration auto-injection or not. Shell integration /// greatly enhances the terminal experience by enabling a number of features: /// @@ -1587,7 +1722,9 @@ keybind: Keybinds = .{}, /// The default value is `detect`. @"shell-integration": ShellIntegration = .detect, -/// Shell integration features to enable if shell integration itself is enabled. +/// Shell integration features to enable. These require our shell integration +/// to be loaded, either automatically via shell-integration or manually. +/// /// The format of this is a list of features to enable separated by commas. If /// you prefix a feature with `no-` then it is disabled. If you omit a feature, /// its default value is used, so you must explicitly disable features you don't @@ -1616,7 +1753,7 @@ keybind: Keybinds = .{}, /// /// * `none` - OSC 4/10/11 queries receive no reply /// -/// * `8-bit` - Color components are return unscaled, i.e. `rr/gg/bb` +/// * `8-bit` - Color components are return unscaled, e.g. `rr/gg/bb` /// /// * `16-bit` - Color components are returned scaled, e.g. `rrrr/gggg/bbbb` /// @@ -1677,6 +1814,31 @@ keybind: Keybinds = .{}, /// open terminals. @"custom-shader-animation": CustomShaderAnimation = .true, +/// Control the in-app notifications that Ghostty shows. +/// +/// On Linux (GTK), in-app notifications show up as toasts. Toasts appear +/// overlaid on top of the terminal window. They are used to show information +/// that is not critical but may be important. +/// +/// Possible notifications are: +/// +/// - `clipboard-copy` (default: true) - Show a notification when text is copied +/// to the clipboard. +/// +/// To specify a notification to enable, specify the name of the notification. +/// To specify a notification to disable, prefix the name with `no-`. For +/// example, to disable `clipboard-copy`, set this configuration to +/// `no-clipboard-copy`. To enable it, set this configuration to `clipboard-copy`. +/// +/// Multiple notifications can be enabled or disabled by separating them +/// with a comma. +/// +/// A value of "false" will disable all notifications. A value of "true" will +/// enable all notifications. +/// +/// This configuration only applies to GTK. +@"app-notifications": AppNotifications = .{}, + /// If anything other than false, fullscreen mode on macOS will not use the /// native fullscreen, but make the window fullscreen without animations and /// using a new space. It's faster than the native fullscreen mode since it @@ -1694,9 +1856,14 @@ keybind: Keybinds = .{}, /// /// Allowable values are: /// -/// * `visible-menu` - Use non-native macOS fullscreen, keep the menu bar visible /// * `true` - Use non-native macOS fullscreen, hide the menu bar /// * `false` - Use native macOS fullscreen +/// * `visible-menu` - Use non-native macOS fullscreen, keep the menu bar +/// visible +/// * `padded-notch` - Use non-native macOS fullscreen, hide the menu bar, +/// but ensure the window is not obscured by the notch on applicable +/// devices. The area around the notch will remain transparent currently, +/// but in the future we may fill it with the window background color. /// /// Changing this option at runtime works, but will only apply to the next /// time the window is made fullscreen. If a window is already fullscreen, @@ -1715,7 +1882,7 @@ keybind: Keybinds = .{}, /// typical for a macOS application and may not work well with all themes. /// /// The "transparent" style will also update in real-time to dynamic -/// changes to the window background color, i.e. via OSC 11. To make this +/// changes to the window background color, e.g. via OSC 11. To make this /// more aesthetically pleasing, this only happens if the terminal is /// a window, tab, or split that borders the top of the window. This /// avoids a disjointed appearance where the titlebar color changes @@ -1731,9 +1898,12 @@ keybind: Keybinds = .{}, /// The "hidden" style hides the titlebar. Unlike `window-decoration = false`, /// however, it does not remove the frame from the window or cause it to have /// squared corners. Changing to or from this option at run-time may affect -/// existing windows in buggy ways. The top titlebar area of the window will -/// continue to drag the window around and you will not be able to use -/// the mouse for terminal events in this space. +/// existing windows in buggy ways. +/// +/// When "hidden", the top titlebar area can no longer be used for dragging +/// the window. To drag the window, you can use option+click on the resizable +/// areas of the frame to drag the window. This is a standard macOS behavior +/// and not something Ghostty enables. /// /// The default value is "transparent". This is an opinionated choice /// but its one I think is the most aesthetically pleasing and works in @@ -1782,7 +1952,7 @@ keybind: Keybinds = .{}, /// - U.S. International /// /// Note that if an *Option*-sequence doesn't produce a printable character, it -/// will be treated as *Alt* regardless of this setting. (i.e. `alt+ctrl+a`). +/// will be treated as *Alt* regardless of this setting. (e.g. `alt+ctrl+a`). /// /// Explicit values that can be set: /// @@ -1804,6 +1974,26 @@ keybind: Keybinds = .{}, /// find false more visually appealing. @"macos-window-shadow": bool = true, +/// If true, the macOS icon in the dock and app switcher will be hidden. This is +/// mainly intended for those primarily using the quick-terminal mode. +/// +/// Note that setting this to true means that keyboard layout changes +/// will no longer be automatic. +/// +/// Control whether macOS app is excluded from the dock and app switcher, +/// a "hidden" state. This is mainly intended for those primarily using +/// quick-terminal mode, but is a general configuration for any use +/// case. +/// +/// Available values: +/// +/// * `never` - The macOS app is never hidden. +/// * `always` - The macOS app is always hidden. +/// +/// Note: When the macOS application is hidden, keyboard layout changes +/// will no longer be automatic. This is a limitation of macOS. +@"macos-hidden": MacHidden = .never, + /// If true, Ghostty on macOS will automatically enable the "Secure Input" /// feature when it detects that a password prompt is being displayed. /// @@ -1844,6 +2034,9 @@ keybind: Keybinds = .{}, /// Valid values: /// /// * `official` - Use the official Ghostty icon. +/// * `blueprint`, `chalkboard`, `microchip`, `glass`, `holographic`, +/// `paper`, `retro`, `xray` - Official variants of the Ghostty icon +/// hand-created by artists (no AI). /// * `custom-style` - Use the official Ghostty icon but with custom /// styles applied to various layers. The custom styles must be /// specified using the additional `macos-icon`-prefixed configurations. @@ -1954,6 +2147,18 @@ keybind: Keybinds = .{}, /// must always be able to move themselves into an isolated cgroup. @"linux-cgroup-hard-fail": bool = false, +/// Enable or disable GTK's OpenGL debugging logs. The default is `true` for +/// debug builds, `false` for all others. +@"gtk-opengl-debug": bool = builtin.mode == .Debug, + +/// After GTK 4.14.0, we need to force the GSK renderer to OpenGL as the default +/// GSK renderer is broken on some systems. If you would like to override +/// that bekavior, set `gtk-gsk-renderer=default` and either use your system's +/// default GSK renderer, or set the GSK_RENDERER environment variable to your +/// renderer of choice before launching Ghostty. This setting has no effect when +/// using versions of GTK earlier than 4.14.0. +@"gtk-gsk-renderer": GtkGskRenderer = .opengl, + /// If `true`, the Ghostty GTK application will run in single-instance mode: /// each new `ghostty` process launched will result in a new window if there is /// already a running process. @@ -1981,22 +2186,20 @@ keybind: Keybinds = .{}, @"gtk-titlebar": bool = true, /// Determines the side of the screen that the GTK tab bar will stick to. -/// Top, bottom, left, right, and hidden are supported. The default is top. +/// Top, bottom, and hidden are supported. The default is top. /// -/// If this option has value `left` or `right` when using Adwaita, it falls -/// back to `top`. `hidden`, meaning that tabs don't exist, is not supported -/// without using Adwaita, falling back to `top`. -/// -/// When `hidden` is set and Adwaita is enabled, a tab button displaying the -/// number of tabs will appear in the title bar. It has the ability to open a -/// tab overview for displaying tabs. Alternatively, you can use the -/// `toggle_tab_overview` action in a keybind if your window doesn't have a -/// title bar, or you can switch tabs with keybinds. +/// When `hidden` is set, a tab button displaying the number of tabs will appear +/// in the title bar. It has the ability to open a tab overview for displaying +/// tabs. Alternatively, you can use the `toggle_tab_overview` action in a +/// keybind if your window doesn't have a title bar, or you can switch tabs +/// with keybinds. @"gtk-tabs-location": GtkTabsLocation = .top, -/// Determines the appearance of the top and bottom bars when using the -/// Adwaita tab bar. This requires `gtk-adwaita` to be enabled (it is -/// by default). +/// If this is `true`, the titlebar will be hidden when the window is maximized, +/// and shown when the titlebar is unmaximized. GTK only. +@"gtk-titlebar-hide-when-maximized": bool = false, + +/// Determines the appearance of the top and bottom bars tab bar. /// /// Valid values are: /// @@ -2006,30 +2209,7 @@ keybind: Keybinds = .{}, /// more subtle border. /// /// Changing this value at runtime will only affect new windows. -@"adw-toolbar-style": AdwToolbarStyle = .raised, - -/// Control the toasts that Ghostty shows. Toasts are small notifications -/// that appear overlaid on top of the terminal window. They are used to -/// show information that is not critical but may be important. -/// -/// Possible toasts are: -/// -/// - `clipboard-copy` (default: true) - Show a toast when text is copied -/// to the clipboard. -/// -/// To specify a toast to enable, specify the name of the toast. To specify -/// a toast to disable, prefix the name with `no-`. For example, to disable -/// the clipboard-copy toast, set this configuration to `no-clipboard-copy`. -/// To enable the clipboard-copy toast, set this configuration to -/// `clipboard-copy`. -/// -/// Multiple toasts can be enabled or disabled by separating them with a comma. -/// -/// A value of "false" will disable all toasts. A value of "true" will -/// enable all toasts. -/// -/// This configuration only applies to GTK with Adwaita enabled. -@"adw-toast": AdwToast = .{}, +@"gtk-toolbar-style": GtkToolbarStyle = .raised, /// If `true` (default), then the Ghostty GTK tabs will be "wide." Wide tabs /// are the new typical Gnome style where tabs fill their available space. @@ -2037,20 +2217,6 @@ keybind: Keybinds = .{}, /// which is the old style. @"gtk-wide-tabs": bool = true, -/// If `true` (default), Ghostty will enable Adwaita theme support. This -/// will make `window-theme` work properly and will also allow Ghostty to -/// properly respond to system theme changes, light/dark mode changing, etc. -/// This requires a GTK4 desktop with a GTK4 theme. -/// -/// If you are running GTK3 or have a GTK3 theme, you may have to set this -/// to false to get your theme picked up properly. Having this set to true -/// with GTK3 should not cause any problems, but it may not work exactly as -/// expected. -/// -/// This configuration only has an effect if Ghostty was built with -/// Adwaita support. -@"gtk-adwaita": bool = true, - /// Custom CSS files to be loaded. /// /// This configuration can be repeated multiple times to load multiple files. @@ -2269,13 +2435,13 @@ pub fn default(alloc_gpa: Allocator) Allocator.Error!Config { try result.keybind.set.put( alloc, .{ .key = .{ .translated = .j }, .mods = inputpkg.ctrlOrSuper(.{ .shift = true }) }, - .{ .write_scrollback_file = .paste }, + .{ .write_screen_file = .paste }, ); try result.keybind.set.put( alloc, .{ .key = .{ .translated = .j }, .mods = inputpkg.ctrlOrSuper(.{ .shift = true, .alt = true }) }, - .{ .write_scrollback_file = .open }, + .{ .write_screen_file = .open }, ); // Expand Selection @@ -2367,6 +2533,11 @@ pub fn default(alloc_gpa: Allocator) Allocator.Error!Config { .{ .key = .{ .translated = .t }, .mods = .{ .ctrl = true, .shift = true } }, .{ .new_tab = {} }, ); + try result.keybind.set.put( + alloc, + .{ .key = .{ .translated = .w }, .mods = .{ .ctrl = true, .shift = true } }, + .{ .close_tab = {} }, + ); try result.keybind.set.put( alloc, .{ .key = .{ .translated = .left }, .mods = .{ .ctrl = true, .shift = true } }, @@ -2632,6 +2803,11 @@ pub fn default(alloc_gpa: Allocator) Allocator.Error!Config { .{ .key = .{ .translated = .w }, .mods = .{ .super = true } }, .{ .close_surface = {} }, ); + try result.keybind.set.put( + alloc, + .{ .key = .{ .translated = .w }, .mods = .{ .super = true, .alt = true } }, + .{ .close_tab = {} }, + ); try result.keybind.set.put( alloc, .{ .key = .{ .translated = .w }, .mods = .{ .super = true, .shift = true } }, @@ -2749,6 +2925,13 @@ pub fn default(alloc_gpa: Allocator) Allocator.Error!Config { .{ .toggle_fullscreen = {} }, ); + // Selection clipboard paste, matches Terminal.app + try result.keybind.set.put( + alloc, + .{ .key = .{ .translated = .v }, .mods = .{ .super = true, .shift = true } }, + .{ .paste_from_selection = {} }, + ); + // "Natural text editing" keybinds. This forces these keys to go back // to legacy encoding (not fixterms). It seems macOS users more than // others are used to these keys so we set them as defaults. If @@ -2767,7 +2950,7 @@ pub fn default(alloc_gpa: Allocator) Allocator.Error!Config { try result.keybind.set.put( alloc, .{ .key = .{ .translated = .backspace }, .mods = .{ .super = true } }, - .{ .esc = "\x15" }, + .{ .text = "\\x15" }, ); try result.keybind.set.put( alloc, @@ -3954,6 +4137,7 @@ pub const NonNativeFullscreen = enum(c_int) { false, true, @"visible-menu", + @"padded-notch", }; /// Valid values for macos-option-as-alt. @@ -3970,6 +4154,11 @@ pub const WindowPaddingColor = enum { @"extend-always", }; +pub const WindowSubtitle = enum { + false, + @"working-directory", +}; + /// Color represents a color using RGB. /// /// This is a packed struct so that the C API to read color values just @@ -5569,6 +5758,12 @@ pub const MacTitlebarProxyIcon = enum { hidden, }; +/// See macos-hidden +pub const MacHidden = enum { + never, + always, +}; + /// See macos-icon /// /// Note: future versions of Ghostty can support a custom icon with @@ -5576,6 +5771,14 @@ pub const MacTitlebarProxyIcon = enum { /// format at all. pub const MacAppIcon = enum { official, + blueprint, + chalkboard, + microchip, + glass, + holographic, + paper, + retro, + xray, @"custom-style", }; @@ -5598,20 +5801,18 @@ pub const GtkSingleInstance = enum { pub const GtkTabsLocation = enum { top, bottom, - left, - right, hidden, }; -/// See adw-toolbar-style -pub const AdwToolbarStyle = enum { +/// See gtk-toolbar-style +pub const GtkToolbarStyle = enum { flat, raised, @"raised-border", }; -/// See adw-toast -pub const AdwToast = packed struct { +/// See app-notifications +pub const AppNotifications = packed struct { @"clipboard-copy": bool = true, }; @@ -5677,12 +5878,32 @@ pub const QuickTerminalScreen = enum { @"macos-menu-bar", }; +// See quick-terminal-space-behavior +pub const QuickTerminalSpaceBehavior = enum { + remain, + move, +}; + /// See grapheme-width-method pub const GraphemeWidthMethod = enum { legacy, unicode, }; +/// See alpha-blending +pub const AlphaBlending = enum { + native, + linear, + @"linear-corrected", + + pub fn isLinear(self: AlphaBlending) bool { + return switch (self) { + .native => false, + .linear, .@"linear-corrected" => true, + }; + } +}; + /// See freetype-load-flag pub const FreetypeLoadFlags = packed struct { // The defaults here at the time of writing this match the defaults @@ -5708,7 +5929,7 @@ pub const AutoUpdate = enum { download, }; -/// See background-blur-radius +/// See background-blur pub const BackgroundBlur = union(enum) { false, true, @@ -5731,6 +5952,14 @@ pub const BackgroundBlur = union(enum) { ) catch return error.InvalidValue }; } + pub fn enabled(self: BackgroundBlur) bool { + return switch (self) { + .false => false, + .true => true, + .radius => |v| v > 0, + }; + } + pub fn cval(self: BackgroundBlur) u8 { return switch (self) { .false => 0, @@ -5772,6 +6001,62 @@ pub const BackgroundBlur = union(enum) { } }; +/// See window-decoration +pub const WindowDecoration = enum { + auto, + client, + server, + none, + + pub fn parseCLI(input_: ?[]const u8) !WindowDecoration { + const input = input_ orelse return .auto; + + return if (cli.args.parseBool(input)) |b| + if (b) .auto else .none + else |_| if (std.meta.stringToEnum(WindowDecoration, input)) |v| + v + else + error.InvalidValue; + } + + test "parse WindowDecoration" { + const testing = std.testing; + + { + const v = try WindowDecoration.parseCLI(null); + try testing.expectEqual(WindowDecoration.auto, v); + } + { + const v = try WindowDecoration.parseCLI("true"); + try testing.expectEqual(WindowDecoration.auto, v); + } + { + const v = try WindowDecoration.parseCLI("false"); + try testing.expectEqual(WindowDecoration.none, v); + } + { + const v = try WindowDecoration.parseCLI("server"); + try testing.expectEqual(WindowDecoration.server, v); + } + { + const v = try WindowDecoration.parseCLI("client"); + try testing.expectEqual(WindowDecoration.client, v); + } + { + const v = try WindowDecoration.parseCLI("auto"); + try testing.expectEqual(WindowDecoration.auto, v); + } + { + const v = try WindowDecoration.parseCLI("none"); + try testing.expectEqual(WindowDecoration.none, v); + } + { + try testing.expectError(error.InvalidValue, WindowDecoration.parseCLI("")); + try testing.expectError(error.InvalidValue, WindowDecoration.parseCLI("aaaa")); + } + } +}; + /// See theme pub const Theme = struct { light: []const u8, @@ -6101,6 +6386,12 @@ pub const WindowPadding = struct { } }; +/// See the `gtk-gsk-renderer` config. +pub const GtkGskRenderer = enum { + default, + opengl, +}; + test "parse duration" { inline for (Duration.units) |unit| { var buf: [16]u8 = undefined; diff --git a/src/config/RepeatableStringMap.zig b/src/config/RepeatableStringMap.zig new file mode 100644 index 0000000000..6f143e95d7 --- /dev/null +++ b/src/config/RepeatableStringMap.zig @@ -0,0 +1,198 @@ +/// RepeatableStringMap is a key/value that can be repeated to accumulate a +/// string map. This isn't called "StringMap" because I find that sometimes +/// leads to confusion that it _accepts_ a map such as JSON dict. +const RepeatableStringMap = @This(); +const std = @import("std"); +const Allocator = std.mem.Allocator; + +const formatterpkg = @import("formatter.zig"); + +const Map = std.ArrayHashMapUnmanaged( + [:0]const u8, + [:0]const u8, + std.array_hash_map.StringContext, + true, +); + +// Allocator for the list is the arena for the parent config. +map: Map = .{}, + +pub fn parseCLI( + self: *RepeatableStringMap, + alloc: Allocator, + input: ?[]const u8, +) !void { + const value = input orelse return error.ValueRequired; + + // Empty value resets the list. We don't need to free our values because + // the allocator used is always an arena. + if (value.len == 0) { + self.map.clearRetainingCapacity(); + return; + } + + const index = std.mem.indexOfScalar( + u8, + value, + '=', + ) orelse return error.ValueRequired; + + const key = std.mem.trim(u8, value[0..index], &std.ascii.whitespace); + const val = std.mem.trim(u8, value[index + 1 ..], &std.ascii.whitespace); + + const key_copy = try alloc.dupeZ(u8, key); + errdefer alloc.free(key_copy); + + // Empty value removes the key from the map. + if (val.len == 0) { + _ = self.map.orderedRemove(key_copy); + alloc.free(key_copy); + return; + } + + const val_copy = try alloc.dupeZ(u8, val); + errdefer alloc.free(val_copy); + + try self.map.put(alloc, key_copy, val_copy); +} + +/// Deep copy of the struct. Required by Config. +pub fn clone( + self: *const RepeatableStringMap, + alloc: Allocator, +) Allocator.Error!RepeatableStringMap { + var map: Map = .{}; + try map.ensureTotalCapacity(alloc, self.map.count()); + + errdefer { + var it = map.iterator(); + while (it.next()) |entry| { + alloc.free(entry.key_ptr.*); + alloc.free(entry.value_ptr.*); + } + map.deinit(alloc); + } + + var it = self.map.iterator(); + while (it.next()) |entry| { + const key = try alloc.dupeZ(u8, entry.key_ptr.*); + const value = try alloc.dupeZ(u8, entry.value_ptr.*); + map.putAssumeCapacity(key, value); + } + + return .{ .map = map }; +} + +/// The number of items in the map +pub fn count(self: RepeatableStringMap) usize { + return self.map.count(); +} + +/// Iterator over the entries in the map. +pub fn iterator(self: RepeatableStringMap) Map.Iterator { + return self.map.iterator(); +} + +/// Compare if two of our value are requal. Required by Config. +pub fn equal(self: RepeatableStringMap, other: RepeatableStringMap) bool { + if (self.map.count() != other.map.count()) return false; + var it = self.map.iterator(); + while (it.next()) |entry| { + const value = other.map.get(entry.key_ptr.*) orelse return false; + if (!std.mem.eql(u8, entry.value_ptr.*, value)) return false; + } else return true; +} + +/// Used by formatter +pub fn formatEntry(self: RepeatableStringMap, formatter: anytype) !void { + // If no items, we want to render an empty field. + if (self.map.count() == 0) { + try formatter.formatEntry(void, {}); + return; + } + + var it = self.map.iterator(); + while (it.next()) |entry| { + var buf: [256]u8 = undefined; + const value = std.fmt.bufPrint(&buf, "{s}={s}", .{ entry.key_ptr.*, entry.value_ptr.* }) catch |err| switch (err) { + error.NoSpaceLeft => return error.OutOfMemory, + }; + try formatter.formatEntry([]const u8, value); + } +} + +test "RepeatableStringMap: parseCLI" { + const testing = std.testing; + var arena = std.heap.ArenaAllocator.init(testing.allocator); + defer arena.deinit(); + const alloc = arena.allocator(); + + var map: RepeatableStringMap = .{}; + + try testing.expectError(error.ValueRequired, map.parseCLI(alloc, "A")); + + try map.parseCLI(alloc, "A=B"); + try map.parseCLI(alloc, "B=C"); + try testing.expectEqual(@as(usize, 2), map.count()); + + try map.parseCLI(alloc, ""); + try testing.expectEqual(@as(usize, 0), map.count()); + + try map.parseCLI(alloc, "A=B"); + try testing.expectEqual(@as(usize, 1), map.count()); + try map.parseCLI(alloc, "A=C"); + try testing.expectEqual(@as(usize, 1), map.count()); +} + +test "RepeatableStringMap: formatConfig empty" { + const testing = std.testing; + var buf = std.ArrayList(u8).init(testing.allocator); + defer buf.deinit(); + + var list: RepeatableStringMap = .{}; + try list.formatEntry(formatterpkg.entryFormatter("a", buf.writer())); + try std.testing.expectEqualSlices(u8, "a = \n", buf.items); +} + +test "RepeatableStringMap: formatConfig single item" { + const testing = std.testing; + + var arena = std.heap.ArenaAllocator.init(testing.allocator); + defer arena.deinit(); + const alloc = arena.allocator(); + + { + var buf = std.ArrayList(u8).init(testing.allocator); + defer buf.deinit(); + var map: RepeatableStringMap = .{}; + try map.parseCLI(alloc, "A=B"); + try map.formatEntry(formatterpkg.entryFormatter("a", buf.writer())); + try std.testing.expectEqualSlices(u8, "a = A=B\n", buf.items); + } + { + var buf = std.ArrayList(u8).init(testing.allocator); + defer buf.deinit(); + var map: RepeatableStringMap = .{}; + try map.parseCLI(alloc, " A = B "); + try map.formatEntry(formatterpkg.entryFormatter("a", buf.writer())); + try std.testing.expectEqualSlices(u8, "a = A=B\n", buf.items); + } +} + +test "RepeatableStringMap: formatConfig multiple items" { + const testing = std.testing; + + var arena = std.heap.ArenaAllocator.init(testing.allocator); + defer arena.deinit(); + const alloc = arena.allocator(); + + { + var buf = std.ArrayList(u8).init(testing.allocator); + defer buf.deinit(); + var list: RepeatableStringMap = .{}; + try list.parseCLI(alloc, "A=B"); + try list.parseCLI(alloc, "B = C"); + try list.formatEntry(formatterpkg.entryFormatter("a", buf.writer())); + try std.testing.expectEqualSlices(u8, "a = A=B\na = B=C\n", buf.items); + } +} diff --git a/src/config/c_get.zig b/src/config/c_get.zig index 6804b0ae0c..251a95e772 100644 --- a/src/config/c_get.zig +++ b/src/config/c_get.zig @@ -192,21 +192,21 @@ test "c_get: background-blur" { defer c.deinit(); { - c.@"background-blur-radius" = .false; + c.@"background-blur" = .false; var cval: u8 = undefined; - try testing.expect(get(&c, .@"background-blur-radius", @ptrCast(&cval))); + try testing.expect(get(&c, .@"background-blur", @ptrCast(&cval))); try testing.expectEqual(0, cval); } { - c.@"background-blur-radius" = .true; + c.@"background-blur" = .true; var cval: u8 = undefined; - try testing.expect(get(&c, .@"background-blur-radius", @ptrCast(&cval))); + try testing.expect(get(&c, .@"background-blur", @ptrCast(&cval))); try testing.expectEqual(20, cval); } { - c.@"background-blur-radius" = .{ .radius = 42 }; + c.@"background-blur" = .{ .radius = 42 }; var cval: u8 = undefined; - try testing.expect(get(&c, .@"background-blur-radius", @ptrCast(&cval))); + try testing.expect(get(&c, .@"background-blur", @ptrCast(&cval))); try testing.expectEqual(42, cval); } } diff --git a/src/config/theme.zig b/src/config/theme.zig index b851ec3d46..2d206e1f64 100644 --- a/src/config/theme.zig +++ b/src/config/theme.zig @@ -104,6 +104,10 @@ pub const LocationIterator = struct { /// Due to the way allocations are handled, an Arena allocator (or another /// similar allocator implementation) should be used. It may not be safe to /// free the returned allocations. +/// +/// This will never return anything other than a handle to a regular file. If +/// the theme resolves to something other than a regular file a diagnostic entry +/// will be added to the list and null will be returned. pub fn open( arena_alloc: Allocator, theme: []const u8, @@ -119,6 +123,29 @@ pub fn open( theme, diags, ) orelse return null; + const stat = file.stat() catch |err| { + try diags.append(arena_alloc, .{ + .message = try std.fmt.allocPrintZ( + arena_alloc, + "not reading theme from \"{s}\": {}", + .{ theme, err }, + ), + }); + return null; + }; + switch (stat.kind) { + .file => {}, + else => { + try diags.append(arena_alloc, .{ + .message = try std.fmt.allocPrintZ( + arena_alloc, + "not reading theme from \"{s}\": it is a {s}", + .{ theme, @tagName(stat.kind) }, + ), + }); + return null; + }, + } return .{ .path = theme, .file = file }; } @@ -140,9 +167,34 @@ pub fn open( const cwd = std.fs.cwd(); while (try it.next()) |loc| { const path = try std.fs.path.join(arena_alloc, &.{ loc.dir, theme }); - if (cwd.openFile(path, .{})) |file| return .{ - .path = path, - .file = file, + if (cwd.openFile(path, .{})) |file| { + const stat = file.stat() catch |err| { + try diags.append(arena_alloc, .{ + .message = try std.fmt.allocPrintZ( + arena_alloc, + "not reading theme from \"{s}\": {}", + .{ theme, err }, + ), + }); + return null; + }; + switch (stat.kind) { + .file => {}, + else => { + try diags.append(arena_alloc, .{ + .message = try std.fmt.allocPrintZ( + arena_alloc, + "not reading theme from \"{s}\": it is a {s}", + .{ theme, @tagName(stat.kind) }, + ), + }); + return null; + }, + } + return .{ + .path = path, + .file = file, + }; } else |err| switch (err) { // Not an error, just continue to the next location. error.FileNotFound => {}, diff --git a/src/config/url.zig b/src/config/url.zig index dcc1902e66..3a7034e0ad 100644 --- a/src/config/url.zig +++ b/src/config/url.zig @@ -26,7 +26,7 @@ pub const regex = "(?:" ++ url_schemes ++ \\)(?: ++ ipv6_url_pattern ++ - \\|[\w\-.~:/?#@!$&*+,;=%]+(?:[\(\[]\w*[\)\]])?)+(? + \\" + \\" THIS FILE IS AUTO-GENERATED + \\ + \\au BufRead,BufNewFile */ghostty/config,*/ghostty/themes/* set ft=ghostty + \\ +; pub const ftplugin = \\" Vim filetype plugin file \\" Language: Ghostty config file @@ -31,13 +40,19 @@ pub const ftplugin = \\ ; pub const compiler = + \\" Vim compiler file + \\" Language: Ghostty config file + \\" Maintainer: Ghostty + \\" + \\" THIS FILE IS AUTO-GENERATED + \\ \\if exists("current_compiler") \\ finish \\endif \\let current_compiler = "ghostty" \\ - \\CompilerSet makeprg=ghostty\ +validate-config - \\CompilerSet errorformat=%f:%l:%m + \\CompilerSet makeprg=ghostty\ +validate-config\ --config-file=%:S + \\CompilerSet errorformat=%f:%l:%m,%m \\ ; diff --git a/src/font/face/coretext.zig b/src/font/face/coretext.zig index 6661295f39..3749b48241 100644 --- a/src/font/face/coretext.zig +++ b/src/font/face/coretext.zig @@ -343,13 +343,12 @@ pub const Face = struct { } = if (!self.isColorGlyph(glyph_index)) .{ .color = false, .depth = 1, - .space = try macos.graphics.ColorSpace.createDeviceGray(), - .context_opts = @intFromEnum(macos.graphics.BitmapInfo.alpha_mask) & - @intFromEnum(macos.graphics.ImageAlphaInfo.only), + .space = try macos.graphics.ColorSpace.createNamed(.linearGray), + .context_opts = @intFromEnum(macos.graphics.ImageAlphaInfo.only), } else .{ .color = true, .depth = 4, - .space = try macos.graphics.ColorSpace.createDeviceRGB(), + .space = try macos.graphics.ColorSpace.createNamed(.displayP3), .context_opts = @intFromEnum(macos.graphics.BitmapInfo.byte_order_32_little) | @intFromEnum(macos.graphics.ImageAlphaInfo.premultiplied_first), }; diff --git a/src/font/sprite/Box.zig b/src/font/sprite/Box.zig index ba7caa26a7..2cd3d929b8 100644 --- a/src/font/sprite/Box.zig +++ b/src/font/sprite/Box.zig @@ -184,6 +184,10 @@ const SmoothMosaic = packed struct(u10) { } }; +// Octant range, inclusive +const octant_min = 0x1cd00; +const octant_max = 0x1cde5; + // Utility names for common fractions const one_eighth: f64 = 0.125; const one_quarter: f64 = 0.25; @@ -581,6 +585,8 @@ fn draw(self: Box, alloc: Allocator, canvas: *font.sprite.Canvas, cp: u32) !void 0x1fb00...0x1fb3b => self.draw_sextant(canvas, cp), + octant_min...octant_max => self.draw_octant(canvas, cp), + // '🬼' 0x1fb3c => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( \\... @@ -2484,6 +2490,65 @@ fn draw_sextant(self: Box, canvas: *font.sprite.Canvas, cp: u32) void { if (sex.br) self.rect(canvas, x_halfs[1], y_thirds[1], self.metrics.cell_width, self.metrics.cell_height); } +fn draw_octant(self: Box, canvas: *font.sprite.Canvas, cp: u32) void { + assert(cp >= octant_min and cp <= octant_max); + + // Octant representation. We use the funny numeric string keys + // so its easier to parse the actual name used in the Symbols for + // Legacy Computing spec. + const Octant = packed struct(u8) { + @"1": bool = false, + @"2": bool = false, + @"3": bool = false, + @"4": bool = false, + @"5": bool = false, + @"6": bool = false, + @"7": bool = false, + @"8": bool = false, + }; + + // Parse the octant data. This is all done at comptime so this is + // static data that is embedded in the binary. + const octants_len = octant_max - octant_min + 1; + const octants: [octants_len]Octant = comptime octants: { + @setEvalBranchQuota(10_000); + + var result: [octants_len]Octant = .{.{}} ** octants_len; + var i: usize = 0; + + const data = @embedFile("octants.txt"); + var it = std.mem.splitScalar(u8, data, '\n'); + while (it.next()) |line| { + // Skip comments + if (line.len == 0 or line[0] == '#') continue; + + const current = &result[i]; + i += 1; + + // Octants are in the format "BLOCK OCTANT-1235". The numbers + // at the end are keys into our packed struct. Since we're + // at comptime we can metaprogram it all. + const idx = std.mem.indexOfScalar(u8, line, '-').?; + for (line[idx + 1 ..]) |c| @field(current, &.{c}) = true; + } + + assert(i == octants_len); + break :octants result; + }; + + const x_halfs = self.xHalfs(); + const y_quads = self.yQuads(); + const oct = octants[cp - octant_min]; + if (oct.@"1") self.rect(canvas, 0, 0, x_halfs[0], y_quads[0]); + if (oct.@"2") self.rect(canvas, x_halfs[1], 0, self.metrics.cell_width, y_quads[0]); + if (oct.@"3") self.rect(canvas, 0, y_quads[0], x_halfs[0], y_quads[1]); + if (oct.@"4") self.rect(canvas, x_halfs[1], y_quads[0], self.metrics.cell_width, y_quads[1]); + if (oct.@"5") self.rect(canvas, 0, y_quads[1], x_halfs[0], y_quads[2]); + if (oct.@"6") self.rect(canvas, x_halfs[1], y_quads[1], self.metrics.cell_width, y_quads[2]); + if (oct.@"7") self.rect(canvas, 0, y_quads[2], x_halfs[0], self.metrics.cell_height); + if (oct.@"8") self.rect(canvas, x_halfs[1], y_quads[2], self.metrics.cell_width, self.metrics.cell_height); +} + fn xHalfs(self: Box) [2]u32 { return .{ @as(u32, @intFromFloat(@round(@as(f64, @floatFromInt(self.metrics.cell_width)) / 2))), @@ -2500,6 +2565,21 @@ fn yThirds(self: Box) [2]u32 { }; } +// assume octants might be striped across multiple rows of cells. to maximize +// distance between excess pixellines, we want (1) an arbitrary region (there +// will be a pattern of 1'-3-1'-3-1'-3 no matter what), (2) discontiguous +// regions (0 and 2 or 1 and 3), and (3) an arbitrary three regions (there will +// be a pattern of 3-1-3-1-3-1 no matter what). +fn yQuads(self: Box) [3]u32 { + return switch (@mod(self.metrics.cell_height, 4)) { + 0 => .{ self.metrics.cell_height / 4, 2 * self.metrics.cell_height / 4, 3 * self.metrics.cell_height / 4 }, + 1 => .{ self.metrics.cell_height / 4, 2 * self.metrics.cell_height / 4 + 1, 3 * self.metrics.cell_height / 4 }, + 2 => .{ self.metrics.cell_height / 4 + 1, 2 * self.metrics.cell_height / 4, 3 * self.metrics.cell_height / 4 + 1 }, + 3 => .{ self.metrics.cell_height / 4 + 1, 2 * self.metrics.cell_height / 4 + 1, 3 * self.metrics.cell_height / 4 }, + else => unreachable, + }; +} + fn draw_smooth_mosaic( self: Box, canvas: *font.sprite.Canvas, @@ -3064,7 +3144,7 @@ fn testRenderAll(self: Box, alloc: Allocator, atlas: *font.Atlas) !void { ); } - // Symbols for Legacy Computing Supplement. + // Symbols for Legacy Computing Supplement: Quadrants // 𜰡 𜰢 𜰣 𜰤 𜰥 𜰦 𜰧 𜰨 𜰩 𜰪 𜰫 𜰬 𜰭 𜰮 𜰯 cp = 0x1cc21; while (cp <= 0x1cc2f) : (cp += 1) { @@ -3077,6 +3157,19 @@ fn testRenderAll(self: Box, alloc: Allocator, atlas: *font.Atlas) !void { else => {}, } } + + // Symbols for Legacy Computing Supplement: Octants + cp = 0x1CD00; + while (cp <= 0x1CDE5) : (cp += 1) { + switch (cp) { + 0x1CD00...0x1CDE5 => _ = try self.renderGlyph( + alloc, + atlas, + cp, + ), + else => {}, + } + } } test "render all sprites" { diff --git a/src/font/sprite/Face.zig b/src/font/sprite/Face.zig index cebf44429b..f15423ada0 100644 --- a/src/font/sprite/Face.zig +++ b/src/font/sprite/Face.zig @@ -236,6 +236,8 @@ const Kind = enum { // (Geometric Shapes) // 🯠 🯡 🯢 🯣 🯤 🯥 🯦 🯧 🯨 🯩 🯪 🯫 🯬 🯭 🯮 🯯 0x1FBCE...0x1FBEF, + // (Octants) + 0x1CD00...0x1CDE5, => .box, // Branch drawing character set, used for drawing git-like diff --git a/src/font/sprite/octants.txt b/src/font/sprite/octants.txt new file mode 100644 index 0000000000..db79aa2c63 --- /dev/null +++ b/src/font/sprite/octants.txt @@ -0,0 +1,234 @@ +# This is the list of all the octants for the Symbols for Legacy +# Computing block. It is used at comptime to generate the lookup +# table for drawing them since we weren't able to discern a +# mathematical pattern for them. +BLOCK OCTANT-3 +BLOCK OCTANT-23 +BLOCK OCTANT-123 +BLOCK OCTANT-4 +BLOCK OCTANT-14 +BLOCK OCTANT-124 +BLOCK OCTANT-34 +BLOCK OCTANT-134 +BLOCK OCTANT-234 +BLOCK OCTANT-5 +BLOCK OCTANT-15 +BLOCK OCTANT-25 +BLOCK OCTANT-125 +BLOCK OCTANT-135 +BLOCK OCTANT-235 +BLOCK OCTANT-1235 +BLOCK OCTANT-45 +BLOCK OCTANT-145 +BLOCK OCTANT-245 +BLOCK OCTANT-1245 +BLOCK OCTANT-345 +BLOCK OCTANT-1345 +BLOCK OCTANT-2345 +BLOCK OCTANT-12345 +BLOCK OCTANT-6 +BLOCK OCTANT-16 +BLOCK OCTANT-26 +BLOCK OCTANT-126 +BLOCK OCTANT-36 +BLOCK OCTANT-136 +BLOCK OCTANT-236 +BLOCK OCTANT-1236 +BLOCK OCTANT-146 +BLOCK OCTANT-246 +BLOCK OCTANT-1246 +BLOCK OCTANT-346 +BLOCK OCTANT-1346 +BLOCK OCTANT-2346 +BLOCK OCTANT-12346 +BLOCK OCTANT-56 +BLOCK OCTANT-156 +BLOCK OCTANT-256 +BLOCK OCTANT-1256 +BLOCK OCTANT-356 +BLOCK OCTANT-1356 +BLOCK OCTANT-2356 +BLOCK OCTANT-12356 +BLOCK OCTANT-456 +BLOCK OCTANT-1456 +BLOCK OCTANT-2456 +BLOCK OCTANT-12456 +BLOCK OCTANT-3456 +BLOCK OCTANT-13456 +BLOCK OCTANT-23456 +BLOCK OCTANT-17 +BLOCK OCTANT-27 +BLOCK OCTANT-127 +BLOCK OCTANT-37 +BLOCK OCTANT-137 +BLOCK OCTANT-237 +BLOCK OCTANT-1237 +BLOCK OCTANT-47 +BLOCK OCTANT-147 +BLOCK OCTANT-247 +BLOCK OCTANT-1247 +BLOCK OCTANT-347 +BLOCK OCTANT-1347 +BLOCK OCTANT-2347 +BLOCK OCTANT-12347 +BLOCK OCTANT-157 +BLOCK OCTANT-257 +BLOCK OCTANT-1257 +BLOCK OCTANT-357 +BLOCK OCTANT-2357 +BLOCK OCTANT-12357 +BLOCK OCTANT-457 +BLOCK OCTANT-1457 +BLOCK OCTANT-12457 +BLOCK OCTANT-3457 +BLOCK OCTANT-13457 +BLOCK OCTANT-23457 +BLOCK OCTANT-67 +BLOCK OCTANT-167 +BLOCK OCTANT-267 +BLOCK OCTANT-1267 +BLOCK OCTANT-367 +BLOCK OCTANT-1367 +BLOCK OCTANT-2367 +BLOCK OCTANT-12367 +BLOCK OCTANT-467 +BLOCK OCTANT-1467 +BLOCK OCTANT-2467 +BLOCK OCTANT-12467 +BLOCK OCTANT-3467 +BLOCK OCTANT-13467 +BLOCK OCTANT-23467 +BLOCK OCTANT-123467 +BLOCK OCTANT-567 +BLOCK OCTANT-1567 +BLOCK OCTANT-2567 +BLOCK OCTANT-12567 +BLOCK OCTANT-3567 +BLOCK OCTANT-13567 +BLOCK OCTANT-23567 +BLOCK OCTANT-123567 +BLOCK OCTANT-4567 +BLOCK OCTANT-14567 +BLOCK OCTANT-24567 +BLOCK OCTANT-124567 +BLOCK OCTANT-34567 +BLOCK OCTANT-134567 +BLOCK OCTANT-234567 +BLOCK OCTANT-1234567 +BLOCK OCTANT-18 +BLOCK OCTANT-28 +BLOCK OCTANT-128 +BLOCK OCTANT-38 +BLOCK OCTANT-138 +BLOCK OCTANT-238 +BLOCK OCTANT-1238 +BLOCK OCTANT-48 +BLOCK OCTANT-148 +BLOCK OCTANT-248 +BLOCK OCTANT-1248 +BLOCK OCTANT-348 +BLOCK OCTANT-1348 +BLOCK OCTANT-2348 +BLOCK OCTANT-12348 +BLOCK OCTANT-58 +BLOCK OCTANT-158 +BLOCK OCTANT-258 +BLOCK OCTANT-1258 +BLOCK OCTANT-358 +BLOCK OCTANT-1358 +BLOCK OCTANT-2358 +BLOCK OCTANT-12358 +BLOCK OCTANT-458 +BLOCK OCTANT-1458 +BLOCK OCTANT-2458 +BLOCK OCTANT-12458 +BLOCK OCTANT-3458 +BLOCK OCTANT-13458 +BLOCK OCTANT-23458 +BLOCK OCTANT-123458 +BLOCK OCTANT-168 +BLOCK OCTANT-268 +BLOCK OCTANT-1268 +BLOCK OCTANT-368 +BLOCK OCTANT-2368 +BLOCK OCTANT-12368 +BLOCK OCTANT-468 +BLOCK OCTANT-1468 +BLOCK OCTANT-12468 +BLOCK OCTANT-3468 +BLOCK OCTANT-13468 +BLOCK OCTANT-23468 +BLOCK OCTANT-568 +BLOCK OCTANT-1568 +BLOCK OCTANT-2568 +BLOCK OCTANT-12568 +BLOCK OCTANT-3568 +BLOCK OCTANT-13568 +BLOCK OCTANT-23568 +BLOCK OCTANT-123568 +BLOCK OCTANT-4568 +BLOCK OCTANT-14568 +BLOCK OCTANT-24568 +BLOCK OCTANT-124568 +BLOCK OCTANT-34568 +BLOCK OCTANT-134568 +BLOCK OCTANT-234568 +BLOCK OCTANT-1234568 +BLOCK OCTANT-178 +BLOCK OCTANT-278 +BLOCK OCTANT-1278 +BLOCK OCTANT-378 +BLOCK OCTANT-1378 +BLOCK OCTANT-2378 +BLOCK OCTANT-12378 +BLOCK OCTANT-478 +BLOCK OCTANT-1478 +BLOCK OCTANT-2478 +BLOCK OCTANT-12478 +BLOCK OCTANT-3478 +BLOCK OCTANT-13478 +BLOCK OCTANT-23478 +BLOCK OCTANT-123478 +BLOCK OCTANT-578 +BLOCK OCTANT-1578 +BLOCK OCTANT-2578 +BLOCK OCTANT-12578 +BLOCK OCTANT-3578 +BLOCK OCTANT-13578 +BLOCK OCTANT-23578 +BLOCK OCTANT-123578 +BLOCK OCTANT-4578 +BLOCK OCTANT-14578 +BLOCK OCTANT-24578 +BLOCK OCTANT-124578 +BLOCK OCTANT-34578 +BLOCK OCTANT-134578 +BLOCK OCTANT-234578 +BLOCK OCTANT-1234578 +BLOCK OCTANT-678 +BLOCK OCTANT-1678 +BLOCK OCTANT-2678 +BLOCK OCTANT-12678 +BLOCK OCTANT-3678 +BLOCK OCTANT-13678 +BLOCK OCTANT-23678 +BLOCK OCTANT-123678 +BLOCK OCTANT-4678 +BLOCK OCTANT-14678 +BLOCK OCTANT-24678 +BLOCK OCTANT-124678 +BLOCK OCTANT-34678 +BLOCK OCTANT-134678 +BLOCK OCTANT-234678 +BLOCK OCTANT-1234678 +BLOCK OCTANT-15678 +BLOCK OCTANT-25678 +BLOCK OCTANT-125678 +BLOCK OCTANT-35678 +BLOCK OCTANT-235678 +BLOCK OCTANT-1235678 +BLOCK OCTANT-45678 +BLOCK OCTANT-145678 +BLOCK OCTANT-1245678 +BLOCK OCTANT-1345678 +BLOCK OCTANT-2345678 diff --git a/src/font/sprite/testdata/Box.ppm b/src/font/sprite/testdata/Box.ppm index 36519a1e95..0feb3ebe49 100644 Binary files a/src/font/sprite/testdata/Box.ppm and b/src/font/sprite/testdata/Box.ppm differ diff --git a/src/global.zig b/src/global.zig index c00ce27a4b..d5a7af630e 100644 --- a/src/global.zig +++ b/src/global.zig @@ -111,6 +111,9 @@ pub const GlobalState = struct { } } + // Setup our signal handlers before logging + initSignals(); + // Output some debug information right away std.log.info("ghostty version={s}", .{build_config.version_string}); std.log.info("ghostty build optimize={s}", .{build_config.mode_string}); @@ -175,6 +178,28 @@ pub const GlobalState = struct { _ = value.deinit(); } } + + fn initSignals() void { + // Only posix systems. + if (comptime builtin.os.tag == .windows) return; + + const p = std.posix; + + var sa: p.Sigaction = .{ + .handler = .{ .handler = p.SIG.IGN }, + .mask = p.empty_sigset, + .flags = 0, + }; + + // We ignore SIGPIPE because it is a common signal we may get + // due to how we implement termio. When a terminal is closed we + // often write to a broken pipe to exit the read thread. This should + // be fixed one day but for now this helps make this a bit more + // robust. + p.sigaction(p.SIG.PIPE, &sa, null) catch |err| { + std.log.warn("failed to ignore SIGPIPE err={}", .{err}); + }; + } }; /// Maintains the Unix resource limits that we set for our process. This diff --git a/src/input/Binding.zig b/src/input/Binding.zig index 64e07e85e1..f919672935 100644 --- a/src/input/Binding.zig +++ b/src/input/Binding.zig @@ -236,9 +236,9 @@ pub const Action = union(enum) { /// Send an `ESC` sequence. esc: []const u8, - // Send the given text. Uses Zig string literal syntax. This is currently - // not validated. If the text is invalid (i.e. contains an invalid escape - // sequence), the error will currently only show up in logs. + /// Send the given text. Uses Zig string literal syntax. This is currently + /// not validated. If the text is invalid (i.e. contains an invalid escape + /// sequence), the error will currently only show up in logs. text: []const u8, /// Send data to the pty depending on whether cursor key mode is enabled @@ -259,6 +259,10 @@ pub const Action = union(enum) { paste_from_clipboard: void, paste_from_selection: void, + /// Copy the URL under the cursor to the clipboard. If there is no + /// URL under the cursor, this does nothing. + copy_url_to_clipboard: void, + /// Increase/decrease the font size by a certain amount. increase_font_size: f32, decrease_font_size: f32, @@ -280,8 +284,15 @@ pub const Action = union(enum) { scroll_page_fractional: f32, scroll_page_lines: i16, - /// Adjust an existing selection in a given direction. This action - /// does nothing if there is no active selection. + /// Adjust the current selection in a given direction. Does nothing if no + /// selection exists. + /// + /// Arguments: + /// - left, right, up, down, page_up, page_down, home, end, + /// beginning_of_line, end_of_line + /// + /// Example: Extend selection to the right + /// keybind = shift+right=adjust_selection:right adjust_selection: AdjustSelection, /// Jump the viewport forward or back by prompt. Positive number is the @@ -337,8 +348,17 @@ pub const Action = union(enum) { /// This only works with libadwaita enabled currently. toggle_tab_overview: void, - /// Create a new split in the given direction. The new split will appear in - /// the direction given. For example `new_split:up`. Valid values are left, right, up, down and auto. + /// Change the title of the current focused surface via a prompt. + /// This only works on macOS currently. + prompt_surface_title: void, + + /// Create a new split in the given direction. + /// + /// Arguments: + /// - right, down, left, up, auto (splits along the larger direction) + /// + /// Example: Create split on the right + /// keybind = cmd+shift+d=new_split:right new_split: SplitDirection, /// Focus on a split in a given direction. For example `goto_split:up`. @@ -348,15 +368,26 @@ pub const Action = union(enum) { /// zoom/unzoom the current split. toggle_split_zoom: void, - /// Resize the current split by moving the split divider in the given - /// direction. For example `resize_split:left,10`. The valid directions are up, down, left and right. + /// Resize the current split in a given direction. + /// + /// Arguments: + /// - up, down, left, right + /// - the number of pixels to resize the split by + /// + /// Example: Move divider up 10 pixels + /// keybind = cmd+shift+up=resize_split:up,10 resize_split: SplitResizeParameter, /// Equalize all splits in the current window equalize_splits: void, - /// Show, hide, or toggle the terminal inspector for the currently focused - /// terminal. + /// Control the terminal inspector visibility. + /// + /// Arguments: + /// - toggle, show, hide + /// + /// Example: Toggle inspector visibility + /// keybind = cmd+i=inspector:toggle inspector: InspectorMode, /// Open the configuration file in the default OS editor. If your default OS @@ -375,6 +406,10 @@ pub const Action = union(enum) { /// configured. close_surface: void, + /// Close the current tab, regardless of how many splits there may be. + /// This will trigger close confirmation as configured. + close_tab: void, + /// Close the window, regardless of how many tabs or splits there may be. /// This will trigger close confirmation as configured. close_window: void, @@ -383,6 +418,9 @@ pub const Action = union(enum) { /// This only works for macOS currently. close_all_windows: void, + /// Toggle maximized window state. This only works on Linux. + toggle_maximize: void, + /// Toggle fullscreen mode of window. toggle_fullscreen: void, @@ -408,7 +446,7 @@ pub const Action = union(enum) { /// is preserved between appearances, so you can always press the keybinding /// to bring it back up. /// - /// To enable the quick terminally globally so that Ghostty doesn't + /// To enable the quick terminal globally so that Ghostty doesn't /// have to be focused, prefix your keybind with `global`. Example: /// /// ```ini @@ -433,10 +471,12 @@ pub const Action = union(enum) { toggle_quick_terminal: void, /// Show/hide all windows. If all windows become shown, we also ensure - /// Ghostty is focused. + /// Ghostty becomes focused. When hiding all windows, focus is yielded + /// to the next application as determined by the OS. + /// + /// Note: When the focused surface is fullscreen, this method does nothing. /// - /// This currently only works on macOS. When hiding all windows, we do - /// not yield focus to the previous application. + /// This currently only works on macOS. toggle_visibility: void, /// Quit ghostty. @@ -505,7 +545,6 @@ pub const Action = union(enum) { pub const SplitFocusDirection = enum { previous, next, - up, left, down, @@ -707,11 +746,13 @@ pub const Action = union(enum) { .cursor_key, .reset, .copy_to_clipboard, + .copy_url_to_clipboard, .paste_from_clipboard, .paste_from_selection, .increase_font_size, .decrease_font_size, .reset_font_size, + .prompt_surface_title, .clear_screen, .select_all, .scroll_to_top, @@ -726,7 +767,9 @@ pub const Action = union(enum) { .write_screen_file, .write_selection_file, .close_surface, + .close_tab, .close_window, + .toggle_maximize, .toggle_fullscreen, .toggle_window_decorations, .toggle_secure_input, @@ -1194,6 +1237,13 @@ pub const Set = struct { /// This is a conscious decision since the primary use case of the reverse /// map is to support GUI toolkit keyboard accelerators and no mainstream /// GUI toolkit supports sequences. + /// + /// Performable triggers are also not present in the reverse map. This + /// is so that GUI toolkits don't register performable triggers as + /// menu shortcuts (the primary use case of the reverse map). GUI toolkits + /// such as GTK handle menu shortcuts too early in the event lifecycle + /// for performable to work so this is a conscious decision to ease the + /// integration with GUI toolkits. reverse: ReverseMap = .{}, /// The entry type for the forward mapping of trigger to action. @@ -1458,6 +1508,11 @@ pub const Set = struct { // unbind should never go into the set, it should be handled prior assert(action != .unbind); + // This is true if we're going to track this entry as + // a reverse mapping. There are certain scenarios we don't. + // See the reverse map docs for more information. + const track_reverse: bool = !flags.performable; + const gop = try self.bindings.getOrPut(alloc, t); if (gop.found_existing) switch (gop.value_ptr.*) { @@ -1469,7 +1524,7 @@ pub const Set = struct { // If we have an existing binding for this trigger, we have to // update the reverse mapping to remove the old action. - .leaf => { + .leaf => if (track_reverse) { const t_hash = t.hash(); var it = self.reverse.iterator(); while (it.next()) |reverse_entry| it: { @@ -1486,8 +1541,9 @@ pub const Set = struct { .flags = flags, } }; errdefer _ = self.bindings.remove(t); - try self.reverse.put(alloc, action, t); - errdefer _ = self.reverse.remove(action); + + if (track_reverse) try self.reverse.put(alloc, action, t); + errdefer if (track_reverse) self.reverse.remove(action); } /// Get a binding for a given trigger. @@ -2337,6 +2393,39 @@ test "set: maintains reverse mapping" { } } +test "set: performable is not part of reverse mappings" { + const testing = std.testing; + const alloc = testing.allocator; + + var s: Set = .{}; + defer s.deinit(alloc); + + try s.put(alloc, .{ .key = .{ .translated = .a } }, .{ .new_window = {} }); + { + const trigger = s.getTrigger(.{ .new_window = {} }).?; + try testing.expect(trigger.key.translated == .a); + } + + // trigger should be non-performable + try s.putFlags( + alloc, + .{ .key = .{ .translated = .b } }, + .{ .new_window = {} }, + .{ .performable = true }, + ); + { + const trigger = s.getTrigger(.{ .new_window = {} }).?; + try testing.expect(trigger.key.translated == .a); + } + + // removal of performable should do nothing + s.remove(alloc, .{ .key = .{ .translated = .b } }); + { + const trigger = s.getTrigger(.{ .new_window = {} }).?; + try testing.expect(trigger.key.translated == .a); + } +} + test "set: overriding a mapping updates reverse" { const testing = std.testing; const alloc = testing.allocator; diff --git a/src/input/KeymapDarwin.zig b/src/input/KeymapDarwin.zig index 3d81b0f4bf..154f648a68 100644 --- a/src/input/KeymapDarwin.zig +++ b/src/input/KeymapDarwin.zig @@ -50,10 +50,13 @@ pub const State = struct { pub const Translation = struct { /// The translation result. If this is a dead key state, then this will /// be pre-edit text that can be displayed but will ultimately be replaced. - text: []const u8, + text: []const u8 = "", /// Whether the text is still composing, i.e. this is a dead key state. - composing: bool, + composing: bool = false, + + /// The mods that were consumed to produce this translation + mods: Mods = .{}, }; pub fn init() !Keymap { @@ -122,8 +125,18 @@ pub fn translate( out: []u8, state: *State, code: u16, - mods: Mods, + input_mods: Mods, ) !Translation { + // On macOS we strip ctrl because UCKeyTranslate + // converts to the masked values (i.e. ctrl+c becomes 3) + // and we don't want that behavior in Ghostty ever. This makes + // this file not a general-purpose keymap implementation. + const mods: Mods = mods: { + var v = input_mods; + v.ctrl = false; + break :mods v; + }; + // Get the keycode for the space key, using comptime. const code_space: u16 = comptime space: for (codes) |entry| { if (std.mem.eql(u8, entry.code, "Space")) @@ -183,7 +196,11 @@ pub fn translate( // Convert the utf16 to utf8 const len = try std.unicode.utf16leToUtf8(out, char[0..char_count]); - return .{ .text = out[0..len], .composing = composing }; + return .{ + .text = out[0..len], + .composing = composing, + .mods = mods, + }; } /// Map to the modifiers format used by the UCKeyTranslate function. diff --git a/src/input/KeymapNoop.zig b/src/input/KeymapNoop.zig index 414c52954a..b6a9d57b96 100644 --- a/src/input/KeymapNoop.zig +++ b/src/input/KeymapNoop.zig @@ -6,8 +6,9 @@ const Mods = @import("key.zig").Mods; pub const State = struct {}; pub const Translation = struct { - text: []const u8, - composing: bool, + text: []const u8 = "", + composing: bool = false, + mods: Mods = .{}, }; pub fn init() !KeymapNoop { diff --git a/src/input/helpgen_actions.zig b/src/input/helpgen_actions.zig new file mode 100644 index 0000000000..58305455b9 --- /dev/null +++ b/src/input/helpgen_actions.zig @@ -0,0 +1,113 @@ +//! This module is a help generator for keybind actions documentation. +//! It can generate documentation in different formats (plaintext for CLI, +//! markdown for website) while maintaining consistent content. + +const std = @import("std"); +const KeybindAction = @import("Binding.zig").Action; +const help_strings = @import("help_strings"); + +/// Format options for generating keybind actions documentation +pub const Format = enum { + /// Plain text output with indentation + plaintext, + /// Markdown formatted output + markdown, + + fn formatFieldName(self: Format, writer: anytype, field_name: []const u8) !void { + switch (self) { + .plaintext => { + try writer.writeAll(field_name); + try writer.writeAll(":\n"); + }, + .markdown => { + try writer.writeAll("## `"); + try writer.writeAll(field_name); + try writer.writeAll("`\n"); + }, + } + } + + fn formatDocLine(self: Format, writer: anytype, line: []const u8) !void { + switch (self) { + .plaintext => { + try writer.appendSlice(" "); + try writer.appendSlice(line); + try writer.appendSlice("\n"); + }, + .markdown => { + try writer.appendSlice(line); + try writer.appendSlice("\n"); + }, + } + } + + fn header(self: Format) ?[]const u8 { + return switch (self) { + .plaintext => null, + .markdown => + \\--- + \\title: Keybinding Action Reference + \\description: Reference of all Ghostty keybinding actions. + \\editOnGithubLink: https://github.com/ghostty-org/ghostty/edit/main/src/input/Binding.zig + \\--- + \\ + \\This is a reference of all Ghostty keybinding actions. + \\ + \\ + , + }; + } +}; + +/// Generate keybind actions documentation with the specified format +pub fn generate( + writer: anytype, + format: Format, + show_docs: bool, + page_allocator: std.mem.Allocator, +) !void { + if (format.header()) |header| { + try writer.writeAll(header); + } + + var buffer = std.ArrayList(u8).init(page_allocator); + defer buffer.deinit(); + + const fields = @typeInfo(KeybindAction).Union.fields; + inline for (fields) |field| { + if (field.name[0] == '_') continue; + + // Write previously stored doc comment below all related actions + if (show_docs and @hasDecl(help_strings.KeybindAction, field.name)) { + try writer.writeAll(buffer.items); + try writer.writeAll("\n"); + + buffer.clearRetainingCapacity(); + } + + if (show_docs) { + try format.formatFieldName(writer, field.name); + } else { + try writer.writeAll(field.name); + try writer.writeAll("\n"); + } + + if (show_docs and @hasDecl(help_strings.KeybindAction, field.name)) { + var iter = std.mem.splitScalar( + u8, + @field(help_strings.KeybindAction, field.name), + '\n', + ); + while (iter.next()) |s| { + // If it is the last line and empty, then skip it. + if (iter.peek() == null and s.len == 0) continue; + try format.formatDocLine(&buffer, s); + } + } + } + + // Write any remaining buffered documentation + if (buffer.items.len > 0) { + try writer.writeAll(buffer.items); + } +} diff --git a/src/inspector/Inspector.zig b/src/inspector/Inspector.zig index 54d49b0883..1824f5eadc 100644 --- a/src/inspector/Inspector.zig +++ b/src/inspector/Inspector.zig @@ -53,6 +53,22 @@ key_events: inspector.key.EventRing, vt_events: inspector.termio.VTEventRing, vt_stream: inspector.termio.Stream, +/// The currently selected event sequence number for keyboard navigation +selected_event_seq: ?u32 = null, + +/// Flag indicating whether we need to scroll to the selected item +need_scroll_to_selected: bool = false, + +/// Flag indicating whether the selection was made by keyboard +is_keyboard_selection: bool = false, + +/// Enum representing keyboard navigation actions +const KeyAction = enum { + down, + none, + up, +}; + const CellInspect = union(enum) { /// Idle, no cell inspection is requested idle: void, @@ -1014,6 +1030,24 @@ fn renderKeyboardWindow(self: *Inspector) void { } // table } +/// Helper function to check keyboard state and determine navigation action. +fn getKeyAction(self: *Inspector) KeyAction { + _ = self; + const keys = .{ + .{ .key = cimgui.c.ImGuiKey_J, .action = KeyAction.down }, + .{ .key = cimgui.c.ImGuiKey_DownArrow, .action = KeyAction.down }, + .{ .key = cimgui.c.ImGuiKey_K, .action = KeyAction.up }, + .{ .key = cimgui.c.ImGuiKey_UpArrow, .action = KeyAction.up }, + }; + + inline for (keys) |k| { + if (cimgui.c.igIsKeyPressed_Bool(k.key, false)) { + return k.action; + } + } + return .none; +} + fn renderTermioWindow(self: *Inspector) void { // Start our window. If we're collapsed we do nothing. defer cimgui.c.igEnd(); @@ -1090,6 +1124,60 @@ fn renderTermioWindow(self: *Inspector) void { 0, ); + // Handle keyboard navigation when window is focused + if (cimgui.c.igIsWindowFocused(cimgui.c.ImGuiFocusedFlags_RootAndChildWindows)) { + const key_pressed = self.getKeyAction(); + + switch (key_pressed) { + .none => {}, + .up, .down => { + // If no event is selected, select the first/last event based on direction + if (self.selected_event_seq == null) { + if (!self.vt_events.empty()) { + var it = self.vt_events.iterator(if (key_pressed == .up) .forward else .reverse); + if (it.next()) |ev| { + self.selected_event_seq = @as(u32, @intCast(ev.seq)); + } + } + } else { + // Find next/previous event based on current selection + var it = self.vt_events.iterator(.reverse); + switch (key_pressed) { + .down => { + var found = false; + while (it.next()) |ev| { + if (found) { + self.selected_event_seq = @as(u32, @intCast(ev.seq)); + break; + } + if (ev.seq == self.selected_event_seq.?) { + found = true; + } + } + }, + .up => { + var prev_ev: ?*const inspector.termio.VTEvent = null; + while (it.next()) |ev| { + if (ev.seq == self.selected_event_seq.?) { + if (prev_ev) |prev| { + self.selected_event_seq = @as(u32, @intCast(prev.seq)); + break; + } + } + prev_ev = ev; + } + }, + .none => unreachable, + } + } + + // Mark that we need to scroll to the newly selected item + self.need_scroll_to_selected = true; + self.is_keyboard_selection = true; + }, + } + } + var it = self.vt_events.iterator(.reverse); while (it.next()) |ev| { // Need to push an ID so that our selectable is unique. @@ -1098,12 +1186,32 @@ fn renderTermioWindow(self: *Inspector) void { cimgui.c.igTableNextRow(cimgui.c.ImGuiTableRowFlags_None, 0); _ = cimgui.c.igTableNextColumn(); - _ = cimgui.c.igSelectable_BoolPtr( + + // Store the previous selection state to detect changes + const was_selected = ev.imgui_selected; + + // Update selection state based on keyboard navigation + if (self.selected_event_seq) |seq| { + ev.imgui_selected = (@as(u32, @intCast(ev.seq)) == seq); + } + + // Handle selectable widget + if (cimgui.c.igSelectable_BoolPtr( "##select", &ev.imgui_selected, cimgui.c.ImGuiSelectableFlags_SpanAllColumns, .{ .x = 0, .y = 0 }, - ); + )) { + // If selection state changed, update keyboard navigation state + if (ev.imgui_selected != was_selected) { + self.selected_event_seq = if (ev.imgui_selected) + @as(u32, @intCast(ev.seq)) + else + null; + self.is_keyboard_selection = false; + } + } + cimgui.c.igSameLine(0, 0); cimgui.c.igText("%d", ev.seq); _ = cimgui.c.igTableNextColumn(); @@ -1159,6 +1267,12 @@ fn renderTermioWindow(self: *Inspector) void { cimgui.c.igText("%s", entry.value_ptr.ptr); } } + + // If this is the selected event and scrolling is needed, scroll to it + if (self.need_scroll_to_selected and self.is_keyboard_selection) { + cimgui.c.igSetScrollHereY(0.5); + self.need_scroll_to_selected = false; + } } } } // table diff --git a/src/main.zig b/src/main.zig index ecf38fbb30..121a3b7d20 100644 --- a/src/main.zig +++ b/src/main.zig @@ -9,6 +9,7 @@ const entrypoint = switch (build_config.exe_entrypoint) { .mdgen_ghostty_5 => @import("build/mdgen/main_ghostty_5.zig"), .webgen_config => @import("build/webgen/main_config.zig"), .webgen_actions => @import("build/webgen/main_actions.zig"), + .webgen_commands => @import("build/webgen/main_commands.zig"), .bench_parser => @import("bench/parser.zig"), .bench_stream => @import("bench/stream.zig"), .bench_codepoint_width => @import("bench/codepoint-width.zig"), diff --git a/src/os/cgroup.zig b/src/os/cgroup.zig index 0a66c59878..5645e337a1 100644 --- a/src/os/cgroup.zig +++ b/src/os/cgroup.zig @@ -77,7 +77,22 @@ pub fn cloneInto(cgroup: []const u8) !posix.pid_t { // Get a file descriptor that refers to the cgroup directory in the cgroup // sysfs to pass to the kernel in clone3. const fd: linux.fd_t = fd: { - const rc = linux.open(path, linux.O{ .PATH = true, .DIRECTORY = true }, 0); + const rc = linux.open( + path, + .{ + // Self-explanatory: we expect to open a directory, and + // we only need the path-level permissions. + .PATH = true, + .DIRECTORY = true, + + // We don't want to leak this fd to the child process + // when we clone below since we're using this fd for + // a cgroup clone. + .CLOEXEC = true, + }, + 0, + ); + switch (posix.errno(rc)) { .SUCCESS => break :fd @as(linux.fd_t, @intCast(rc)), else => |errno| { @@ -87,6 +102,7 @@ pub fn cloneInto(cgroup: []const u8) !posix.pid_t { } }; assert(fd >= 0); + defer _ = linux.close(fd); const args: extern struct { flags: u64, @@ -115,7 +131,8 @@ pub fn cloneInto(cgroup: []const u8) !posix.pid_t { }; const rc = linux.syscall2(linux.SYS.clone3, @intFromPtr(&args), @sizeOf(@TypeOf(args))); - return switch (posix.errno(rc)) { + // do not use posix.errno, when linking libc it will use the libc errno which will not be set when making the syscall directly + return switch (std.os.linux.E.init(rc)) { .SUCCESS => @as(posix.pid_t, @intCast(rc)), else => |errno| err: { log.err("unable to clone: {}", .{errno}); diff --git a/src/os/env.zig b/src/os/env.zig index cf6cc0fe75..1916053b32 100644 --- a/src/os/env.zig +++ b/src/os/env.zig @@ -2,6 +2,17 @@ const std = @import("std"); const builtin = @import("builtin"); const Allocator = std.mem.Allocator; const posix = std.posix; +const isFlatpak = @import("flatpak.zig").isFlatpak; + +pub const Error = Allocator.Error; + +/// Get the environment map. +pub fn getEnvMap(alloc: Allocator) !std.process.EnvMap { + return if (isFlatpak()) + std.process.EnvMap.init(alloc) + else + try std.process.getEnvMap(alloc); +} /// Append a value to an environment variable such as PATH. /// The returned value is always allocated so it must be freed. @@ -9,7 +20,7 @@ pub fn appendEnv( alloc: Allocator, current: []const u8, value: []const u8, -) ![]u8 { +) Error![]u8 { // If there is no prior value, we return it as-is if (current.len == 0) return try alloc.dupe(u8, value); @@ -26,7 +37,7 @@ pub fn appendEnvAlways( alloc: Allocator, current: []const u8, value: []const u8, -) ![]u8 { +) Error![]u8 { return try std.fmt.allocPrint(alloc, "{s}{c}{s}", .{ current, std.fs.path.delimiter, @@ -40,7 +51,7 @@ pub fn prependEnv( alloc: Allocator, current: []const u8, value: []const u8, -) ![]u8 { +) Error![]u8 { // If there is no prior value, we return it as-is if (current.len == 0) return try alloc.dupe(u8, value); @@ -68,7 +79,7 @@ pub const GetEnvResult = struct { /// This will allocate on Windows but not on other platforms. The returned /// value should have deinit called to do the proper cleanup no matter what /// platform you are on. -pub fn getenv(alloc: Allocator, key: []const u8) !?GetEnvResult { +pub fn getenv(alloc: Allocator, key: []const u8) Error!?GetEnvResult { return switch (builtin.os.tag) { // Non-Windows doesn't need to allocate else => if (posix.getenv(key)) |v| .{ .value = v } else null, @@ -78,7 +89,8 @@ pub fn getenv(alloc: Allocator, key: []const u8) !?GetEnvResult { .value = v, } else |err| switch (err) { error.EnvironmentVariableNotFound => null, - else => err, + error.InvalidWtf8 => null, + else => |e| e, }, }; } diff --git a/src/os/flatpak.zig b/src/os/flatpak.zig index faac4bd272..09570554ef 100644 --- a/src/os/flatpak.zig +++ b/src/os/flatpak.zig @@ -265,16 +265,12 @@ pub const FlatpakHostCommand = struct { } // Build our args - const args_ptr = c.g_ptr_array_new(); - { - errdefer _ = c.g_ptr_array_free(args_ptr, 1); - for (self.argv) |arg| { - const argZ = try arena.dupeZ(u8, arg); - c.g_ptr_array_add(args_ptr, argZ.ptr); - } + const args = try arena.alloc(?[*:0]u8, self.argv.len + 1); + for (0.., self.argv) |i, arg| { + const argZ = try arena.dupeZ(u8, arg); + args[i] = argZ.ptr; } - const args = c.g_ptr_array_free(args_ptr, 0); - defer c.g_free(@as(?*anyopaque, @ptrCast(args))); + args[args.len - 1] = null; // Get the cwd in case we don't have ours set. A small optimization // would be to do this only if we need it but this isn't a @@ -286,7 +282,7 @@ pub const FlatpakHostCommand = struct { const params = c.g_variant_new( "(^ay^aay@a{uh}@a{ss}u)", @as(*const anyopaque, if (self.cwd) |*cwd| cwd.ptr else g_cwd), - args, + args.ptr, c.g_variant_builder_end(fd_builder), c.g_variant_builder_end(env_builder), @as(c_int, 0), diff --git a/src/os/main.zig b/src/os/main.zig index e652a7981b..cb93559315 100644 --- a/src/os/main.zig +++ b/src/os/main.zig @@ -21,10 +21,12 @@ pub const passwd = @import("passwd.zig"); pub const xdg = @import("xdg.zig"); pub const windows = @import("windows.zig"); pub const macos = @import("macos.zig"); +pub const shell = @import("shell.zig"); // Functions and types pub const CFReleaseThread = @import("cf_release_thread.zig"); pub const TempDir = @import("TempDir.zig"); +pub const getEnvMap = env.getEnvMap; pub const appendEnv = env.appendEnv; pub const appendEnvAlways = env.appendEnvAlways; pub const prependEnv = env.prependEnv; @@ -48,3 +50,4 @@ pub const open = openpkg.open; pub const OpenType = openpkg.Type; pub const pipe = pipepkg.pipe; pub const resourcesDir = resourcesdir.resourcesDir; +pub const ShellEscapeWriter = shell.ShellEscapeWriter; diff --git a/src/os/pipe.zig b/src/os/pipe.zig index 392f720834..2cb7bd4a39 100644 --- a/src/os/pipe.zig +++ b/src/os/pipe.zig @@ -3,10 +3,11 @@ const builtin = @import("builtin"); const windows = @import("windows.zig"); const posix = std.posix; -/// pipe() that works on Windows and POSIX. +/// pipe() that works on Windows and POSIX. For POSIX systems, this sets +/// CLOEXEC on the file descriptors. pub fn pipe() ![2]posix.fd_t { switch (builtin.os.tag) { - else => return try posix.pipe(), + else => return try posix.pipe2(.{ .CLOEXEC = true }), .windows => { var read: windows.HANDLE = undefined; var write: windows.HANDLE = undefined; diff --git a/src/os/resourcesdir.zig b/src/os/resourcesdir.zig index c0f82dec5d..4ef256c1ac 100644 --- a/src/os/resourcesdir.zig +++ b/src/os/resourcesdir.zig @@ -21,7 +21,11 @@ pub fn resourcesDir(alloc: std.mem.Allocator) !?[]const u8 { // This is the sentinel value we look for in the path to know // we've found the resources directory. - const sentinel = "terminfo/ghostty.termcap"; + const sentinel = switch (comptime builtin.target.os.tag) { + .windows => "terminfo/ghostty.terminfo", + .macos => "terminfo/78/xterm-ghostty", + else => "terminfo/x/xterm-ghostty", + }; // Get the path to our running binary var exe_buf: [std.fs.max_path_bytes]u8 = undefined; diff --git a/src/os/shell.zig b/src/os/shell.zig new file mode 100644 index 0000000000..23648a82ae --- /dev/null +++ b/src/os/shell.zig @@ -0,0 +1,95 @@ +const std = @import("std"); +const testing = std.testing; + +/// Writer that escapes characters that shells treat specially to reduce the +/// risk of injection attacks or other such weirdness. Specifically excludes +/// linefeeds so that they can be used to delineate lists of file paths. +/// +/// T should be a Zig type that follows the `std.io.Writer` interface. +pub fn ShellEscapeWriter(comptime T: type) type { + return struct { + child_writer: T, + + fn write(self: *ShellEscapeWriter(T), data: []const u8) error{Error}!usize { + var count: usize = 0; + for (data) |byte| { + const buf = switch (byte) { + '\\', + '"', + '\'', + '$', + '`', + '*', + '?', + ' ', + '|', + => &[_]u8{ '\\', byte }, + else => &[_]u8{byte}, + }; + self.child_writer.writeAll(buf) catch return error.Error; + count += 1; + } + return count; + } + + const Writer = std.io.Writer(*ShellEscapeWriter(T), error{Error}, write); + + pub fn writer(self: *ShellEscapeWriter(T)) Writer { + return .{ .context = self }; + } + }; +} + +test "shell escape 1" { + var buf: [128]u8 = undefined; + var fmt = std.io.fixedBufferStream(&buf); + var shell: ShellEscapeWriter(@TypeOf(fmt).Writer) = .{ .child_writer = fmt.writer() }; + const writer = shell.writer(); + try writer.writeAll("abc"); + try testing.expectEqualStrings("abc", fmt.getWritten()); +} + +test "shell escape 2" { + var buf: [128]u8 = undefined; + var fmt = std.io.fixedBufferStream(&buf); + var shell: ShellEscapeWriter(@TypeOf(fmt).Writer) = .{ .child_writer = fmt.writer() }; + const writer = shell.writer(); + try writer.writeAll("a c"); + try testing.expectEqualStrings("a\\ c", fmt.getWritten()); +} + +test "shell escape 3" { + var buf: [128]u8 = undefined; + var fmt = std.io.fixedBufferStream(&buf); + var shell: ShellEscapeWriter(@TypeOf(fmt).Writer) = .{ .child_writer = fmt.writer() }; + const writer = shell.writer(); + try writer.writeAll("a?c"); + try testing.expectEqualStrings("a\\?c", fmt.getWritten()); +} + +test "shell escape 4" { + var buf: [128]u8 = undefined; + var fmt = std.io.fixedBufferStream(&buf); + var shell: ShellEscapeWriter(@TypeOf(fmt).Writer) = .{ .child_writer = fmt.writer() }; + const writer = shell.writer(); + try writer.writeAll("a\\c"); + try testing.expectEqualStrings("a\\\\c", fmt.getWritten()); +} + +test "shell escape 5" { + var buf: [128]u8 = undefined; + var fmt = std.io.fixedBufferStream(&buf); + var shell: ShellEscapeWriter(@TypeOf(fmt).Writer) = .{ .child_writer = fmt.writer() }; + const writer = shell.writer(); + try writer.writeAll("a|c"); + try testing.expectEqualStrings("a\\|c", fmt.getWritten()); +} + +test "shell escape 6" { + var buf: [128]u8 = undefined; + var fmt = std.io.fixedBufferStream(&buf); + var shell: ShellEscapeWriter(@TypeOf(fmt).Writer) = .{ .child_writer = fmt.writer() }; + const writer = shell.writer(); + try writer.writeAll("a\"c"); + try testing.expectEqualStrings("a\\\"c", fmt.getWritten()); +} diff --git a/src/pty.zig b/src/pty.zig index c0d082411c..6f97e190d4 100644 --- a/src/pty.zig +++ b/src/pty.zig @@ -41,12 +41,16 @@ pub const Mode = packed struct { // a termio that doesn't use a pty. This isn't used in any user-facing // artifacts, this is just a stopgap to get compilation to work on iOS. const NullPty = struct { + pub const Error = OpenError || GetModeError || SetSizeError || ChildPreExecError; + pub const Fd = posix.fd_t; master: Fd, slave: Fd, - pub fn open(size: winsize) !Pty { + pub const OpenError = error{}; + + pub fn open(size: winsize) OpenError!Pty { _ = size; return .{ .master = 0, .slave = 0 }; } @@ -55,17 +59,23 @@ const NullPty = struct { _ = self; } - pub fn getMode(self: Pty) error{GetModeFailed}!Mode { + pub const GetModeError = error{GetModeFailed}; + + pub fn getMode(self: Pty) GetModeError!Mode { _ = self; return .{}; } - pub fn setSize(self: *Pty, size: winsize) !void { + pub const SetSizeError = error{}; + + pub fn setSize(self: *Pty, size: winsize) SetSizeError!void { _ = self; _ = size; } - pub fn childPreExec(self: Pty) !void { + pub const ChildPreExecError = error{}; + + pub fn childPreExec(self: Pty) ChildPreExecError!void { _ = self; } }; @@ -74,6 +84,8 @@ const NullPty = struct { /// of Linux syscalls. The caller is responsible for detail-oriented handling /// of the returned file handles. const PosixPty = struct { + pub const Error = OpenError || GetModeError || GetSizeError || SetSizeError || ChildPreExecError; + pub const Fd = posix.fd_t; // https://github.com/ziglang/zig/issues/13277 @@ -94,11 +106,16 @@ const PosixPty = struct { }; /// The file descriptors for the master and slave side of the pty. + /// The slave side is never closed automatically by this struct + /// so the caller is responsible for closing it if things + /// go wrong. master: Fd, slave: Fd, + pub const OpenError = error{OpenptyFailed}; + /// Open a new PTY with the given initial size. - pub fn open(size: winsize) !Pty { + pub fn open(size: winsize) OpenError!Pty { // Need to copy so that it becomes non-const. var sizeCopy = size; @@ -117,6 +134,24 @@ const PosixPty = struct { _ = posix.system.close(slave_fd); } + // Set CLOEXEC on the master fd, only the slave fd should be inherited + // by the child process (shell/command). + cloexec: { + const flags = std.posix.fcntl(master_fd, std.posix.F.GETFD, 0) catch |err| { + log.warn("error getting flags for master fd err={}", .{err}); + break :cloexec; + }; + + _ = std.posix.fcntl( + master_fd, + std.posix.F.SETFD, + flags | std.posix.FD_CLOEXEC, + ) catch |err| { + log.warn("error setting CLOEXEC on master fd err={}", .{err}); + break :cloexec; + }; + } + // Enable UTF-8 mode. I think this is on by default on Linux but it // is NOT on by default on macOS so we ensure that it is always set. var attrs: c.termios = undefined; @@ -126,7 +161,7 @@ const PosixPty = struct { if (c.tcsetattr(master_fd, c.TCSANOW, &attrs) != 0) return error.OpenptyFailed; - return Pty{ + return .{ .master = master_fd, .slave = slave_fd, }; @@ -134,11 +169,12 @@ const PosixPty = struct { pub fn deinit(self: *Pty) void { _ = posix.system.close(self.master); - _ = posix.system.close(self.slave); self.* = undefined; } - pub fn getMode(self: Pty) error{GetModeFailed}!Mode { + pub const GetModeError = error{GetModeFailed}; + + pub fn getMode(self: Pty) GetModeError!Mode { var attrs: c.termios = undefined; if (c.tcgetattr(self.master, &attrs) != 0) return error.GetModeFailed; @@ -149,8 +185,10 @@ const PosixPty = struct { }; } + pub const GetSizeError = error{IoctlFailed}; + /// Return the size of the pty. - pub fn getSize(self: Pty) !winsize { + pub fn getSize(self: Pty) GetSizeError!winsize { var ws: winsize = undefined; if (c.ioctl(self.master, TIOCGWINSZ, @intFromPtr(&ws)) < 0) return error.IoctlFailed; @@ -158,15 +196,19 @@ const PosixPty = struct { return ws; } + pub const SetSizeError = error{IoctlFailed}; + /// Set the size of the pty. - pub fn setSize(self: *Pty, size: winsize) !void { + pub fn setSize(self: *Pty, size: winsize) SetSizeError!void { if (c.ioctl(self.master, TIOCSWINSZ, @intFromPtr(&size)) < 0) return error.IoctlFailed; } + pub const ChildPreExecError = error{ OperationNotSupported, ProcessGroupFailed, SetControllingTerminalFailed }; + /// This should be called prior to exec in the forked child process /// in order to setup the tty properly. - pub fn childPreExec(self: Pty) !void { + pub fn childPreExec(self: Pty) ChildPreExecError!void { // Reset our signals var sa: posix.Sigaction = .{ .handler = .{ .handler = posix.SIG.DFL }, @@ -181,6 +223,7 @@ const PosixPty = struct { try posix.sigaction(posix.SIG.HUP, &sa, null); try posix.sigaction(posix.SIG.ILL, &sa, null); try posix.sigaction(posix.SIG.INT, &sa, null); + try posix.sigaction(posix.SIG.PIPE, &sa, null); try posix.sigaction(posix.SIG.SEGV, &sa, null); try posix.sigaction(posix.SIG.TRAP, &sa, null); try posix.sigaction(posix.SIG.TERM, &sa, null); @@ -201,13 +244,13 @@ const PosixPty = struct { // Can close master/slave pair now posix.close(self.slave); posix.close(self.master); - - // TODO: reset signals } }; /// Windows PTY creation and management. const WindowsPty = struct { + pub const Error = OpenError || GetSizeError || SetSizeError; + pub const Fd = windows.HANDLE; // Process-wide counter for pipe names @@ -220,8 +263,10 @@ const WindowsPty = struct { pseudo_console: windows.exp.HPCON, size: winsize, + pub const OpenError = error{Unexpected}; + /// Open a new PTY with the given initial size. - pub fn open(size: winsize) !Pty { + pub fn open(size: winsize) OpenError!Pty { var pty: Pty = undefined; var pipe_path_buf: [128]u8 = undefined; @@ -330,13 +375,17 @@ const WindowsPty = struct { self.* = undefined; } + pub const GetSizeError = error{}; + /// Return the size of the pty. - pub fn getSize(self: Pty) !winsize { + pub fn getSize(self: Pty) GetSizeError!winsize { return self.size; } + pub const SetSizeError = error{ResizeFailed}; + /// Set the size of the pty. - pub fn setSize(self: *Pty, size: winsize) !void { + pub fn setSize(self: *Pty, size: winsize) SetSizeError!void { const result = windows.exp.kernel32.ResizePseudoConsole( self.pseudo_console, .{ .X = @intCast(size.ws_col), .Y = @intCast(size.ws_row) }, diff --git a/src/renderer/Metal.zig b/src/renderer/Metal.zig index 09dafd1fc7..ca13f87deb 100644 --- a/src/renderer/Metal.zig +++ b/src/renderer/Metal.zig @@ -21,6 +21,7 @@ const renderer = @import("../renderer.zig"); const math = @import("../math.zig"); const Surface = @import("../Surface.zig"); const link = @import("link.zig"); +const graphics = macos.graphics; const fgMode = @import("cell.zig").fgMode; const isCovering = @import("cell.zig").isCovering; const shadertoy = @import("shadertoy.zig"); @@ -105,10 +106,6 @@ default_cursor_color: ?terminal.color.RGB, /// foreground color as the cursor color. cursor_invert: bool, -/// The current frame background color. This is only updated during -/// the updateFrame method. -current_background_color: terminal.color.RGB, - /// The current set of cells to render. This is rebuilt on every frame /// but we keep this around so that we don't reallocate. Each set of /// cells goes into a separate shader. @@ -151,6 +148,9 @@ layer: objc.Object, // CAMetalLayer /// a display link. display_link: ?DisplayLink = null, +/// The `CGColorSpace` that represents our current terminal color space +terminal_colorspace: *graphics.ColorSpace, + /// Custom shader state. This is only set if we have custom shaders. custom_shader_state: ?CustomShaderState = null, @@ -182,15 +182,34 @@ pub const GPUState = struct { /// This buffer is written exactly once so we can use it globally. instance: InstanceBuffer, // MTLBuffer + /// The default storage mode to use for resources created with our device. + /// + /// This is based on whether the device is a discrete GPU or not, since + /// discrete GPUs do not have unified memory and therefore do not support + /// the "shared" storage mode, instead we have to use the "managed" mode. + default_storage_mode: mtl.MTLResourceOptions.StorageMode, + pub fn init() !GPUState { const device = try chooseDevice(); const queue = device.msgSend(objc.Object, objc.sel("newCommandQueue"), .{}); errdefer queue.release(); + // We determine whether our device is a discrete GPU based on these: + // - We're on macOS (iOS, iPadOS, etc. are guaranteed to be integrated). + // - We're not on aarch64 (Apple Silicon, therefore integrated). + // - The device reports that it does not have unified memory. + const is_discrete = + builtin.target.os.tag == .macos and + builtin.target.cpu.arch != .aarch64 and + !device.getProperty(bool, "hasUnifiedMemory"); + + const default_storage_mode: mtl.MTLResourceOptions.StorageMode = + if (is_discrete) .managed else .shared; + var instance = try InstanceBuffer.initFill(device, &.{ 0, 1, 3, // Top-left triangle 1, 2, 3, // Bottom-right triangle - }); + }, .{ .storage_mode = default_storage_mode }); errdefer instance.deinit(); var result: GPUState = .{ @@ -198,11 +217,12 @@ pub const GPUState = struct { .queue = queue, .instance = instance, .frames = undefined, + .default_storage_mode = default_storage_mode, }; // Initialize all of our frame state. for (&result.frames) |*frame| { - frame.* = try FrameState.init(result.device); + frame.* = try FrameState.init(result.device, default_storage_mode); } return result; @@ -288,18 +308,47 @@ pub const FrameState = struct { const CellBgBuffer = mtl_buffer.Buffer(mtl_shaders.CellBg); const CellTextBuffer = mtl_buffer.Buffer(mtl_shaders.CellText); - pub fn init(device: objc.Object) !FrameState { + pub fn init( + device: objc.Object, + /// Storage mode for buffers and textures. + storage_mode: mtl.MTLResourceOptions.StorageMode, + ) !FrameState { // Uniform buffer contains exactly 1 uniform struct. The // uniform data will be undefined so this must be set before // a frame is drawn. - var uniforms = try UniformBuffer.init(device, 1); + var uniforms = try UniformBuffer.init( + device, + 1, + .{ + // Indicate that the CPU writes to this resource but never reads it. + .cpu_cache_mode = .write_combined, + .storage_mode = storage_mode, + }, + ); errdefer uniforms.deinit(); // Create the buffers for our vertex data. The preallocation size // is likely too small but our first frame update will resize it. - var cells = try CellTextBuffer.init(device, 10 * 10); + var cells = try CellTextBuffer.init( + device, + 10 * 10, + .{ + // Indicate that the CPU writes to this resource but never reads it. + .cpu_cache_mode = .write_combined, + .storage_mode = storage_mode, + }, + ); errdefer cells.deinit(); - var cells_bg = try CellBgBuffer.init(device, 10 * 10); + var cells_bg = try CellBgBuffer.init( + device, + 10 * 10, + .{ + // Indicate that the CPU writes to this resource but never reads it. + .cpu_cache_mode = .write_combined, + .storage_mode = storage_mode, + }, + ); + errdefer cells_bg.deinit(); // Initialize our textures for our font atlas. @@ -307,13 +356,13 @@ pub const FrameState = struct { .data = undefined, .size = 8, .format = .grayscale, - }); + }, storage_mode); errdefer grayscale.release(); const color = try initAtlasTexture(device, &.{ .data = undefined, .size = 8, .format = .rgba, - }); + }, storage_mode); errdefer color.release(); return .{ @@ -390,6 +439,8 @@ pub const DerivedConfig = struct { custom_shaders: configpkg.RepeatablePath, links: link.Set, vsync: bool, + colorspace: configpkg.Config.WindowColorspace, + blending: configpkg.Config.AlphaBlending, pub fn init( alloc_gpa: Allocator, @@ -460,7 +511,8 @@ pub const DerivedConfig = struct { .custom_shaders = custom_shaders, .links = links, .vsync = config.@"window-vsync", - + .colorspace = config.@"window-colorspace", + .blending = config.@"alpha-blending", .arena = arena, }; } @@ -490,10 +542,6 @@ pub fn surfaceInit(surface: *apprt.Surface) !void { } pub fn init(alloc: Allocator, options: renderer.Options) !Metal { - var arena = ArenaAllocator.init(alloc); - defer arena.deinit(); - const arena_alloc = arena.allocator(); - const ViewInfo = struct { view: objc.Object, scaleFactor: f64, @@ -512,7 +560,7 @@ pub fn init(alloc: Allocator, options: renderer.Options) !Metal { nswindow.getProperty(?*anyopaque, "contentView").?, ); const scaleFactor = nswindow.getProperty( - macos.graphics.c.CGFloat, + graphics.c.CGFloat, "backingScaleFactor", ); @@ -553,6 +601,40 @@ pub fn init(alloc: Allocator, options: renderer.Options) !Metal { layer.setProperty("opaque", options.config.background_opacity >= 1); layer.setProperty("displaySyncEnabled", options.config.vsync); + // Set our layer's pixel format appropriately. + layer.setProperty( + "pixelFormat", + // Using an `*_srgb` pixel format makes Metal gamma encode + // the pixels written to it *after* blending, which means + // we get linear alpha blending rather than gamma-incorrect + // blending. + if (options.config.blending.isLinear()) + @intFromEnum(mtl.MTLPixelFormat.bgra8unorm_srgb) + else + @intFromEnum(mtl.MTLPixelFormat.bgra8unorm), + ); + + // Set our layer's color space to Display P3. + // This allows us to have "Apple-style" alpha blending, + // since it seems to be the case that Apple apps like + // Terminal and TextEdit render text in the display's + // color space using converted colors, which reduces, + // but does not fully eliminate blending artifacts. + const colorspace = try graphics.ColorSpace.createNamed(.displayP3); + defer colorspace.release(); + layer.setProperty("colorspace", colorspace); + + // Create a colorspace the represents our terminal colors + // this will allow us to create e.g. `CGColor`s for things + // like the current background color. + const terminal_colorspace = try graphics.ColorSpace.createNamed( + switch (options.config.colorspace) { + .@"display-p3" => .displayP3, + .srgb => .sRGB, + }, + ); + errdefer terminal_colorspace.release(); + // Make our view layer-backed with our Metal layer. On iOS views are // always layer backed so we don't need to do this. But on iOS the // caller MUST be sure to set the layerClass to CAMetalLayer. @@ -578,54 +660,6 @@ pub fn init(alloc: Allocator, options: renderer.Options) !Metal { }); errdefer font_shaper.deinit(); - // Load our custom shaders - const custom_shaders: []const [:0]const u8 = shadertoy.loadFromFiles( - arena_alloc, - options.config.custom_shaders, - .msl, - ) catch |err| err: { - log.warn("error loading custom shaders err={}", .{err}); - break :err &.{}; - }; - - // If we have custom shaders then setup our state - var custom_shader_state: ?CustomShaderState = state: { - if (custom_shaders.len == 0) break :state null; - - // Build our sampler for our texture - var sampler = try mtl_sampler.Sampler.init(gpu_state.device); - errdefer sampler.deinit(); - - break :state .{ - // Resolution and screen textures will be fixed up by first - // call to setScreenSize. Draw calls will bail out early if - // the screen size hasn't been set yet, so it won't error. - .front_texture = undefined, - .back_texture = undefined, - .sampler = sampler, - .uniforms = .{ - .resolution = .{ 0, 0, 1 }, - .time = 1, - .time_delta = 1, - .frame_rate = 1, - .frame = 1, - .channel_time = [1][4]f32{.{ 0, 0, 0, 0 }} ** 4, - .channel_resolution = [1][4]f32{.{ 0, 0, 0, 0 }} ** 4, - .mouse = .{ 0, 0, 0, 0 }, - .date = .{ 0, 0, 0, 0 }, - .sample_rate = 1, - }, - - .first_frame_time = try std.time.Instant.now(), - .last_frame_time = try std.time.Instant.now(), - }; - }; - errdefer if (custom_shader_state) |*state| state.deinit(); - - // Initialize our shaders - var shaders = try Shaders.init(alloc, gpu_state.device, custom_shaders); - errdefer shaders.deinit(alloc); - // Initialize all the data that requires a critical font section. const font_critical: struct { metrics: font.Metrics, @@ -661,7 +695,6 @@ pub fn init(alloc: Allocator, options: renderer.Options) !Metal { .cursor_color = null, .default_cursor_color = options.config.cursor_color, .cursor_invert = options.config.cursor_invert, - .current_background_color = options.config.background, // Render state .cells = .{}, @@ -674,7 +707,16 @@ pub fn init(alloc: Allocator, options: renderer.Options) !Metal { .min_contrast = options.config.min_contrast, .cursor_pos = .{ std.math.maxInt(u16), std.math.maxInt(u16) }, .cursor_color = undefined, + .bg_color = .{ + options.config.background.r, + options.config.background.g, + options.config.background.b, + @intFromFloat(@round(options.config.background_opacity * 255.0)), + }, .cursor_wide = false, + .use_display_p3 = options.config.colorspace == .@"display-p3", + .use_linear_blending = options.config.blending.isLinear(), + .use_linear_correction = options.config.blending == .@"linear-corrected", }, // Fonts @@ -682,16 +724,19 @@ pub fn init(alloc: Allocator, options: renderer.Options) !Metal { .font_shaper = font_shaper, .font_shaper_cache = font.ShaperCache.init(), - // Shaders - .shaders = shaders, + // Shaders (initialized below) + .shaders = undefined, // Metal stuff .layer = layer, .display_link = display_link, - .custom_shader_state = custom_shader_state, + .terminal_colorspace = terminal_colorspace, + .custom_shader_state = null, .gpu_state = gpu_state, }; + try result.initShaders(); + // Do an initialize screen size setup to ensure our undefined values // above are initialized. try result.setScreenSize(result.size); @@ -709,6 +754,8 @@ pub fn deinit(self: *Metal) void { } } + self.terminal_colorspace.release(); + self.cells.deinit(self.alloc); self.font_shaper.deinit(); @@ -723,11 +770,82 @@ pub fn deinit(self: *Metal) void { } self.image_placements.deinit(self.alloc); + self.deinitShaders(); + + self.* = undefined; +} + +fn deinitShaders(self: *Metal) void { if (self.custom_shader_state) |*state| state.deinit(); self.shaders.deinit(self.alloc); +} - self.* = undefined; +fn initShaders(self: *Metal) !void { + var arena = ArenaAllocator.init(self.alloc); + defer arena.deinit(); + const arena_alloc = arena.allocator(); + + // Load our custom shaders + const custom_shaders: []const [:0]const u8 = shadertoy.loadFromFiles( + arena_alloc, + self.config.custom_shaders, + .msl, + ) catch |err| err: { + log.warn("error loading custom shaders err={}", .{err}); + break :err &.{}; + }; + + var custom_shader_state: ?CustomShaderState = state: { + if (custom_shaders.len == 0) break :state null; + + // Build our sampler for our texture + var sampler = try mtl_sampler.Sampler.init(self.gpu_state.device); + errdefer sampler.deinit(); + + break :state .{ + // Resolution and screen textures will be fixed up by first + // call to setScreenSize. Draw calls will bail out early if + // the screen size hasn't been set yet, so it won't error. + .front_texture = undefined, + .back_texture = undefined, + .sampler = sampler, + .uniforms = .{ + .resolution = .{ 0, 0, 1 }, + .time = 1, + .time_delta = 1, + .frame_rate = 1, + .frame = 1, + .channel_time = [1][4]f32{.{ 0, 0, 0, 0 }} ** 4, + .channel_resolution = [1][4]f32{.{ 0, 0, 0, 0 }} ** 4, + .mouse = .{ 0, 0, 0, 0 }, + .date = .{ 0, 0, 0, 0 }, + .sample_rate = 1, + }, + + .first_frame_time = try std.time.Instant.now(), + .last_frame_time = try std.time.Instant.now(), + }; + }; + errdefer if (custom_shader_state) |*state| state.deinit(); + + var shaders = try Shaders.init( + self.alloc, + self.gpu_state.device, + custom_shaders, + // Using an `*_srgb` pixel format makes Metal gamma encode + // the pixels written to it *after* blending, which means + // we get linear alpha blending rather than gamma-incorrect + // blending. + if (self.config.blending.isLinear()) + mtl.MTLPixelFormat.bgra8unorm_srgb + else + mtl.MTLPixelFormat.bgra8unorm, + ); + errdefer shaders.deinit(self.alloc); + + self.shaders = shaders; + self.custom_shader_state = custom_shader_state; } /// This is called just prior to spinning up the renderer thread for @@ -977,19 +1095,6 @@ pub fn updateFrame( } } - // If our terminal screen size doesn't match our expected renderer - // size then we skip a frame. This can happen if the terminal state - // is resized between when the renderer mailbox is drained and when - // the state mutex is acquired inside this function. - // - // For some reason this doesn't seem to cause any significant issues - // with flickering while resizing. '\_('-')_/' - if (self.cells.size.rows != state.terminal.rows or - self.cells.size.columns != state.terminal.cols) - { - return; - } - // Get the viewport pin so that we can compare it to the current. const viewport_pin = state.terminal.screen.pages.pin(.{ .viewport = .{} }).?; @@ -1111,7 +1216,38 @@ pub fn updateFrame( self.cells_viewport = critical.viewport_pin; // Update our background color - self.current_background_color = critical.bg; + self.uniforms.bg_color = .{ + critical.bg.r, + critical.bg.g, + critical.bg.b, + @intFromFloat(@round(self.config.background_opacity * 255.0)), + }; + + // Update the background color on our layer + // + // TODO: Is this expensive? Should we be checking if our + // bg color has changed first before doing this work? + { + const color = graphics.c.CGColorCreate( + @ptrCast(self.terminal_colorspace), + &[4]f64{ + @as(f64, @floatFromInt(critical.bg.r)) / 255.0, + @as(f64, @floatFromInt(critical.bg.g)) / 255.0, + @as(f64, @floatFromInt(critical.bg.b)) / 255.0, + self.config.background_opacity, + }, + ); + defer graphics.c.CGColorRelease(color); + + // We use a CATransaction so that Core Animation knows that we + // updated the background color property. Otherwise it behaves + // weird, not updating the color until we resize. + const CATransaction = objc.getClass("CATransaction").?; + CATransaction.msgSend(void, "begin", .{}); + defer CATransaction.msgSend(void, "commit", .{}); + + self.layer.setProperty("backgroundColor", color); + } // Go through our images and see if we need to setup any textures. { @@ -1128,7 +1264,11 @@ pub fn updateFrame( .replace_gray_alpha, .replace_rgb, .replace_rgba, - => try kv.value_ptr.image.upload(self.alloc, self.gpu_state.device), + => try kv.value_ptr.image.upload( + self.alloc, + self.gpu_state.device, + self.gpu_state.default_storage_mode, + ), .unload_pending, .unload_replace, @@ -1196,7 +1336,12 @@ pub fn drawFrame(self: *Metal, surface: *apprt.Surface) !void { self.font_grid.lock.lockShared(); defer self.font_grid.lock.unlockShared(); frame.grayscale_modified = self.font_grid.atlas_grayscale.modified.load(.monotonic); - try syncAtlasTexture(self.gpu_state.device, &self.font_grid.atlas_grayscale, &frame.grayscale); + try syncAtlasTexture( + self.gpu_state.device, + &self.font_grid.atlas_grayscale, + &frame.grayscale, + self.gpu_state.default_storage_mode, + ); } texture: { const modified = self.font_grid.atlas_color.modified.load(.monotonic); @@ -1204,7 +1349,12 @@ pub fn drawFrame(self: *Metal, surface: *apprt.Surface) !void { self.font_grid.lock.lockShared(); defer self.font_grid.lock.unlockShared(); frame.color_modified = self.font_grid.atlas_color.modified.load(.monotonic); - try syncAtlasTexture(self.gpu_state.device, &self.font_grid.atlas_color, &frame.color); + try syncAtlasTexture( + self.gpu_state.device, + &self.font_grid.atlas_color, + &frame.color, + self.gpu_state.default_storage_mode, + ); } // Command buffer (MTLCommandBuffer) @@ -1233,10 +1383,10 @@ pub fn drawFrame(self: *Metal, surface: *apprt.Surface) !void { attachment.setProperty("storeAction", @intFromEnum(mtl.MTLStoreAction.store)); attachment.setProperty("texture", screen_texture.value); attachment.setProperty("clearColor", mtl.MTLClearColor{ - .red = @as(f32, @floatFromInt(self.current_background_color.r)) / 255 * self.config.background_opacity, - .green = @as(f32, @floatFromInt(self.current_background_color.g)) / 255 * self.config.background_opacity, - .blue = @as(f32, @floatFromInt(self.current_background_color.b)) / 255 * self.config.background_opacity, - .alpha = self.config.background_opacity, + .red = 0.0, + .green = 0.0, + .blue = 0.0, + .alpha = 0.0, }); } @@ -1252,19 +1402,19 @@ pub fn drawFrame(self: *Metal, surface: *apprt.Surface) !void { defer encoder.msgSend(void, objc.sel("endEncoding"), .{}); // Draw background images first - try self.drawImagePlacements(encoder, self.image_placements.items[0..self.image_bg_end]); + try self.drawImagePlacements(encoder, frame, self.image_placements.items[0..self.image_bg_end]); // Then draw background cells try self.drawCellBgs(encoder, frame); // Then draw images under text - try self.drawImagePlacements(encoder, self.image_placements.items[self.image_bg_end..self.image_text_end]); + try self.drawImagePlacements(encoder, frame, self.image_placements.items[self.image_bg_end..self.image_text_end]); // Then draw fg cells try self.drawCellFgs(encoder, frame, fg_count); // Then draw remaining images - try self.drawImagePlacements(encoder, self.image_placements.items[self.image_text_end..]); + try self.drawImagePlacements(encoder, frame, self.image_placements.items[self.image_text_end..]); } // If we have custom shaders, then we render them. @@ -1457,6 +1607,7 @@ fn drawPostShader( fn drawImagePlacements( self: *Metal, encoder: objc.Object, + frame: *const FrameState, placements: []const mtl_image.Placement, ) !void { if (placements.len == 0) return; @@ -1468,15 +1619,16 @@ fn drawImagePlacements( .{self.shaders.image_pipeline.value}, ); - // Set our uniform, which is the only shared buffer + // Set our uniforms encoder.msgSend( void, - objc.sel("setVertexBytes:length:atIndex:"), - .{ - @as(*const anyopaque, @ptrCast(&self.uniforms)), - @as(c_ulong, @sizeOf(@TypeOf(self.uniforms))), - @as(c_ulong, 1), - }, + objc.sel("setVertexBuffer:offset:atIndex:"), + .{ frame.uniforms.buffer.value, @as(c_ulong, 0), @as(c_ulong, 1) }, + ); + encoder.msgSend( + void, + objc.sel("setFragmentBuffer:offset:atIndex:"), + .{ frame.uniforms.buffer.value, @as(c_ulong, 0), @as(c_ulong, 1) }, ); for (placements) |placement| { @@ -1529,7 +1681,11 @@ fn drawImagePlacement( @as(f32, @floatFromInt(p.width)), @as(f32, @floatFromInt(p.height)), }, - }}); + }}, .{ + // Indicate that the CPU writes to this resource but never reads it. + .cpu_cache_mode = .write_combined, + .storage_mode = self.gpu_state.default_storage_mode, + }); defer buf.deinit(); // Set our buffer @@ -1588,6 +1744,11 @@ fn drawCellBgs( ); // Set our buffers + encoder.msgSend( + void, + objc.sel("setVertexBuffer:offset:atIndex:"), + .{ frame.uniforms.buffer.value, @as(c_ulong, 0), @as(c_ulong, 1) }, + ); encoder.msgSend( void, objc.sel("setFragmentBuffer:offset:atIndex:"), @@ -1647,18 +1808,17 @@ fn drawCellFgs( encoder.msgSend( void, objc.sel("setFragmentTexture:atIndex:"), - .{ - frame.grayscale.value, - @as(c_ulong, 0), - }, + .{ frame.grayscale.value, @as(c_ulong, 0) }, ); encoder.msgSend( void, objc.sel("setFragmentTexture:atIndex:"), - .{ - frame.color.value, - @as(c_ulong, 1), - }, + .{ frame.color.value, @as(c_ulong, 1) }, + ); + encoder.msgSend( + void, + objc.sel("setFragmentBuffer:offset:atIndex:"), + .{ frame.uniforms.buffer.value, @as(c_ulong, 0), @as(c_ulong, 2) }, ); encoder.msgSend( @@ -2003,17 +2163,73 @@ pub fn changeConfig(self: *Metal, config: *DerivedConfig) !void { // Set our new minimum contrast self.uniforms.min_contrast = config.min_contrast; + // Set our new color space and blending + self.uniforms.use_display_p3 = config.colorspace == .@"display-p3"; + self.uniforms.use_linear_blending = config.blending.isLinear(); + self.uniforms.use_linear_correction = config.blending == .@"linear-corrected"; + // Set our new colors self.default_background_color = config.background; self.default_foreground_color = config.foreground; self.default_cursor_color = if (!config.cursor_invert) config.cursor_color else null; self.cursor_invert = config.cursor_invert; + // Update our layer's opaqueness and display sync in case they changed. + { + // We use a CATransaction so that Core Animation knows that we + // updated the opaque property. Otherwise it behaves weird, not + // properly going from opaque to transparent unless we resize. + const CATransaction = objc.getClass("CATransaction").?; + CATransaction.msgSend(void, "begin", .{}); + defer CATransaction.msgSend(void, "commit", .{}); + + self.layer.setProperty("opaque", config.background_opacity >= 1); + self.layer.setProperty("displaySyncEnabled", config.vsync); + } + + // Update our terminal colorspace if it changed + if (self.config.colorspace != config.colorspace) { + const terminal_colorspace = try graphics.ColorSpace.createNamed( + switch (config.colorspace) { + .@"display-p3" => .displayP3, + .srgb => .sRGB, + }, + ); + errdefer terminal_colorspace.release(); + self.terminal_colorspace.release(); + self.terminal_colorspace = terminal_colorspace; + } + + const old_blending = self.config.blending; + const old_custom_shaders = self.config.custom_shaders; + self.config.deinit(); self.config = config.*; // Reset our viewport to force a rebuild, in case of a font change. self.cells_viewport = null; + + // We reinitialize our shaders if our + // blending or custom shaders changed. + if (old_blending != config.blending or + !old_custom_shaders.equal(config.custom_shaders)) + { + self.deinitShaders(); + try self.initShaders(); + // We call setScreenSize to reinitialize + // the textures used for custom shaders. + if (self.custom_shader_state != null) { + try self.setScreenSize(self.size); + } + // And we update our layer's pixel format appropriately. + self.layer.setProperty( + "pixelFormat", + if (config.blending.isLinear()) + @intFromEnum(mtl.MTLPixelFormat.bgra8unorm_srgb) + else + @intFromEnum(mtl.MTLPixelFormat.bgra8unorm), + ); + } } /// Resize the screen. @@ -2057,7 +2273,7 @@ pub fn setScreenSize( } // Set the size of the drawable surface to the bounds - self.layer.setProperty("drawableSize", macos.graphics.Size{ + self.layer.setProperty("drawableSize", graphics.Size{ .width = @floatFromInt(size.screen.width), .height = @floatFromInt(size.screen.height), }); @@ -2089,7 +2305,11 @@ pub fn setScreenSize( .min_contrast = old.min_contrast, .cursor_pos = old.cursor_pos, .cursor_color = old.cursor_color, + .bg_color = old.bg_color, .cursor_wide = old.cursor_wide, + .use_display_p3 = old.use_display_p3, + .use_linear_blending = old.use_linear_blending, + .use_linear_correction = old.use_linear_correction, }; // Reset our cell contents if our grid size has changed. @@ -2124,7 +2344,17 @@ pub fn setScreenSize( const id_init = id_alloc.msgSend(objc.Object, objc.sel("init"), .{}); break :init id_init; }; - desc.setProperty("pixelFormat", @intFromEnum(mtl.MTLPixelFormat.bgra8unorm)); + desc.setProperty( + "pixelFormat", + // Using an `*_srgb` pixel format makes Metal gamma encode + // the pixels written to it *after* blending, which means + // we get linear alpha blending rather than gamma-incorrect + // blending. + if (self.config.blending.isLinear()) + @intFromEnum(mtl.MTLPixelFormat.bgra8unorm_srgb) + else + @intFromEnum(mtl.MTLPixelFormat.bgra8unorm), + ); desc.setProperty("width", @as(c_ulong, @intCast(size.screen.width))); desc.setProperty("height", @as(c_ulong, @intCast(size.screen.height))); desc.setProperty( @@ -2154,7 +2384,17 @@ pub fn setScreenSize( const id_init = id_alloc.msgSend(objc.Object, objc.sel("init"), .{}); break :init id_init; }; - desc.setProperty("pixelFormat", @intFromEnum(mtl.MTLPixelFormat.bgra8unorm)); + desc.setProperty( + "pixelFormat", + // Using an `*_srgb` pixel format makes Metal gamma encode + // the pixels written to it *after* blending, which means + // we get linear alpha blending rather than gamma-incorrect + // blending. + if (self.config.blending.isLinear()) + @intFromEnum(mtl.MTLPixelFormat.bgra8unorm_srgb) + else + @intFromEnum(mtl.MTLPixelFormat.bgra8unorm), + ); desc.setProperty("width", @as(c_ulong, @intCast(size.screen.width))); desc.setProperty("height", @as(c_ulong, @intCast(size.screen.height))); desc.setProperty( @@ -2251,12 +2491,22 @@ fn rebuildCells( } } - // Go row-by-row to build the cells. We go row by row because we do - // font shaping by row. In the future, we will also do dirty tracking - // by row. + // We rebuild the cells row-by-row because we + // do font shaping and dirty tracking by row. var row_it = screen.pages.rowIterator(.left_up, .{ .viewport = .{} }, null); - var y: terminal.size.CellCountInt = screen.pages.rows; + // If our cell contents buffer is shorter than the screen viewport, + // we render the rows that fit, starting from the bottom. If instead + // the viewport is shorter than the cell contents buffer, we align + // the top of the viewport with the top of the contents buffer. + var y: terminal.size.CellCountInt = @min( + screen.pages.rows, + self.cells.size.rows, + ); while (row_it.next()) |row| { + // The viewport may have more rows than our cell contents, + // so we need to break from the loop early if we hit y = 0. + if (y == 0) break; + y -= 1; if (!rebuild) { @@ -2315,7 +2565,11 @@ fn rebuildCells( var shaper_cells: ?[]const font.shape.Cell = null; var shaper_cells_i: usize = 0; - const row_cells = row.cells(.all); + const row_cells_all = row.cells(.all); + + // If our viewport is wider than our cell contents buffer, + // we still only process cells up to the width of the buffer. + const row_cells = row_cells_all[0..@min(row_cells_all.len, self.cells.size.columns)]; for (row_cells, 0..) |*cell, x| { // If this cell falls within our preedit range then we @@ -2466,8 +2720,10 @@ fn rebuildCells( // Foreground alpha for this cell. const alpha: u8 = if (style.flags.faint) 175 else 255; - // If the cell has a background color, set it. - if (bg) |rgb| { + // Set the cell's background color. + { + const rgb = bg orelse self.background_color orelse self.default_background_color; + // Determine our background alpha. If we have transparency configured // then this is dynamic depending on some situations. This is all // in an attempt to make transparency look the best for various @@ -2477,23 +2733,19 @@ fn rebuildCells( if (self.config.background_opacity >= 1) break :bg_alpha default; - // If we're selected, we do not apply background opacity + // Cells that are selected should be fully opaque. if (selected) break :bg_alpha default; - // If we're reversed, do not apply background opacity + // Cells that are reversed should be fully opaque. if (style.flags.inverse) break :bg_alpha default; - // If we have a background and its not the default background - // then we apply background opacity - if (style.bg(cell, color_palette) != null and !rgb.eql(self.background_color orelse self.default_background_color)) { + // Cells that have an explicit bg color should be fully opaque. + if (bg_style != null) { break :bg_alpha default; } - // We apply background opacity. - var bg_alpha: f64 = @floatFromInt(default); - bg_alpha *= self.config.background_opacity; - bg_alpha = @ceil(bg_alpha); - break :bg_alpha @intFromFloat(bg_alpha); + // Otherwise, we use the configured background opacity. + break :bg_alpha @intFromFloat(@round(self.config.background_opacity * 255.0)); }; self.cells.bgCell(y, x).* = .{ @@ -3032,14 +3284,20 @@ fn addPreeditCell( /// Sync the atlas data to the given texture. This copies the bytes /// associated with the atlas to the given texture. If the atlas no longer /// fits into the texture, the texture will be resized. -fn syncAtlasTexture(device: objc.Object, atlas: *const font.Atlas, texture: *objc.Object) !void { +fn syncAtlasTexture( + device: objc.Object, + atlas: *const font.Atlas, + texture: *objc.Object, + /// Storage mode for the MTLTexture object + storage_mode: mtl.MTLResourceOptions.StorageMode, +) !void { const width = texture.getProperty(c_ulong, "width"); if (atlas.size > width) { // Free our old texture texture.*.release(); // Reallocate - texture.* = try initAtlasTexture(device, atlas); + texture.* = try initAtlasTexture(device, atlas, storage_mode); } texture.msgSend( @@ -3062,7 +3320,12 @@ fn syncAtlasTexture(device: objc.Object, atlas: *const font.Atlas, texture: *obj } /// Initialize a MTLTexture object for the given atlas. -fn initAtlasTexture(device: objc.Object, atlas: *const font.Atlas) !objc.Object { +fn initAtlasTexture( + device: objc.Object, + atlas: *const font.Atlas, + /// Storage mode for the MTLTexture object + storage_mode: mtl.MTLResourceOptions.StorageMode, +) !objc.Object { // Determine our pixel format const pixel_format: mtl.MTLPixelFormat = switch (atlas.format) { .grayscale => .r8unorm, @@ -3083,15 +3346,14 @@ fn initAtlasTexture(device: objc.Object, atlas: *const font.Atlas) !objc.Object desc.setProperty("width", @as(c_ulong, @intCast(atlas.size))); desc.setProperty("height", @as(c_ulong, @intCast(atlas.size))); - // Xcode tells us that this texture should be shared mode on - // aarch64. This configuration is not supported on x86_64 so - // we only set it on aarch64. - if (comptime builtin.target.cpu.arch == .aarch64) { - desc.setProperty( - "storageMode", - @as(c_ulong, mtl.MTLResourceStorageModeShared), - ); - } + desc.setProperty( + "resourceOptions", + mtl.MTLResourceOptions{ + // Indicate that the CPU writes to this resource but never reads it. + .cpu_cache_mode = .write_combined, + .storage_mode = storage_mode, + }, + ); // Initialize const id = device.msgSend( diff --git a/src/renderer/OpenGL.zig b/src/renderer/OpenGL.zig index e5dec6b2bf..3e674c7155 100644 --- a/src/renderer/OpenGL.zig +++ b/src/renderer/OpenGL.zig @@ -706,8 +706,6 @@ pub fn updateFrame( // Update all our data as tightly as possible within the mutex. var critical: Critical = critical: { - const grid_size = self.size.grid(); - state.mutex.lock(); defer state.mutex.unlock(); @@ -748,19 +746,6 @@ pub fn updateFrame( } } - // If our terminal screen size doesn't match our expected renderer - // size then we skip a frame. This can happen if the terminal state - // is resized between when the renderer mailbox is drained and when - // the state mutex is acquired inside this function. - // - // For some reason this doesn't seem to cause any significant issues - // with flickering while resizing. '\_('-')_/' - if (grid_size.rows != state.terminal.rows or - grid_size.columns != state.terminal.cols) - { - return; - } - // Get the viewport pin so that we can compare it to the current. const viewport_pin = state.terminal.screen.pages.pin(.{ .viewport = .{} }).?; @@ -1276,10 +1261,23 @@ pub fn rebuildCells( } } - // Build each cell + const grid_size = self.size.grid(); + + // We rebuild the cells row-by-row because we do font shaping by row. var row_it = screen.pages.rowIterator(.left_up, .{ .viewport = .{} }, null); - var y: terminal.size.CellCountInt = screen.pages.rows; + // If our cell contents buffer is shorter than the screen viewport, + // we render the rows that fit, starting from the bottom. If instead + // the viewport is shorter than the cell contents buffer, we align + // the top of the viewport with the top of the contents buffer. + var y: terminal.size.CellCountInt = @min( + screen.pages.rows, + grid_size.rows, + ); while (row_it.next()) |row| { + // The viewport may have more rows than our cell contents, + // so we need to break from the loop early if we hit y = 0. + if (y == 0) break; + y -= 1; // True if we want to do font shaping around the cursor. We want to @@ -1356,7 +1354,11 @@ pub fn rebuildCells( var shaper_cells: ?[]const font.shape.Cell = null; var shaper_cells_i: usize = 0; - const row_cells = row.cells(.all); + const row_cells_all = row.cells(.all); + + // If our viewport is wider than our cell contents buffer, + // we still only process cells up to the width of the buffer. + const row_cells = row_cells_all[0..@min(row_cells_all.len, grid_size.columns)]; for (row_cells, 0..) |*cell, x| { // If this cell falls within our preedit range then we @@ -2350,11 +2352,9 @@ pub fn drawFrame(self: *OpenGL, surface: *apprt.Surface) !void { } /// Draw the custom shaders. -fn drawCustomPrograms( - self: *OpenGL, - custom_state: *custom.State, -) !void { +fn drawCustomPrograms(self: *OpenGL, custom_state: *custom.State) !void { _ = self; + assert(custom_state.programs.len > 0); // Bind our state that is global to all custom shaders const custom_bind = try custom_state.bind(); @@ -2365,10 +2365,10 @@ fn drawCustomPrograms( // Go through each custom shader and draw it. for (custom_state.programs) |program| { - // Bind our cell program state, buffers const bind = try program.bind(); defer bind.unbind(); try bind.draw(); + try custom_state.copyFramebuffer(); } } diff --git a/src/renderer/metal/api.zig b/src/renderer/metal/api.zig index 48056ae5ee..535a0b42b0 100644 --- a/src/renderer/metal/api.zig +++ b/src/renderer/metal/api.zig @@ -24,12 +24,36 @@ pub const MTLStoreAction = enum(c_ulong) { store = 1, }; -/// https://developer.apple.com/documentation/metal/mtlstoragemode?language=objc -pub const MTLStorageMode = enum(c_ulong) { - shared = 0, - managed = 1, - private = 2, - memoryless = 3, +/// https://developer.apple.com/documentation/metal/mtlresourceoptions?language=objc +pub const MTLResourceOptions = packed struct(c_ulong) { + /// https://developer.apple.com/documentation/metal/mtlcpucachemode?language=objc + cpu_cache_mode: CPUCacheMode = .default, + /// https://developer.apple.com/documentation/metal/mtlstoragemode?language=objc + storage_mode: StorageMode, + /// https://developer.apple.com/documentation/metal/mtlhazardtrackingmode?language=objc + hazard_tracking_mode: HazardTrackingMode = .default, + + _pad: @Type(.{ + .Int = .{ .signedness = .unsigned, .bits = @bitSizeOf(c_ulong) - 10 }, + }) = 0, + + pub const CPUCacheMode = enum(u4) { + default = 0, + write_combined = 1, + }; + + pub const StorageMode = enum(u4) { + shared = 0, + managed = 1, + private = 2, + memoryless = 3, + }; + + pub const HazardTrackingMode = enum(u2) { + default = 0, + untracked = 1, + tracked = 2, + }; }; /// https://developer.apple.com/documentation/metal/mtlprimitivetype?language=objc @@ -74,6 +98,7 @@ pub const MTLPixelFormat = enum(c_ulong) { rgba8unorm = 70, rgba8uint = 73, bgra8unorm = 80, + bgra8unorm_srgb = 81, }; /// https://developer.apple.com/documentation/metal/mtlpurgeablestate?language=objc @@ -138,10 +163,6 @@ pub const MTLTextureUsage = enum(c_ulong) { pixel_format_view = 8, }; -/// https://developer.apple.com/documentation/metal/mtlresourceoptions?language=objc -/// (incomplete, we only use this mode so we just hardcode it) -pub const MTLResourceStorageModeShared: c_ulong = @intFromEnum(MTLStorageMode.shared) << 4; - pub const MTLClearColor = extern struct { red: f64, green: f64, diff --git a/src/renderer/metal/buffer.zig b/src/renderer/metal/buffer.zig index 55a207f030..4128e297b7 100644 --- a/src/renderer/metal/buffer.zig +++ b/src/renderer/metal/buffer.zig @@ -2,6 +2,7 @@ const std = @import("std"); const Allocator = std.mem.Allocator; const assert = std.debug.assert; const objc = @import("objc"); +const macos = @import("macos"); const mtl = @import("api.zig"); @@ -14,35 +15,46 @@ pub fn Buffer(comptime T: type) type { return struct { const Self = @This(); + /// The resource options for this buffer. + options: mtl.MTLResourceOptions, + buffer: objc.Object, // MTLBuffer /// Initialize a buffer with the given length pre-allocated. - pub fn init(device: objc.Object, len: usize) !Self { + pub fn init( + device: objc.Object, + len: usize, + options: mtl.MTLResourceOptions, + ) !Self { const buffer = device.msgSend( objc.Object, objc.sel("newBufferWithLength:options:"), .{ @as(c_ulong, @intCast(len * @sizeOf(T))), - mtl.MTLResourceStorageModeShared, + options, }, ); - return .{ .buffer = buffer }; + return .{ .buffer = buffer, .options = options }; } /// Init the buffer filled with the given data. - pub fn initFill(device: objc.Object, data: []const T) !Self { + pub fn initFill( + device: objc.Object, + data: []const T, + options: mtl.MTLResourceOptions, + ) !Self { const buffer = device.msgSend( objc.Object, objc.sel("newBufferWithBytes:length:options:"), .{ @as(*const anyopaque, @ptrCast(data.ptr)), @as(c_ulong, @intCast(data.len * @sizeOf(T))), - mtl.MTLResourceStorageModeShared, + options, }, ); - return .{ .buffer = buffer }; + return .{ .buffer = buffer, .options = options }; } pub fn deinit(self: *Self) void { @@ -85,7 +97,7 @@ pub fn Buffer(comptime T: type) type { objc.sel("newBufferWithLength:options:"), .{ @as(c_ulong, @intCast(size * @sizeOf(T))), - mtl.MTLResourceStorageModeShared, + self.options, }, ); } @@ -106,6 +118,18 @@ pub fn Buffer(comptime T: type) type { }; @memcpy(dst, src); + + // If we're using the managed resource storage mode, then + // we need to signal Metal to synchronize the buffer data. + // + // Ref: https://developer.apple.com/documentation/metal/synchronizing-a-managed-resource-in-macos?language=objc + if (self.options.storage_mode == .managed) { + self.buffer.msgSend( + void, + "didModifyRange:", + .{macos.foundation.Range.init(0, req_bytes)}, + ); + } } /// Like Buffer.sync but takes data from an array of ArrayLists, @@ -130,7 +154,7 @@ pub fn Buffer(comptime T: type) type { objc.sel("newBufferWithLength:options:"), .{ @as(c_ulong, @intCast(size * @sizeOf(T))), - mtl.MTLResourceStorageModeShared, + self.options, }, ); } @@ -153,6 +177,18 @@ pub fn Buffer(comptime T: type) type { i += list.items.len * @sizeOf(T); } + // If we're using the managed resource storage mode, then + // we need to signal Metal to synchronize the buffer data. + // + // Ref: https://developer.apple.com/documentation/metal/synchronizing-a-managed-resource-in-macos?language=objc + if (self.options.storage_mode == .managed) { + self.buffer.msgSend( + void, + "didModifyRange:", + .{macos.foundation.Range.init(0, req_bytes)}, + ); + } + return total_len; } }; diff --git a/src/renderer/metal/image.zig b/src/renderer/metal/image.zig index 9d72cae96a..835fbd672b 100644 --- a/src/renderer/metal/image.zig +++ b/src/renderer/metal/image.zig @@ -358,6 +358,8 @@ pub const Image = union(enum) { self: *Image, alloc: Allocator, device: objc.Object, + /// Storage mode for the MTLTexture object + storage_mode: mtl.MTLResourceOptions.StorageMode, ) !void { // Convert our data if we have to try self.convert(alloc); @@ -366,7 +368,7 @@ pub const Image = union(enum) { const p = self.pending().?; // Create our texture - const texture = try initTexture(p, device); + const texture = try initTexture(p, device, storage_mode); errdefer texture.msgSend(void, objc.sel("release"), .{}); // Upload our data @@ -424,7 +426,12 @@ pub const Image = union(enum) { }; } - fn initTexture(p: Pending, device: objc.Object) !objc.Object { + fn initTexture( + p: Pending, + device: objc.Object, + /// Storage mode for the MTLTexture object + storage_mode: mtl.MTLResourceOptions.StorageMode, + ) !objc.Object { // Create our descriptor const desc = init: { const Class = objc.getClass("MTLTextureDescriptor").?; @@ -438,6 +445,15 @@ pub const Image = union(enum) { desc.setProperty("width", @as(c_ulong, @intCast(p.width))); desc.setProperty("height", @as(c_ulong, @intCast(p.height))); + desc.setProperty( + "resourceOptions", + mtl.MTLResourceOptions{ + // Indicate that the CPU writes to this resource but never reads it. + .cpu_cache_mode = .write_combined, + .storage_mode = storage_mode, + }, + ); + // Initialize const id = device.msgSend( ?*anyopaque, diff --git a/src/renderer/metal/shaders.zig b/src/renderer/metal/shaders.zig index b909a2f2a9..b297de809c 100644 --- a/src/renderer/metal/shaders.zig +++ b/src/renderer/metal/shaders.zig @@ -13,9 +13,7 @@ const log = std.log.scoped(.metal); pub const Shaders = struct { library: objc.Object, - /// The cell shader is the shader used to render the terminal cells. - /// It is a single shader that is used for both the background and - /// foreground. + /// Renders cell foreground elements (text, decorations). cell_text_pipeline: objc.Object, /// The cell background shader is the shader used to render the @@ -40,17 +38,18 @@ pub const Shaders = struct { alloc: Allocator, device: objc.Object, post_shaders: []const [:0]const u8, + pixel_format: mtl.MTLPixelFormat, ) !Shaders { const library = try initLibrary(device); errdefer library.msgSend(void, objc.sel("release"), .{}); - const cell_text_pipeline = try initCellTextPipeline(device, library); + const cell_text_pipeline = try initCellTextPipeline(device, library, pixel_format); errdefer cell_text_pipeline.msgSend(void, objc.sel("release"), .{}); - const cell_bg_pipeline = try initCellBgPipeline(device, library); + const cell_bg_pipeline = try initCellBgPipeline(device, library, pixel_format); errdefer cell_bg_pipeline.msgSend(void, objc.sel("release"), .{}); - const image_pipeline = try initImagePipeline(device, library); + const image_pipeline = try initImagePipeline(device, library, pixel_format); errdefer image_pipeline.msgSend(void, objc.sel("release"), .{}); const post_pipelines: []const objc.Object = initPostPipelines( @@ -58,6 +57,7 @@ pub const Shaders = struct { device, library, post_shaders, + pixel_format, ) catch |err| err: { // If an error happens while building postprocess shaders we // want to just not use any postprocess shaders since we don't @@ -137,9 +137,29 @@ pub const Uniforms = extern struct { cursor_pos: [2]u16 align(4), cursor_color: [4]u8 align(4), - // Whether the cursor is 2 cells wide. + /// The background color for the whole surface. + bg_color: [4]u8 align(4), + + /// Whether the cursor is 2 cells wide. cursor_wide: bool align(1), + /// Indicates that colors provided to the shader are already in + /// the P3 color space, so they don't need to be converted from + /// sRGB. + use_display_p3: bool align(1), + + /// Indicates that the color attachments for the shaders have + /// an `*_srgb` pixel format, which means the shaders need to + /// output linear RGB colors rather than gamma encoded colors, + /// since blending will be performed in linear space and then + /// Metal itself will re-encode the colors for storage. + use_linear_blending: bool align(1), + + /// Enables a weight correction step that makes text rendered + /// with linear alpha blending have a similar apparent weight + /// (thickness) to gamma-incorrect blending. + use_linear_correction: bool align(1) = false, + const PaddingExtend = packed struct(u8) { left: bool = false, right: bool = false, @@ -201,6 +221,7 @@ fn initPostPipelines( device: objc.Object, library: objc.Object, shaders: []const [:0]const u8, + pixel_format: mtl.MTLPixelFormat, ) ![]const objc.Object { // If we have no shaders, do nothing. if (shaders.len == 0) return &.{}; @@ -220,7 +241,12 @@ fn initPostPipelines( // Build each shader. Note we don't use "0.." to build our index // because we need to keep track of our length to clean up above. for (shaders) |source| { - pipelines[i] = try initPostPipeline(device, library, source); + pipelines[i] = try initPostPipeline( + device, + library, + source, + pixel_format, + ); i += 1; } @@ -232,6 +258,7 @@ fn initPostPipeline( device: objc.Object, library: objc.Object, data: [:0]const u8, + pixel_format: mtl.MTLPixelFormat, ) !objc.Object { // Create our library which has the shader source const post_library = library: { @@ -301,8 +328,7 @@ fn initPostPipeline( .{@as(c_ulong, 0)}, ); - // Value is MTLPixelFormatBGRA8Unorm - attachment.setProperty("pixelFormat", @as(c_ulong, 80)); + attachment.setProperty("pixelFormat", @intFromEnum(pixel_format)); } // Make our state @@ -343,7 +369,11 @@ pub const CellText = extern struct { }; /// Initialize the cell render pipeline for our shader library. -fn initCellTextPipeline(device: objc.Object, library: objc.Object) !objc.Object { +fn initCellTextPipeline( + device: objc.Object, + library: objc.Object, + pixel_format: mtl.MTLPixelFormat, +) !objc.Object { // Get our vertex and fragment functions const func_vert = func_vert: { const str = try macos.foundation.String.createWithBytes( @@ -427,8 +457,7 @@ fn initCellTextPipeline(device: objc.Object, library: objc.Object) !objc.Object .{@as(c_ulong, 0)}, ); - // Value is MTLPixelFormatBGRA8Unorm - attachment.setProperty("pixelFormat", @as(c_ulong, 80)); + attachment.setProperty("pixelFormat", @intFromEnum(pixel_format)); // Blending. This is required so that our text we render on top // of our drawable properly blends into the bg. @@ -458,11 +487,15 @@ fn initCellTextPipeline(device: objc.Object, library: objc.Object) !objc.Object pub const CellBg = [4]u8; /// Initialize the cell background render pipeline for our shader library. -fn initCellBgPipeline(device: objc.Object, library: objc.Object) !objc.Object { +fn initCellBgPipeline( + device: objc.Object, + library: objc.Object, + pixel_format: mtl.MTLPixelFormat, +) !objc.Object { // Get our vertex and fragment functions const func_vert = func_vert: { const str = try macos.foundation.String.createWithBytes( - "full_screen_vertex", + "cell_bg_vertex", .utf8, false, ); @@ -507,8 +540,7 @@ fn initCellBgPipeline(device: objc.Object, library: objc.Object) !objc.Object { .{@as(c_ulong, 0)}, ); - // Value is MTLPixelFormatBGRA8Unorm - attachment.setProperty("pixelFormat", @as(c_ulong, 80)); + attachment.setProperty("pixelFormat", @intFromEnum(pixel_format)); // Blending. This is required so that our text we render on top // of our drawable properly blends into the bg. @@ -535,7 +567,11 @@ fn initCellBgPipeline(device: objc.Object, library: objc.Object) !objc.Object { } /// Initialize the image render pipeline for our shader library. -fn initImagePipeline(device: objc.Object, library: objc.Object) !objc.Object { +fn initImagePipeline( + device: objc.Object, + library: objc.Object, + pixel_format: mtl.MTLPixelFormat, +) !objc.Object { // Get our vertex and fragment functions const func_vert = func_vert: { const str = try macos.foundation.String.createWithBytes( @@ -619,8 +655,7 @@ fn initImagePipeline(device: objc.Object, library: objc.Object) !objc.Object { .{@as(c_ulong, 0)}, ); - // Value is MTLPixelFormatBGRA8Unorm - attachment.setProperty("pixelFormat", @as(c_ulong, 80)); + attachment.setProperty("pixelFormat", @intFromEnum(pixel_format)); // Blending. This is required so that our text we render on top // of our drawable properly blends into the bg. diff --git a/src/renderer/opengl/custom.zig b/src/renderer/opengl/custom.zig index 2cab0940c5..859277ce51 100644 --- a/src/renderer/opengl/custom.zig +++ b/src/renderer/opengl/custom.zig @@ -230,6 +230,21 @@ pub const State = struct { }; } + /// Copy the fbo's attached texture to the backbuffer. + pub fn copyFramebuffer(self: *State) !void { + const texbind = try self.fb_texture.bind(.@"2D"); + errdefer texbind.unbind(); + try texbind.copySubImage2D( + 0, + 0, + 0, + 0, + 0, + @intFromFloat(self.uniforms.resolution[0]), + @intFromFloat(self.uniforms.resolution[1]), + ); + } + pub const Binding = struct { vao: gl.VertexArray.Binding, ebo: gl.Buffer.Binding, @@ -251,7 +266,6 @@ pub const Program = struct { const program = try gl.Program.createVF( @embedFile("../shaders/custom.v.glsl"), src, - //@embedFile("../shaders/temp.f.glsl"), ); errdefer program.destroy(); diff --git a/src/renderer/shaders/cell.metal b/src/renderer/shaders/cell.metal index 2a107402b2..e24ddcb1ef 100644 --- a/src/renderer/shaders/cell.metal +++ b/src/renderer/shaders/cell.metal @@ -18,7 +18,11 @@ struct Uniforms { float min_contrast; ushort2 cursor_pos; uchar4 cursor_color; + uchar4 bg_color; bool cursor_wide; + bool use_display_p3; + bool use_linear_blending; + bool use_linear_correction; }; //------------------------------------------------------------------- @@ -26,40 +30,88 @@ struct Uniforms { //------------------------------------------------------------------- #pragma mark - Colors -// https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef -float luminance_component(float c) { - if (c <= 0.03928f) { - return c / 12.92f; - } else { - return pow((c + 0.055f) / 1.055f, 2.4f); - } +// D50-adapted sRGB to XYZ conversion matrix. +// http://www.brucelindbloom.com/Eqn_RGB_XYZ_Matrix.html +constant float3x3 sRGB_XYZ = transpose(float3x3( + 0.4360747, 0.3850649, 0.1430804, + 0.2225045, 0.7168786, 0.0606169, + 0.0139322, 0.0971045, 0.7141733 +)); +// XYZ to Display P3 conversion matrix. +// http://endavid.com/index.php?entry=79 +constant float3x3 XYZ_DP3 = transpose(float3x3( + 2.40414768,-0.99010704,-0.39759019, + -0.84239098, 1.79905954, 0.01597023, + 0.04838763,-0.09752546, 1.27393636 +)); +// By composing the two above matrices we get +// our sRGB to Display P3 conversion matrix. +constant float3x3 sRGB_DP3 = XYZ_DP3 * sRGB_XYZ; + +// Converts a color in linear sRGB to linear Display P3 +// +// TODO: The color matrix should probably be computed +// dynamically and passed as a uniform, rather +// than being hard coded above. +float3 srgb_to_display_p3(float3 srgb) { + return sRGB_DP3 * srgb; +} + +// Converts a color from sRGB gamma encoding to linear. +float4 linearize(float4 srgb) { + bool3 cutoff = srgb.rgb <= 0.04045; + float3 lower = srgb.rgb / 12.92; + float3 higher = pow((srgb.rgb + 0.055) / 1.055, 2.4); + srgb.rgb = mix(higher, lower, float3(cutoff)); + + return srgb; } +float linearize(float v) { + return v <= 0.04045 ? v / 12.92 : pow((v + 0.055) / 1.055, 2.4); +} + +// Converts a color from linear to sRGB gamma encoding. +float4 unlinearize(float4 linear) { + bool3 cutoff = linear.rgb <= 0.0031308; + float3 lower = linear.rgb * 12.92; + float3 higher = pow(linear.rgb, 1.0 / 2.4) * 1.055 - 0.055; + linear.rgb = mix(higher, lower, float3(cutoff)); -float relative_luminance(float3 color) { - color.r = luminance_component(color.r); - color.g = luminance_component(color.g); - color.b = luminance_component(color.b); - float3 weights = float3(0.2126f, 0.7152f, 0.0722f); - return dot(color, weights); + return linear; +} +float unlinearize(float v) { + return v <= 0.0031308 ? v * 12.92 : pow(v, 1.0 / 2.4) * 1.055 - 0.055; +} + +// Compute the luminance of the provided color. +// +// Takes colors in linear RGB space. If your colors are gamma +// encoded, linearize them before using them with this function. +float luminance(float3 color) { + return dot(color, float3(0.2126f, 0.7152f, 0.0722f)); } // https://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef +// +// Takes colors in linear RGB space. If your colors are gamma +// encoded, linearize them before using them with this function. float contrast_ratio(float3 color1, float3 color2) { - float l1 = relative_luminance(color1); - float l2 = relative_luminance(color2); + float l1 = luminance(color1); + float l2 = luminance(color2); return (max(l1, l2) + 0.05f) / (min(l1, l2) + 0.05f); } // Return the fg if the contrast ratio is greater than min, otherwise // return a color that satisfies the contrast ratio. Currently, the color // is always white or black, whichever has the highest contrast ratio. +// +// Takes colors in linear RGB space. If your colors are gamma +// encoded, linearize them before using them with this function. float4 contrasted_color(float min, float4 fg, float4 bg) { - float3 fg_premult = fg.rgb * fg.a; - float3 bg_premult = bg.rgb * bg.a; - float ratio = contrast_ratio(fg_premult, bg_premult); + float ratio = contrast_ratio(fg.rgb, bg.rgb); if (ratio < min) { - float white_ratio = contrast_ratio(float3(1.0f), bg_premult); - float black_ratio = contrast_ratio(float3(0.0f), bg_premult); + float white_ratio = contrast_ratio(float3(1.0f), bg.rgb); + float black_ratio = contrast_ratio(float3(0.0f), bg.rgb); if (white_ratio > black_ratio) { return float4(1.0f); } else { @@ -70,6 +122,62 @@ float4 contrasted_color(float min, float4 fg, float4 bg) { return fg; } +// Load a 4 byte RGBA non-premultiplied color and linearize +// and convert it as necessary depending on the provided info. +// +// Returns a color in the Display P3 color space. +// +// If `display_p3` is true, then the provided color is assumed to +// already be in the Display P3 color space, otherwise it's treated +// as an sRGB color and is appropriately converted to Display P3. +// +// `linear` controls whether the returned color is linear or gamma encoded. +float4 load_color( + uchar4 in_color, + bool display_p3, + bool linear +) { + // 0 .. 255 -> 0.0 .. 1.0 + float4 color = float4(in_color) / 255.0f; + + // If our color is already in Display P3 and + // we aren't doing linear blending, then we + // already have the correct color here and + // can premultiply and return it. + if (display_p3 && !linear) { + color.rgb *= color.a; + return color; + } + + // The color is in either the sRGB or Display P3 color space, + // so in either case, it's a color space which uses the sRGB + // transfer function, so we can use one function in order to + // linearize it in either case. + // + // Even if we aren't doing linear blending, the color + // needs to be in linear space to convert color spaces. + color = linearize(color); + + // If we're *NOT* using display P3 colors, then we're dealing + // with an sRGB color, in which case we need to convert it in + // to the Display P3 color space, since our output is always + // Display P3. + if (!display_p3) { + color.rgb = srgb_to_display_p3(color.rgb); + } + + // If we're not doing linear blending, then we need to + // unlinearize after doing the color space conversion. + if (!linear) { + color = unlinearize(color); + } + + // Premultiply our color by its alpha. + color.rgb *= color.a; + + return color; +} + //------------------------------------------------------------------- // Full Screen Vertex Shader //------------------------------------------------------------------- @@ -112,25 +220,62 @@ vertex FullScreenVertexOut full_screen_vertex( //------------------------------------------------------------------- #pragma mark - Cell BG Shader +struct CellBgVertexOut { + float4 position [[position]]; + float4 bg_color; +}; + +vertex CellBgVertexOut cell_bg_vertex( + uint vid [[vertex_id]], + constant Uniforms& uniforms [[buffer(1)]] +) { + CellBgVertexOut out; + + float4 position; + position.x = (vid == 2) ? 3.0 : -1.0; + position.y = (vid == 0) ? -3.0 : 1.0; + position.zw = 1.0; + out.position = position; + + // Convert the background color to Display P3 + out.bg_color = load_color( + uniforms.bg_color, + uniforms.use_display_p3, + uniforms.use_linear_blending + ); + + return out; +} + fragment float4 cell_bg_fragment( - FullScreenVertexOut in [[stage_in]], + CellBgVertexOut in [[stage_in]], constant uchar4 *cells [[buffer(0)]], constant Uniforms& uniforms [[buffer(1)]] ) { int2 grid_pos = int2(floor((in.position.xy - uniforms.grid_padding.wx) / uniforms.cell_size)); + float4 bg = float4(0.0); + // If we have any background transparency then we render bg-colored cells as + // fully transparent, since the background is handled by the layer bg color + // and we don't want to double up our bg color, but if our bg color is fully + // opaque then our layer is opaque and can't handle transparency, so we need + // to return the bg color directly instead. + if (uniforms.bg_color.a == 255) { + bg = in.bg_color; + } + // Clamp x position, extends edge bg colors in to padding on sides. if (grid_pos.x < 0) { if (uniforms.padding_extend & EXTEND_LEFT) { grid_pos.x = 0; } else { - return float4(0.0); + return bg; } } else if (grid_pos.x > uniforms.grid_size.x - 1) { if (uniforms.padding_extend & EXTEND_RIGHT) { grid_pos.x = uniforms.grid_size.x - 1; } else { - return float4(0.0); + return bg; } } @@ -139,18 +284,40 @@ fragment float4 cell_bg_fragment( if (uniforms.padding_extend & EXTEND_UP) { grid_pos.y = 0; } else { - return float4(0.0); + return bg; } } else if (grid_pos.y > uniforms.grid_size.y - 1) { if (uniforms.padding_extend & EXTEND_DOWN) { grid_pos.y = uniforms.grid_size.y - 1; } else { - return float4(0.0); + return bg; } } - // Retrieve color for cell and return it. - return float4(cells[grid_pos.y * uniforms.grid_size.x + grid_pos.x]) / 255.0; + // Load the color for the cell. + uchar4 cell_color = cells[grid_pos.y * uniforms.grid_size.x + grid_pos.x]; + + // We have special case handling for when the cell color matches the bg color. + if (all(cell_color == uniforms.bg_color)) { + return bg; + } + + // Convert the color and return it. + // + // TODO: We may want to blend the color with the background + // color, rather than purely replacing it, this needs + // some consideration about config options though. + // + // TODO: It might be a good idea to do a pass before this + // to convert all of the bg colors, so we don't waste + // a bunch of work converting the cell color in every + // fragment of each cell. It's not the most epxensive + // operation, but it is still wasted work. + return load_color( + cell_color, + uniforms.use_display_p3, + uniforms.use_linear_blending + ); } //------------------------------------------------------------------- @@ -192,8 +359,9 @@ struct CellTextVertexIn { struct CellTextVertexOut { float4 position [[position]]; - uint8_t mode; - float4 color; + uint8_t mode [[flat]]; + float4 color [[flat]]; + float4 bg_color [[flat]]; float2 tex_coord; }; @@ -222,7 +390,6 @@ vertex CellTextVertexOut cell_text_vertex( CellTextVertexOut out; out.mode = in.mode; - out.color = float4(in.color) / 255.0f; // === Grid Cell === // +X @@ -277,6 +444,21 @@ vertex CellTextVertexOut cell_text_vertex( // be sampled with pixel coordinate mode. out.tex_coord = float2(in.glyph_pos) + float2(in.glyph_size) * corner; + // Get our color. We always fetch a linearized version to + // make it easier to handle minimum contrast calculations. + out.color = load_color( + in.color, + uniforms.use_display_p3, + true + ); + + // Get the BG color + out.bg_color = load_color( + bg_colors[in.grid_pos.y * uniforms.grid_size.x + in.grid_pos.x], + uniforms.use_display_p3, + true + ); + // If we have a minimum contrast, we need to check if we need to // change the color of the text to ensure it has enough contrast // with the background. @@ -285,21 +467,24 @@ vertex CellTextVertexOut cell_text_vertex( // and Powerline glyphs to be unaffected (else parts of the line would // have different colors as some parts are displayed via background colors). if (uniforms.min_contrast > 1.0f && in.mode == MODE_TEXT) { - float4 bg_color = float4(bg_colors[in.grid_pos.y * uniforms.grid_size.x + in.grid_pos.x]) / 255.0f; - out.color = contrasted_color(uniforms.min_contrast, out.color, bg_color); + // Ensure our minimum contrast + out.color = contrasted_color(uniforms.min_contrast, out.color, out.bg_color); } - // If this cell is the cursor cell, then we need to change the color. - if ( - in.mode != MODE_TEXT_CURSOR && - ( + // Check if current position is under cursor (including wide cursor) + bool is_cursor_pos = ( in.grid_pos.x == uniforms.cursor_pos.x || uniforms.cursor_wide && in.grid_pos.x == uniforms.cursor_pos.x + 1 - ) && - in.grid_pos.y == uniforms.cursor_pos.y - ) { - out.color = float4(uniforms.cursor_color) / 255.0f; + ) && in.grid_pos.y == uniforms.cursor_pos.y; + + // If this cell is the cursor cell, then we need to change the color. + if (in.mode != MODE_TEXT_CURSOR && is_cursor_pos) { + out.color = load_color( + uniforms.cursor_color, + uniforms.use_display_p3, + false + ); } return out; @@ -308,7 +493,8 @@ vertex CellTextVertexOut cell_text_vertex( fragment float4 cell_text_fragment( CellTextVertexOut in [[stage_in]], texture2d textureGrayscale [[texture(0)]], - texture2d textureColor [[texture(1)]] + texture2d textureColor [[texture(1)]], + constant Uniforms& uniforms [[buffer(2)]] ) { constexpr sampler textureSampler( coord::pixel, @@ -322,20 +508,72 @@ fragment float4 cell_text_fragment( case MODE_TEXT_CONSTRAINED: case MODE_TEXT_POWERLINE: case MODE_TEXT: { - // We premult the alpha to our whole color since our blend function - // uses One/OneMinusSourceAlpha to avoid blurry edges. - // We first premult our given color. - float4 premult = float4(in.color.rgb * in.color.a, in.color.a); - - // Then premult the texture color + // Our input color is always linear. + float4 color = in.color; + + // If we're not doing linear blending, then we need to + // re-apply the gamma encoding to our color manually. + // + // Since the alpha is premultiplied, we need to divide + // it out before unlinearizing and re-multiply it after. + if (!uniforms.use_linear_blending) { + color.rgb /= color.a; + color = unlinearize(color); + color.rgb *= color.a; + } + + // Fetch our alpha mask for this pixel. float a = textureGrayscale.sample(textureSampler, in.tex_coord).r; - premult = premult * a; - return premult; + // Linear blending weight correction corrects the alpha value to + // produce blending results which match gamma-incorrect blending. + if (uniforms.use_linear_correction) { + // Short explanation of how this works: + // + // We get the luminances of the foreground and background colors, + // and then unlinearize them and perform blending on them. This + // gives us our desired luminance, which we derive our new alpha + // value from by mapping the range [bg_l, fg_l] to [0, 1], since + // our final blend will be a linear interpolation from bg to fg. + // + // This yields virtually identical results for grayscale blending, + // and very similar but non-identical results for color blending. + float4 bg = in.bg_color; + float fg_l = luminance(color.rgb); + float bg_l = luminance(bg.rgb); + // To avoid numbers going haywire, we don't apply correction + // when the bg and fg luminances are within 0.001 of each other. + if (abs(fg_l - bg_l) > 0.001) { + float blend_l = linearize(unlinearize(fg_l) * a + unlinearize(bg_l) * (1.0 - a)); + a = clamp((blend_l - bg_l) / (fg_l - bg_l), 0.0, 1.0); + } + } + + // Multiply our whole color by the alpha mask. + // Since we use premultiplied alpha, this is + // the correct way to apply the mask. + color *= a; + + return color; } case MODE_TEXT_COLOR: { - return textureColor.sample(textureSampler, in.tex_coord); + // For now, we assume that color glyphs are + // already premultiplied Display P3 colors. + float4 color = textureColor.sample(textureSampler, in.tex_coord); + + // If we aren't doing linear blending, we can return this right away. + if (!uniforms.use_linear_blending) { + return color; + } + + // Otherwise we need to linearize the color. Since the alpha is + // premultiplied, we need to divide it out before linearizing. + color.rgb /= color.a; + color = linearize(color); + color.rgb *= color.a; + + return color; } } } @@ -409,7 +647,8 @@ vertex ImageVertexOut image_vertex( fragment float4 image_fragment( ImageVertexOut in [[stage_in]], - texture2d image [[texture(0)]] + texture2d image [[texture(0)]], + constant Uniforms& uniforms [[buffer(1)]] ) { constexpr sampler textureSampler(address::clamp_to_edge, filter::linear); @@ -418,10 +657,12 @@ fragment float4 image_fragment( // our texture to BGRA8Unorm. uint4 rgba = image.sample(textureSampler, in.tex_coord); - // Convert to float4 and premultiply the alpha. We should also probably - // premultiply the alpha in the texture. - float4 result = float4(rgba) / 255.0f; - result.rgb *= result.a; - return result; + return load_color( + uchar4(rgba), + // We assume all images are sRGB regardless of the configured colorspace + // TODO: Maybe support wide gamut images? + false, + uniforms.use_linear_blending + ); } diff --git a/src/shell-integration/README.md b/src/shell-integration/README.md index 976cf49240..3d5159c711 100644 --- a/src/shell-integration/README.md +++ b/src/shell-integration/README.md @@ -6,7 +6,7 @@ supports. This README is meant as developer documentation and not as user documentation. For user documentation, see the main -README. +README or [ghostty.org](https://ghostty.org/docs) ## Implementation Details diff --git a/src/shell-integration/bash/bash-preexec.sh b/src/shell-integration/bash/bash-preexec.sh index 14a677888d..e07da0d1e5 100644 --- a/src/shell-integration/bash/bash-preexec.sh +++ b/src/shell-integration/bash/bash-preexec.sh @@ -250,10 +250,8 @@ __bp_preexec_invoke_exec() { fi local this_command - this_command=$( - export LC_ALL=C - HISTTIMEFORMAT='' builtin history 1 | sed '1 s/^ *[0-9][0-9]*[* ] //' - ) + this_command=$(LC_ALL=C HISTTIMEFORMAT='' builtin history 1) + this_command="${this_command#*[[:digit:]][* ] }" # Sanity check to make sure we have something to invoke our function with. if [[ -z "$this_command" ]]; then @@ -297,10 +295,8 @@ __bp_install() { trap '__bp_preexec_invoke_exec "$_"' DEBUG # Preserve any prior DEBUG trap as a preexec function - local prior_trap - # we can't easily do this with variable expansion. Leaving as sed command. - # shellcheck disable=SC2001 - prior_trap=$(sed "s/[^']*'\(.*\)'[^']*/\1/" <<<"${__bp_trap_string:-}") + eval "local trap_argv=(${__bp_trap_string:-})" + local prior_trap=${trap_argv[2]:-} unset __bp_trap_string if [[ -n "$prior_trap" ]]; then eval '__bp_original_debug_trap() { diff --git a/src/shell-integration/bash/ghostty.bash b/src/shell-integration/bash/ghostty.bash index 71c644b696..7fae435a3a 100644 --- a/src/shell-integration/bash/ghostty.bash +++ b/src/shell-integration/bash/ghostty.bash @@ -20,14 +20,13 @@ if [[ "$-" != *i* ]] ; then builtin return; fi if [ -z "$GHOSTTY_RESOURCES_DIR" ]; then builtin return; fi -# When automatic shell integration is active, we need to manually -# load the normal bash startup files based on the injected state. +# When automatic shell integration is active, we were started in POSIX +# mode and need to manually recreate the bash startup sequence. if [ -n "$GHOSTTY_BASH_INJECT" ]; then - builtin declare ghostty_bash_inject="$GHOSTTY_BASH_INJECT" - builtin unset GHOSTTY_BASH_INJECT ENV - - # At this point, we're in POSIX mode and rely on the injected - # flags to guide is through the rest of the startup sequence. + # Store a temporary copy of our startup flags and unset these global + # environment variables so we can safely handle reentrancy. + builtin declare __ghostty_bash_flags="$GHOSTTY_BASH_INJECT" + builtin unset ENV GHOSTTY_BASH_INJECT # Restore bash's default 'posix' behavior. Also reset 'inherit_errexit', # which doesn't happen as part of the 'posix' reset. @@ -40,35 +39,34 @@ if [ -n "$GHOSTTY_BASH_INJECT" ]; then builtin unset GHOSTTY_BASH_UNEXPORT_HISTFILE fi - # Manually source the startup files, respecting the injected flags like - # --norc and --noprofile that we parsed with the shell integration code. - # - # See also: run_startup_files() in shell.c in the Bash source code + # Manually source the startup files. See INVOCATION in bash(1) and + # run_startup_files() in shell.c in the Bash source code. if builtin shopt -q login_shell; then - if [[ $ghostty_bash_inject != *"--noprofile"* ]]; then + if [[ $__ghostty_bash_flags != *"--noprofile"* ]]; then [ -r /etc/profile ] && builtin source "/etc/profile" - for rcfile in "$HOME/.bash_profile" "$HOME/.bash_login" "$HOME/.profile"; do - [ -r "$rcfile" ] && { builtin source "$rcfile"; break; } + for __ghostty_rcfile in "$HOME/.bash_profile" "$HOME/.bash_login" "$HOME/.profile"; do + [ -r "$__ghostty_rcfile" ] && { builtin source "$__ghostty_rcfile"; break; } done fi else - if [[ $ghostty_bash_inject != *"--norc"* ]]; then + if [[ $__ghostty_bash_flags != *"--norc"* ]]; then # The location of the system bashrc is determined at bash build # time via -DSYS_BASHRC and can therefore vary across distros: # Arch, Debian, Ubuntu use /etc/bash.bashrc # Fedora uses /etc/bashrc sourced from ~/.bashrc instead of SYS_BASHRC # Void Linux uses /etc/bash/bashrc # Nixos uses /etc/bashrc - for rcfile in /etc/bash.bashrc /etc/bash/bashrc /etc/bashrc; do - [ -r "$rcfile" ] && { builtin source "$rcfile"; break; } + for __ghostty_rcfile in /etc/bash.bashrc /etc/bash/bashrc /etc/bashrc; do + [ -r "$__ghostty_rcfile" ] && { builtin source "$__ghostty_rcfile"; break; } done if [[ -z "$GHOSTTY_BASH_RCFILE" ]]; then GHOSTTY_BASH_RCFILE="$HOME/.bashrc"; fi [ -r "$GHOSTTY_BASH_RCFILE" ] && builtin source "$GHOSTTY_BASH_RCFILE" fi fi + builtin unset __ghostty_rcfile + builtin unset __ghostty_bash_flags builtin unset GHOSTTY_BASH_RCFILE - builtin unset ghostty_bash_inject rcfile fi # Sudo diff --git a/src/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish b/src/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish index 420a495286..cd4f56105b 100644 --- a/src/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish +++ b/src/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish @@ -71,11 +71,11 @@ function __ghostty_setup --on-event fish_prompt -d "Setup ghostty integration" and test -n "$TERMINFO"; and test "file" = (type -t sudo 2> /dev/null; or echo "x") # Wrap `sudo` command to ensure Ghostty terminfo is preserved function sudo -d "Wrap sudo to preserve terminfo" - set --local sudo_has_sudoedit_flags "no" + set --function sudo_has_sudoedit_flags "no" for arg in $argv # Check if argument is '-e' or '--edit' (sudoedit flags) if string match -q -- "-e" "$arg"; or string match -q -- "--edit" "$arg" - set --local sudo_has_sudoedit_flags "yes" + set --function sudo_has_sudoedit_flags "yes" break end # Check if argument is neither an option nor a key-value pair diff --git a/src/stb/stb_image.h b/src/stb/stb_image.h index 5e807a0a6e..3ae1815c16 100644 --- a/src/stb/stb_image.h +++ b/src/stb/stb_image.h @@ -4962,7 +4962,7 @@ static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); if (p == NULL) return stbi__err("outofmem", "Out of memory"); - // between here and free(out) below, exitting would leak + // between here and free(out) below, exiting would leak temp_out = p; if (pal_img_n == 3) { diff --git a/src/terminal/PageList.zig b/src/terminal/PageList.zig index 260733b945..b838332b0a 100644 --- a/src/terminal/PageList.zig +++ b/src/terminal/PageList.zig @@ -520,6 +520,7 @@ pub fn clone( assert(node.data.capacity.rows >= chunk.end - chunk.start); defer node.data.assertIntegrity(); node.data.size.rows = chunk.end - chunk.start; + node.data.size.cols = chunk.node.data.size.cols; try node.data.cloneFrom( &chunk.node.data, chunk.start, diff --git a/src/terminal/Parser.zig b/src/terminal/Parser.zig index 9aebdbd3a5..a779c3350e 100644 --- a/src/terminal/Parser.zig +++ b/src/terminal/Parser.zig @@ -6,6 +6,7 @@ const Parser = @This(); const std = @import("std"); const builtin = @import("builtin"); +const assert = std.debug.assert; const testing = std.testing; const table = @import("parse_table.zig").table; const osc = @import("osc.zig"); @@ -81,11 +82,15 @@ pub const Action = union(enum) { pub const CSI = struct { intermediates: []u8, params: []u16, + params_sep: SepList, final: u8, - sep: Sep, + + /// The list of separators used for CSI params. The value of the + /// bit can be mapped to Sep. + pub const SepList = std.StaticBitSet(MAX_PARAMS); /// The separator used for CSI params. - pub const Sep = enum { semicolon, colon }; + pub const Sep = enum(u1) { semicolon = 0, colon = 1 }; // Implement formatter for logging pub fn format( @@ -183,15 +188,6 @@ pub const Action = union(enum) { } }; -/// Keeps track of the parameter sep used for CSI params. We allow colons -/// to be used ONLY by the 'm' CSI action. -pub const ParamSepState = enum(u8) { - none = 0, - semicolon = ';', - colon = ':', - mixed = 1, -}; - /// Maximum number of intermediate characters during parsing. This is /// 4 because we also use the intermediates array for UTF8 decoding which /// can be at most 4 bytes. @@ -207,8 +203,8 @@ intermediates_idx: u8 = 0, /// Param tracking, building params: [MAX_PARAMS]u16 = undefined, +params_sep: Action.CSI.SepList = Action.CSI.SepList.initEmpty(), params_idx: u8 = 0, -params_sep: ParamSepState = .none, param_acc: u16 = 0, param_acc_idx: u8 = 0, @@ -312,13 +308,9 @@ fn doAction(self: *Parser, action: TransitionAction, c: u8) ?Action { // Ignore too many parameters if (self.params_idx >= MAX_PARAMS) break :param null; - // If this is our first time seeing a parameter, we track - // the separator used so that we can't mix separators later. - if (self.params_idx == 0) self.params_sep = @enumFromInt(c); - if (@as(ParamSepState, @enumFromInt(c)) != self.params_sep) self.params_sep = .mixed; - // Set param final value self.params[self.params_idx] = self.param_acc; + if (c == ':') self.params_sep.set(self.params_idx); self.params_idx += 1; // Reset current param value to 0 @@ -359,29 +351,18 @@ fn doAction(self: *Parser, action: TransitionAction, c: u8) ?Action { .csi_dispatch = .{ .intermediates = self.intermediates[0..self.intermediates_idx], .params = self.params[0..self.params_idx], + .params_sep = self.params_sep, .final = c, - .sep = switch (self.params_sep) { - .none, .semicolon => .semicolon, - .colon => .colon, - - // There is nothing that treats mixed separators specially - // afaik so we just treat it as a semicolon. - .mixed => .semicolon, - }, }, }; // We only allow colon or mixed separators for the 'm' command. - switch (self.params_sep) { - .none => {}, - .semicolon => {}, - .colon, .mixed => if (c != 'm') { - log.warn( - "CSI colon or mixed separators only allowed for 'm' command, got: {}", - .{result}, - ); - break :csi_dispatch null; - }, + if (c != 'm' and self.params_sep.count() > 0) { + log.warn( + "CSI colon or mixed separators only allowed for 'm' command, got: {}", + .{result}, + ); + break :csi_dispatch null; } break :csi_dispatch result; @@ -400,7 +381,7 @@ fn doAction(self: *Parser, action: TransitionAction, c: u8) ?Action { pub fn clear(self: *Parser) void { self.intermediates_idx = 0; self.params_idx = 0; - self.params_sep = .none; + self.params_sep = Action.CSI.SepList.initEmpty(); self.param_acc = 0; self.param_acc_idx = 0; } @@ -507,10 +488,11 @@ test "csi: SGR ESC [ 38 : 2 m" { const d = a[1].?.csi_dispatch; try testing.expect(d.final == 'm'); - try testing.expect(d.sep == .colon); try testing.expect(d.params.len == 2); try testing.expectEqual(@as(u16, 38), d.params[0]); + try testing.expect(d.params_sep.isSet(0)); try testing.expectEqual(@as(u16, 2), d.params[1]); + try testing.expect(!d.params_sep.isSet(1)); } } @@ -581,13 +563,17 @@ test "csi: SGR ESC [ 48 : 2 m" { const d = a[1].?.csi_dispatch; try testing.expect(d.final == 'm'); - try testing.expect(d.sep == .colon); try testing.expect(d.params.len == 5); try testing.expectEqual(@as(u16, 48), d.params[0]); + try testing.expect(d.params_sep.isSet(0)); try testing.expectEqual(@as(u16, 2), d.params[1]); + try testing.expect(d.params_sep.isSet(1)); try testing.expectEqual(@as(u16, 240), d.params[2]); + try testing.expect(d.params_sep.isSet(2)); try testing.expectEqual(@as(u16, 143), d.params[3]); + try testing.expect(d.params_sep.isSet(3)); try testing.expectEqual(@as(u16, 104), d.params[4]); + try testing.expect(!d.params_sep.isSet(4)); } } @@ -608,10 +594,11 @@ test "csi: SGR ESC [4:3m colon" { const d = a[1].?.csi_dispatch; try testing.expect(d.final == 'm'); - try testing.expect(d.sep == .colon); try testing.expect(d.params.len == 2); try testing.expectEqual(@as(u16, 4), d.params[0]); + try testing.expect(d.params_sep.isSet(0)); try testing.expectEqual(@as(u16, 3), d.params[1]); + try testing.expect(!d.params_sep.isSet(1)); } } @@ -634,14 +621,71 @@ test "csi: SGR with many blank and colon" { const d = a[1].?.csi_dispatch; try testing.expect(d.final == 'm'); - try testing.expect(d.sep == .colon); try testing.expect(d.params.len == 6); try testing.expectEqual(@as(u16, 58), d.params[0]); + try testing.expect(d.params_sep.isSet(0)); try testing.expectEqual(@as(u16, 2), d.params[1]); + try testing.expect(d.params_sep.isSet(1)); try testing.expectEqual(@as(u16, 0), d.params[2]); + try testing.expect(d.params_sep.isSet(2)); try testing.expectEqual(@as(u16, 240), d.params[3]); + try testing.expect(d.params_sep.isSet(3)); try testing.expectEqual(@as(u16, 143), d.params[4]); + try testing.expect(d.params_sep.isSet(4)); try testing.expectEqual(@as(u16, 104), d.params[5]); + try testing.expect(!d.params_sep.isSet(5)); + } +} + +// This is from a Kakoune actual SGR sequence. +test "csi: SGR mixed colon and semicolon with blank" { + var p = init(); + _ = p.next(0x1B); + for ("[;4:3;38;2;175;175;215;58:2::190:80:70") |c| { + const a = p.next(c); + try testing.expect(a[0] == null); + try testing.expect(a[1] == null); + try testing.expect(a[2] == null); + } + + { + const a = p.next('m'); + try testing.expect(p.state == .ground); + try testing.expect(a[0] == null); + try testing.expect(a[1].? == .csi_dispatch); + try testing.expect(a[2] == null); + + const d = a[1].?.csi_dispatch; + try testing.expect(d.final == 'm'); + try testing.expectEqual(14, d.params.len); + try testing.expectEqual(@as(u16, 0), d.params[0]); + try testing.expect(!d.params_sep.isSet(0)); + try testing.expectEqual(@as(u16, 4), d.params[1]); + try testing.expect(d.params_sep.isSet(1)); + try testing.expectEqual(@as(u16, 3), d.params[2]); + try testing.expect(!d.params_sep.isSet(2)); + try testing.expectEqual(@as(u16, 38), d.params[3]); + try testing.expect(!d.params_sep.isSet(3)); + try testing.expectEqual(@as(u16, 2), d.params[4]); + try testing.expect(!d.params_sep.isSet(4)); + try testing.expectEqual(@as(u16, 175), d.params[5]); + try testing.expect(!d.params_sep.isSet(5)); + try testing.expectEqual(@as(u16, 175), d.params[6]); + try testing.expect(!d.params_sep.isSet(6)); + try testing.expectEqual(@as(u16, 215), d.params[7]); + try testing.expect(!d.params_sep.isSet(7)); + try testing.expectEqual(@as(u16, 58), d.params[8]); + try testing.expect(d.params_sep.isSet(8)); + try testing.expectEqual(@as(u16, 2), d.params[9]); + try testing.expect(d.params_sep.isSet(9)); + try testing.expectEqual(@as(u16, 0), d.params[10]); + try testing.expect(d.params_sep.isSet(10)); + try testing.expectEqual(@as(u16, 190), d.params[11]); + try testing.expect(d.params_sep.isSet(11)); + try testing.expectEqual(@as(u16, 80), d.params[12]); + try testing.expect(d.params_sep.isSet(12)); + try testing.expectEqual(@as(u16, 70), d.params[13]); + try testing.expect(!d.params_sep.isSet(13)); } } diff --git a/src/terminal/Screen.zig b/src/terminal/Screen.zig index eb70d32d07..273e1aebec 100644 --- a/src/terminal/Screen.zig +++ b/src/terminal/Screen.zig @@ -278,12 +278,9 @@ pub fn reset(self: *Screen) void { .page_cell = cursor_rac.cell, }; - // Clear kitty graphics - self.kitty_images.delete( - self.alloc, - undefined, // All image deletion doesn't need the terminal - .{ .all = true }, - ); + // Reset kitty graphics storage + self.kitty_images.deinit(self.alloc, self); + self.kitty_images = .{ .dirty = true }; // Reset our basic state self.saved_cursor = null; @@ -474,26 +471,42 @@ pub fn adjustCapacity( const new_node = try self.pages.adjustCapacity(node, adjustment); const new_page: *Page = &new_node.data; - // All additions below have unreachable catches because when - // we adjust cap we should have enough memory to fit the - // existing data. - - // Re-add the style + // Re-add the style, if the page somehow doesn't have enough + // memory to add it, we emit a warning and gracefully degrade + // to the default style for the cursor. if (self.cursor.style_id != 0) { self.cursor.style_id = new_page.styles.add( new_page.memory, self.cursor.style, - ) catch unreachable; + ) catch |err| id: { + // TODO: Should we increase the capacity further in this case? + log.warn( + "(Screen.adjustCapacity) Failed to add cursor style back to page, err={}", + .{err}, + ); + + // Reset the cursor style. + self.cursor.style = .{}; + break :id style.default_id; + }; } - // Re-add the hyperlink + // Re-add the hyperlink, if the page somehow doesn't have enough + // memory to add it, we emit a warning and gracefully degrade to + // no hyperlink. if (self.cursor.hyperlink) |link| { // So we don't attempt to free any memory in the replaced page. self.cursor.hyperlink_id = 0; self.cursor.hyperlink = null; // Re-add - self.startHyperlinkOnce(link.*) catch unreachable; + self.startHyperlinkOnce(link.*) catch |err| { + // TODO: Should we increase the capacity further in this case? + log.warn( + "(Screen.adjustCapacity) Failed to add cursor hyperlink back to page, err={}", + .{err}, + ); + }; // Remove our old link link.deinit(self.alloc); @@ -1003,8 +1016,9 @@ pub fn cursorCopy(self: *Screen, other: Cursor, opts: struct { /// Always use this to write to cursor.page_pin.*. /// /// This specifically handles the case when the new pin is on a different -/// page than the old AND we have a style set. In that case, we must release -/// our old style and upsert our new style since styles are stored per-page. +/// page than the old AND we have a style or hyperlink set. In that case, +/// we must release our old one and insert the new one, since styles are +/// stored per-page. fn cursorChangePin(self: *Screen, new: Pin) void { // Moving the cursor affects text run splitting (ligatures) so // we must mark the old and new page dirty. We do this as long @@ -1576,6 +1590,18 @@ fn resizeInternal( self.cursor.hyperlink = null; } + // We need to insert a tracked pin for our saved cursor so we can + // modify its X/Y for reflow. + const saved_cursor_pin: ?*Pin = saved_cursor: { + const sc = self.saved_cursor orelse break :saved_cursor null; + const pin = self.pages.pin(.{ .active = .{ + .x = sc.x, + .y = sc.y, + } }) orelse break :saved_cursor null; + break :saved_cursor try self.pages.trackPin(pin); + }; + defer if (saved_cursor_pin) |p| self.pages.untrackPin(p); + // Perform the resize operation. try self.pages.resize(.{ .rows = rows, @@ -1595,6 +1621,36 @@ fn resizeInternal( // state is correct. self.cursorReload(); + // If we reflowed a saved cursor, update it. + if (saved_cursor_pin) |p| { + // This should never fail because a non-null saved_cursor_pin + // implies a non-null saved_cursor. + const sc = &self.saved_cursor.?; + if (self.pages.pointFromPin(.active, p.*)) |pt| { + sc.x = @intCast(pt.active.x); + sc.y = @intCast(pt.active.y); + + // If we had pending wrap set and we're no longer at the end of + // the line, we unset the pending wrap and move the cursor to + // reflect the correct next position. + if (sc.pending_wrap and sc.x != cols - 1) { + sc.pending_wrap = false; + sc.x += 1; + } + } else { + // I think this can happen if the screen is resized to be + // less rows or less cols and our saved cursor moves outside + // the active area. In this case, there isn't anything really + // reasonable we can do so we just move the cursor to the + // top-left. It may be reasonable to also move the cursor to + // match the primary cursor. Any behavior is fine since this is + // totally unspecified. + sc.x = 0; + sc.y = 0; + sc.pending_wrap = false; + } + } + // Fix up our hyperlink if we had one. if (hyperlink_) |link| { self.startHyperlink(link.uri, switch (link.id) { @@ -1986,9 +2042,40 @@ pub fn cursorSetHyperlink(self: *Screen) !void { } else |err| switch (err) { // hyperlink_map is out of space, realloc the page to be larger error.HyperlinkMapOutOfMemory => { + const uri_size = if (self.cursor.hyperlink) |link| link.uri.len else 0; + + var string_bytes = page.capacity.string_bytes; + + // Attempt to allocate the space that would be required to + // insert a new copy of the cursor hyperlink uri in to the + // string alloc, since right now adjustCapacity always just + // adds an extra copy even if one already exists in the page. + // If this alloc fails then we know we also need to grow our + // string bytes. + // + // FIXME: This SUCKS + if (page.string_alloc.alloc( + u8, + page.memory, + uri_size, + )) |slice| { + // We don't bother freeing because we're + // about to free the entire page anyway. + _ = &slice; + } else |_| { + // We didn't have enough room, let's just double our + // string bytes until there's definitely enough room + // for our uri. + const before = string_bytes; + while (string_bytes - before < uri_size) string_bytes *= 2; + } + _ = try self.adjustCapacity( self.cursor.page_pin.node, - .{ .hyperlink_bytes = page.capacity.hyperlink_bytes * 2 }, + .{ + .hyperlink_bytes = page.capacity.hyperlink_bytes * 2, + .string_bytes = string_bytes, + }, ); // Retry @@ -2591,13 +2678,36 @@ pub fn selectOutput(self: *Screen, pin: Pin) ?Selection { const start: Pin = boundary: { var it = pin.rowIterator(.left_up, null); var it_prev = pin; + + // First, iterate until we find the first line of command output while (it.next()) |p| { + it_prev = p; const row = p.rowAndCell().row; switch (row.semantic_prompt) { - .command => break :boundary p, - else => {}, + .command => break, + + .unknown, + .prompt, + .prompt_continuation, + .input, + => {}, } + } + // Because the first line of command output may span multiple visual rows we must now + // iterate until we find the first row of anything other than command output and then + // yield the previous row. + while (it.next()) |p| { + const row = p.rowAndCell().row; + switch (row.semantic_prompt) { + .command => {}, + + .unknown, + .prompt, + .prompt_continuation, + .input, + => break :boundary it_prev, + } it_prev = p; } @@ -2859,6 +2969,9 @@ pub fn testWriteString(self: *Screen, text: []const u8) !void { .protected = self.cursor.protected, }; + // If we have a hyperlink, add it to the cell. + if (self.cursor.hyperlink_id > 0) try self.cursorSetHyperlink(); + // If we have a ref-counted style, increase. if (self.cursor.style_id != style.default_id) { const page = self.cursor.page_pin.node.data; @@ -2877,6 +2990,9 @@ pub fn testWriteString(self: *Screen, text: []const u8) !void { .protected = self.cursor.protected, }; + // If we have a hyperlink, add it to the cell. + if (self.cursor.hyperlink_id > 0) try self.cursorSetHyperlink(); + self.cursor.page_row.wrap = true; try self.cursorDownOrScroll(); self.cursorHorizontalAbsolute(0); @@ -2892,6 +3008,9 @@ pub fn testWriteString(self: *Screen, text: []const u8) !void { .protected = self.cursor.protected, }; + // If we have a hyperlink, add it to the cell. + if (self.cursor.hyperlink_id > 0) try self.cursorSetHyperlink(); + // Write our tail self.cursorRight(1); self.cursor.page_cell.* = .{ @@ -2901,6 +3020,9 @@ pub fn testWriteString(self: *Screen, text: []const u8) !void { .protected = self.cursor.protected, }; + // If we have a hyperlink, add it to the cell. + if (self.cursor.hyperlink_id > 0) try self.cursorSetHyperlink(); + // If we have a ref-counted style, increase twice. if (self.cursor.style_id != style.default_id) { const page = self.cursor.page_pin.node.data; @@ -7641,17 +7763,17 @@ test "Screen: selectOutput" { // zig fmt: off { - // line number: - try s.testWriteString("output1\n"); // 0 - try s.testWriteString("output1\n"); // 1 - try s.testWriteString("prompt2\n"); // 2 - try s.testWriteString("input2\n"); // 3 - try s.testWriteString("output2\n"); // 4 - try s.testWriteString("output2\n"); // 5 - try s.testWriteString("prompt3$ input3\n"); // 6 - try s.testWriteString("output3\n"); // 7 - try s.testWriteString("output3\n"); // 8 - try s.testWriteString("output3"); // 9 + // line number: + try s.testWriteString("output1\n"); // 0 + try s.testWriteString("output1\n"); // 1 + try s.testWriteString("prompt2\n"); // 2 + try s.testWriteString("input2\n"); // 3 + try s.testWriteString("output2output2output2output2\n"); // 4, 5, 6 due to overflow + try s.testWriteString("output2\n"); // 7 + try s.testWriteString("prompt3$ input3\n"); // 8 + try s.testWriteString("output3\n"); // 9 + try s.testWriteString("output3\n"); // 10 + try s.testWriteString("output3"); // 11 } // zig fmt: on @@ -7670,13 +7792,23 @@ test "Screen: selectOutput" { const row = pin.rowAndCell().row; row.semantic_prompt = .command; } + { + const pin = s.pages.pin(.{ .screen = .{ .y = 5 } }).?; + const row = pin.rowAndCell().row; + row.semantic_prompt = .command; + } { const pin = s.pages.pin(.{ .screen = .{ .y = 6 } }).?; const row = pin.rowAndCell().row; + row.semantic_prompt = .command; + } + { + const pin = s.pages.pin(.{ .screen = .{ .y = 8 } }).?; + const row = pin.rowAndCell().row; row.semantic_prompt = .input; } { - const pin = s.pages.pin(.{ .screen = .{ .y = 7 } }).?; + const pin = s.pages.pin(.{ .screen = .{ .y = 9 } }).?; const row = pin.rowAndCell().row; row.semantic_prompt = .command; } @@ -7701,7 +7833,7 @@ test "Screen: selectOutput" { { var sel = s.selectOutput(s.pages.pin(.{ .active = .{ .x = 3, - .y = 5, + .y = 7, } }).?).?; defer sel.deinit(&s); try testing.expectEqual(point.Point{ .active = .{ @@ -7710,23 +7842,23 @@ test "Screen: selectOutput" { } }, s.pages.pointFromPin(.active, sel.start()).?); try testing.expectEqual(point.Point{ .active = .{ .x = 9, - .y = 5, + .y = 7, } }, s.pages.pointFromPin(.active, sel.end()).?); } // No end marker, should select till the end { var sel = s.selectOutput(s.pages.pin(.{ .active = .{ .x = 2, - .y = 7, + .y = 10, } }).?).?; defer sel.deinit(&s); try testing.expectEqual(point.Point{ .active = .{ .x = 0, - .y = 7, + .y = 9, } }, s.pages.pointFromPin(.active, sel.start()).?); try testing.expectEqual(point.Point{ .active = .{ .x = 9, - .y = 10, + .y = 12, } }, s.pages.pointFromPin(.active, sel.end()).?); } // input / prompt at y = 0, pt.y = 0 @@ -8692,6 +8824,40 @@ test "Screen: hyperlink cursor state on resize" { } } +test "Screen: cursorSetHyperlink OOM + URI too large for string alloc" { + const testing = std.testing; + const alloc = testing.allocator; + + var s = try init(alloc, 80, 24, 0); + defer s.deinit(); + + // Start a hyperlink with a URI that just barely fits in the string alloc. + // This will ensure that additional string alloc space is needed for the + // redundant copy of the URI when the page is re-alloced. + const uri = "a" ** (pagepkg.std_capacity.string_bytes - 8); + try s.startHyperlink(uri, null); + + // Figure out how many cells should can have hyperlinks in this page, + // and write twice that number, to guarantee the capacity needs to be + // increased at some point. + const base_capacity = s.cursor.page_pin.node.data.hyperlinkCapacity(); + const base_string_bytes = s.cursor.page_pin.node.data.capacity.string_bytes; + for (0..base_capacity * 2) |_| { + try s.cursorSetHyperlink(); + if (s.cursor.x >= s.pages.cols - 1) { + try s.cursorDownOrScroll(); + s.cursorHorizontalAbsolute(0); + } else { + s.cursorRight(1); + } + } + + // Make sure the capacity really did increase. + try testing.expect(base_capacity < s.cursor.page_pin.node.data.hyperlinkCapacity()); + // And that our string_bytes increased as well. + try testing.expect(base_string_bytes < s.cursor.page_pin.node.data.capacity.string_bytes); +} + test "Screen: adjustCapacity cursor style ref count" { const testing = std.testing; const alloc = testing.allocator; @@ -8726,6 +8892,102 @@ test "Screen: adjustCapacity cursor style ref count" { } } +test "Screen: adjustCapacity cursor hyperlink exceeds string alloc size" { + const testing = std.testing; + const alloc = testing.allocator; + + var s = try init(alloc, 80, 24, 0); + defer s.deinit(); + + // Start a hyperlink with a URI that just barely fits in the string alloc. + // This will ensure that the redundant copy added in `adjustCapacity` won't + // fit in the available string alloc space. + const uri = "a" ** (pagepkg.std_capacity.string_bytes - 8); + try s.startHyperlink(uri, null); + + // Write some characters with this so that the URI + // is copied to the new page when adjusting capacity. + try s.testWriteString("Hello"); + + // Adjust the capacity, right now this will cause a redundant copy of + // the URI to be added to the string alloc, but since there isn't room + // for this this will clear the cursor hyperlink. + _ = try s.adjustCapacity(s.cursor.page_pin.node, .{}); + + // The cursor hyperlink should have been cleared by the `adjustCapacity` + // call, because there isn't enough room to add the redundant URI string. + // + // This behavior will change, causing this test to fail, if any of these + // changes are made: + // + // - The string alloc is changed to intern strings. + // + // - The adjustCapacity function is changed to ensure the new + // capacity will fit the redundant copy of the hyperlink uri. + // + // - The cursor managed memory handling is reworked so that it + // doesn't reside in the pages anymore and doesn't need this + // accounting. + // + // In such a case, adjust this test accordingly. + try testing.expectEqual(null, s.cursor.hyperlink); + try testing.expectEqual(0, s.cursor.hyperlink_id); +} + +test "Screen: adjustCapacity cursor style exceeds style set capacity" { + const testing = std.testing; + const alloc = testing.allocator; + + var s = try init(alloc, 80, 24, 1000); + defer s.deinit(); + + const page = &s.cursor.page_pin.node.data; + + // We add unique styles to the page until no more will fit. + fill: for (0..255) |bg| { + for (0..255) |fg| { + const st: style.Style = .{ + .bg_color = .{ .palette = @intCast(bg) }, + .fg_color = .{ .palette = @intCast(fg) }, + }; + + s.cursor.style = st; + + // Try to insert the new style, if it doesn't fit then + // we succeeded in filling the style set, so we break. + s.cursor.style_id = page.styles.add( + page.memory, + s.cursor.style, + ) catch break :fill; + + try s.testWriteString("a"); + } + } + + // Adjust the capacity, this should cause the style set to reach the + // same state it was in to begin with, since it will clone the page + // in the same order as the styles were added to begin with, meaning + // the cursor style will not be able to be added to the set, which + // should, right now, result in the cursor style being cleared. + _ = try s.adjustCapacity(s.cursor.page_pin.node, .{}); + + // The cursor style should have been cleared by the `adjustCapacity`. + // + // This behavior will change, causing this test to fail, if either + // of these changes are made: + // + // - The adjustCapacity function is changed to ensure the + // new capacity will definitely fit the cursor style. + // + // - The cursor managed memory handling is reworked so that it + // doesn't reside in the pages anymore and doesn't need this + // accounting. + // + // In such a case, adjust this test accordingly. + try testing.expect(s.cursor.style.default()); + try testing.expectEqual(style.default_id, s.cursor.style_id); +} + test "Screen UTF8 cell map with newlines" { const testing = std.testing; const alloc = testing.allocator; diff --git a/src/terminal/Terminal.zig b/src/terminal/Terminal.zig index 65476108d7..bec0a24a23 100644 --- a/src/terminal/Terminal.zig +++ b/src/terminal/Terminal.zig @@ -10708,6 +10708,87 @@ test "Terminal: resize with high unique style per cell with wrapping" { try t.resize(alloc, 60, 30); } +test "Terminal: resize with reflow and saved cursor" { + const alloc = testing.allocator; + var t = try init(alloc, .{ .cols = 2, .rows = 3 }); + defer t.deinit(alloc); + try t.printString("1A2B"); + t.setCursorPos(2, 2); + { + const list_cell = t.screen.pages.getCell(.{ .active = .{ + .x = t.screen.cursor.x, + .y = t.screen.cursor.y, + } }).?; + const cell = list_cell.cell; + try testing.expectEqual(@as(u32, 'B'), cell.content.codepoint); + } + + { + const str = try t.plainString(testing.allocator); + defer testing.allocator.free(str); + try testing.expectEqualStrings("1A\n2B", str); + } + + t.saveCursor(); + try t.resize(alloc, 5, 3); + try t.restoreCursor(); + + { + const str = try t.plainString(testing.allocator); + defer testing.allocator.free(str); + try testing.expectEqualStrings("1A2B", str); + } + + // Verify our cursor is still in the same place + { + const list_cell = t.screen.pages.getCell(.{ .active = .{ + .x = t.screen.cursor.x, + .y = t.screen.cursor.y, + } }).?; + const cell = list_cell.cell; + try testing.expectEqual(@as(u32, 'B'), cell.content.codepoint); + } +} + +test "Terminal: resize with reflow and saved cursor pending wrap" { + const alloc = testing.allocator; + var t = try init(alloc, .{ .cols = 2, .rows = 3 }); + defer t.deinit(alloc); + try t.printString("1A2B"); + { + const list_cell = t.screen.pages.getCell(.{ .active = .{ + .x = t.screen.cursor.x, + .y = t.screen.cursor.y, + } }).?; + const cell = list_cell.cell; + try testing.expectEqual(@as(u32, 'B'), cell.content.codepoint); + } + + { + const str = try t.plainString(testing.allocator); + defer testing.allocator.free(str); + try testing.expectEqualStrings("1A\n2B", str); + } + + t.saveCursor(); + try t.resize(alloc, 5, 3); + try t.restoreCursor(); + + { + const str = try t.plainString(testing.allocator); + defer testing.allocator.free(str); + try testing.expectEqualStrings("1A2B", str); + } + + // Pending wrap should be reset + try t.print('X'); + { + const str = try t.plainString(testing.allocator); + defer testing.allocator.free(str); + try testing.expectEqualStrings("1A2BX", str); + } +} + test "Terminal: DECCOLM without DEC mode 40" { const alloc = testing.allocator; var t = try init(alloc, .{ .rows = 5, .cols = 5 }); diff --git a/src/terminal/hyperlink.zig b/src/terminal/hyperlink.zig index 1ab3c5ea74..bb9e78ca65 100644 --- a/src/terminal/hyperlink.zig +++ b/src/terminal/hyperlink.zig @@ -194,14 +194,24 @@ pub const Set = RefCountedSet( Id, size.CellCountInt, struct { + /// The page which holds the strings for items in this set. page: ?*Page = null, + /// The page which holds the strings for items + /// looked up with, e.g., `add` or `lookup`, + /// if different from the destination page. + src_page: ?*const Page = null, + pub fn hash(self: *const @This(), link: PageEntry) u64 { - return link.hash(self.page.?.memory); + return link.hash((self.src_page orelse self.page.?).memory); } pub fn eql(self: *const @This(), a: PageEntry, b: PageEntry) bool { - return a.eql(self.page.?.memory, &b, self.page.?.memory); + return a.eql( + (self.src_page orelse self.page.?).memory, + &b, + self.page.?.memory, + ); } pub fn deleted(self: *const @This(), link: PageEntry) void { diff --git a/src/terminal/osc.zig b/src/terminal/osc.zig index 33d753c9f3..faf376d13d 100644 --- a/src/terminal/osc.zig +++ b/src/terminal/osc.zig @@ -178,6 +178,9 @@ pub const Command = union(enum) { progress: ?u8 = null, }, + /// Wait input (OSC 9;5) + wait_input: void, + pub const ColorKind = union(enum) { palette: u8, foreground, @@ -269,6 +272,9 @@ pub const Parser = struct { // Maximum length of a single OSC command. This is the full OSC command // sequence length (excluding ESC ]). This is arbitrary, I couldn't find // any definitive resource on how long this should be. + // + // NOTE: This does mean certain OSC sequences such as OSC 8 (hyperlinks) + // won't work if their parameters are larger than fit in the buffer. const MAX_BUF = 2048; pub const State = enum { @@ -422,9 +428,23 @@ pub const Parser = struct { /// Consume the next character c and advance the parser state. pub fn next(self: *Parser, c: u8) void { - // If our buffer is full then we're invalid. + // If our buffer is full then we're invalid, so we set our state + // accordingly and indicate the sequence is incomplete so that we + // don't accidentally issue a command when ending. if (self.buf_idx >= self.buf.len) { + if (self.state != .invalid) { + log.warn( + "OSC sequence too long (> {d}), ignoring. state={}", + .{ self.buf.len, self.state }, + ); + } + self.state = .invalid; + + // We have to do this here because it will never reach the + // switch statement below, since our buf_idx will always be + // too high after this. + self.complete = false; return; } @@ -811,6 +831,11 @@ pub const Parser = struct { '4' => { self.state = .conemu_progress_prestate; }, + '5' => { + self.state = .swallow; + self.command = .{ .wait_input = {} }; + self.complete = true; + }, // Todo: parse out other ConEmu operating system commands. // Even if we don't support them we probably don't want @@ -1635,10 +1660,11 @@ test "OSC: longer than buffer" { var p: Parser = .{}; - const input = "a" ** (Parser.MAX_BUF + 2); + const input = "0;" ++ "a" ** (Parser.MAX_BUF + 2); for (input) |ch| p.next(ch); try testing.expect(p.end(null) == null); + try testing.expect(p.complete == false); } test "OSC: report default foreground color" { @@ -2096,6 +2122,30 @@ test "OSC: OSC9 progress pause with progress" { try testing.expect(cmd.progress.progress == 100); } +test "OSC: OSC9 conemu wait input" { + const testing = std.testing; + + var p: Parser = .{}; + + const input = "9;5"; + for (input) |ch| p.next(ch); + + const cmd = p.end('\x1b').?; + try testing.expect(cmd == .wait_input); +} + +test "OSC: OSC9 conemu wait ignores trailing characters" { + const testing = std.testing; + + var p: Parser = .{}; + + const input = "9;5;foo"; + for (input) |ch| p.next(ch); + + const cmd = p.end('\x1b').?; + try testing.expect(cmd == .wait_input); +} + test "OSC: empty param" { const testing = std.testing; diff --git a/src/terminal/page.zig b/src/terminal/page.zig index ae14b8c016..30f6658aa5 100644 --- a/src/terminal/page.zig +++ b/src/terminal/page.zig @@ -821,11 +821,7 @@ pub const Page = struct { if (self.hyperlink_set.lookupContext( self.memory, other_link.*, - - // `lookupContext` uses the context for hashing, and - // that doesn't write to the page, so this constCast - // is completely safe. - .{ .page = @constCast(other) }, + .{ .page = self, .src_page = @constCast(other) }, )) |i| { self.hyperlink_set.use(self.memory, i); break :dst_id i; diff --git a/src/terminal/ref_counted_set.zig b/src/terminal/ref_counted_set.zig index 1a58a4e5b5..b674295dcb 100644 --- a/src/terminal/ref_counted_set.zig +++ b/src/terminal/ref_counted_set.zig @@ -38,8 +38,14 @@ const fastmem = @import("../fastmem.zig"); /// /// `Context` /// A type containing methods to define behaviors. +/// /// - `fn hash(*Context, T) u64` - Return a hash for an item. +/// /// - `fn eql(*Context, T, T) bool` - Check two items for equality. +/// The first of the two items passed in is guaranteed to be from +/// a value passed in to an `add` or `lookup` function, the second +/// is guaranteed to be a value already resident in the set. +/// /// - `fn deleted(*Context, T) void` - [OPTIONAL] Deletion callback. /// If present, called whenever an item is finally deleted. /// Useful if the item has memory that needs to be freed. diff --git a/src/terminal/sgr.zig b/src/terminal/sgr.zig index cdf39657bd..52bfb2c31a 100644 --- a/src/terminal/sgr.zig +++ b/src/terminal/sgr.zig @@ -1,13 +1,17 @@ //! SGR (Select Graphic Rendition) attrinvbute parsing and types. const std = @import("std"); +const assert = std.debug.assert; const testing = std.testing; const color = @import("color.zig"); +const SepList = @import("Parser.zig").Action.CSI.SepList; /// Attribute type for SGR pub const Attribute = union(enum) { + pub const Tag = std.meta.FieldEnum(Attribute); + /// Unset all attributes - unset: void, + unset, /// Unknown attribute, the raw CSI command parameters are here. unknown: struct { @@ -19,43 +23,43 @@ pub const Attribute = union(enum) { }, /// Bold the text. - bold: void, - reset_bold: void, + bold, + reset_bold, /// Italic text. - italic: void, - reset_italic: void, + italic, + reset_italic, /// Faint/dim text. /// Note: reset faint is the same SGR code as reset bold - faint: void, + faint, /// Underline the text underline: Underline, - reset_underline: void, + reset_underline, underline_color: color.RGB, @"256_underline_color": u8, - reset_underline_color: void, + reset_underline_color, // Overline the text - overline: void, - reset_overline: void, + overline, + reset_overline, /// Blink the text - blink: void, - reset_blink: void, + blink, + reset_blink, /// Invert fg/bg colors. - inverse: void, - reset_inverse: void, + inverse, + reset_inverse, /// Invisible - invisible: void, - reset_invisible: void, + invisible, + reset_invisible, /// Strikethrough the text. - strikethrough: void, - reset_strikethrough: void, + strikethrough, + reset_strikethrough, /// Set foreground color as RGB values. direct_color_fg: color.RGB, @@ -68,8 +72,8 @@ pub const Attribute = union(enum) { @"8_fg": color.Name, /// Reset the fg/bg to their default values. - reset_fg: void, - reset_bg: void, + reset_fg, + reset_bg, /// Set the background/foreground as a named bright color attribute. @"8_bright_bg": color.Name, @@ -94,11 +98,9 @@ pub const Attribute = union(enum) { /// Parser parses the attributes from a list of SGR parameters. pub const Parser = struct { params: []const u16, + params_sep: SepList = SepList.initEmpty(), idx: usize = 0, - /// True if the separator is a colon - colon: bool = false, - /// Next returns the next attribute or null if there are no more attributes. pub fn next(self: *Parser) ?Attribute { if (self.idx > self.params.len) return null; @@ -106,220 +108,261 @@ pub const Parser = struct { // Implicitly means unset if (self.params.len == 0) { self.idx += 1; - return Attribute{ .unset = {} }; + return .unset; } const slice = self.params[self.idx..self.params.len]; + const colon = self.params_sep.isSet(self.idx); self.idx += 1; // Our last one will have an idx be the last value. if (slice.len == 0) return null; + // If we have a colon separator then we need to ensure we're + // parsing a value that allows it. + if (colon) switch (slice[0]) { + 4, 38, 48, 58 => {}, + + else => { + // Consume all the colon separated values. + const start = self.idx; + while (self.params_sep.isSet(self.idx)) self.idx += 1; + self.idx += 1; + return .{ .unknown = .{ + .full = self.params, + .partial = slice[0 .. self.idx - start + 1], + } }; + }, + }; + switch (slice[0]) { - 0 => return Attribute{ .unset = {} }, - - 1 => return Attribute{ .bold = {} }, - - 2 => return Attribute{ .faint = {} }, - - 3 => return Attribute{ .italic = {} }, - - 4 => blk: { - if (self.colon) { - switch (slice.len) { - // 0 is unreachable because we're here and we read - // an element to get here. - 0 => unreachable, - - // 1 is possible if underline is the last element. - 1 => return Attribute{ .underline = .single }, - - // 2 means we have a specific underline style. - 2 => { - self.idx += 1; - switch (slice[1]) { - 0 => return Attribute{ .reset_underline = {} }, - 1 => return Attribute{ .underline = .single }, - 2 => return Attribute{ .underline = .double }, - 3 => return Attribute{ .underline = .curly }, - 4 => return Attribute{ .underline = .dotted }, - 5 => return Attribute{ .underline = .dashed }, - - // For unknown underline styles, just render - // a single underline. - else => return Attribute{ .underline = .single }, - } - }, - - // Colon-separated must only be 2. - else => break :blk, + 0 => return .unset, + + 1 => return .bold, + + 2 => return .faint, + + 3 => return .italic, + + 4 => underline: { + if (colon) { + assert(slice.len >= 2); + if (self.isColon()) { + self.consumeUnknownColon(); + break :underline; + } + + self.idx += 1; + switch (slice[1]) { + 0 => return .reset_underline, + 1 => return .{ .underline = .single }, + 2 => return .{ .underline = .double }, + 3 => return .{ .underline = .curly }, + 4 => return .{ .underline = .dotted }, + 5 => return .{ .underline = .dashed }, + + // For unknown underline styles, just render + // a single underline. + else => return .{ .underline = .single }, } } - return Attribute{ .underline = .single }; + return .{ .underline = .single }; }, - 5 => return Attribute{ .blink = {} }, + 5 => return .blink, - 6 => return Attribute{ .blink = {} }, + 6 => return .blink, - 7 => return Attribute{ .inverse = {} }, + 7 => return .inverse, - 8 => return Attribute{ .invisible = {} }, + 8 => return .invisible, - 9 => return Attribute{ .strikethrough = {} }, + 9 => return .strikethrough, - 21 => return Attribute{ .underline = .double }, + 21 => return .{ .underline = .double }, - 22 => return Attribute{ .reset_bold = {} }, + 22 => return .reset_bold, - 23 => return Attribute{ .reset_italic = {} }, + 23 => return .reset_italic, - 24 => return Attribute{ .reset_underline = {} }, + 24 => return .reset_underline, - 25 => return Attribute{ .reset_blink = {} }, + 25 => return .reset_blink, - 27 => return Attribute{ .reset_inverse = {} }, + 27 => return .reset_inverse, - 28 => return Attribute{ .reset_invisible = {} }, + 28 => return .reset_invisible, - 29 => return Attribute{ .reset_strikethrough = {} }, + 29 => return .reset_strikethrough, - 30...37 => return Attribute{ + 30...37 => return .{ .@"8_fg" = @enumFromInt(slice[0] - 30), }, 38 => if (slice.len >= 2) switch (slice[1]) { // `2` indicates direct-color (r, g, b). // We need at least 3 more params for this to make sense. - 2 => if (slice.len >= 5) { - self.idx += 4; - // When a colon separator is used, there may or may not be - // a color space identifier as the third param, which we - // need to ignore (it has no standardized behavior). - const rgb = if (slice.len == 5 or !self.colon) - slice[2..5] - else rgb: { - self.idx += 1; - break :rgb slice[3..6]; - }; + 2 => if (self.parseDirectColor( + .direct_color_fg, + slice, + colon, + )) |v| return v, - // We use @truncate because the value should be 0 to 255. If - // it isn't, the behavior is undefined so we just... truncate it. - return Attribute{ - .direct_color_fg = .{ - .r = @truncate(rgb[0]), - .g = @truncate(rgb[1]), - .b = @truncate(rgb[2]), - }, - }; - }, // `5` indicates indexed color. 5 => if (slice.len >= 3) { self.idx += 2; - return Attribute{ + return .{ .@"256_fg" = @truncate(slice[2]), }; }, else => {}, }, - 39 => return Attribute{ .reset_fg = {} }, + 39 => return .reset_fg, - 40...47 => return Attribute{ + 40...47 => return .{ .@"8_bg" = @enumFromInt(slice[0] - 40), }, 48 => if (slice.len >= 2) switch (slice[1]) { // `2` indicates direct-color (r, g, b). // We need at least 3 more params for this to make sense. - 2 => if (slice.len >= 5) { - self.idx += 4; - // When a colon separator is used, there may or may not be - // a color space identifier as the third param, which we - // need to ignore (it has no standardized behavior). - const rgb = if (slice.len == 5 or !self.colon) - slice[2..5] - else rgb: { - self.idx += 1; - break :rgb slice[3..6]; - }; + 2 => if (self.parseDirectColor( + .direct_color_bg, + slice, + colon, + )) |v| return v, - // We use @truncate because the value should be 0 to 255. If - // it isn't, the behavior is undefined so we just... truncate it. - return Attribute{ - .direct_color_bg = .{ - .r = @truncate(rgb[0]), - .g = @truncate(rgb[1]), - .b = @truncate(rgb[2]), - }, - }; - }, // `5` indicates indexed color. 5 => if (slice.len >= 3) { self.idx += 2; - return Attribute{ + return .{ .@"256_bg" = @truncate(slice[2]), }; }, else => {}, }, - 49 => return Attribute{ .reset_bg = {} }, + 49 => return .reset_bg, - 53 => return Attribute{ .overline = {} }, - 55 => return Attribute{ .reset_overline = {} }, + 53 => return .overline, + 55 => return .reset_overline, 58 => if (slice.len >= 2) switch (slice[1]) { // `2` indicates direct-color (r, g, b). // We need at least 3 more params for this to make sense. - 2 => if (slice.len >= 5) { - self.idx += 4; - // When a colon separator is used, there may or may not be - // a color space identifier as the third param, which we - // need to ignore (it has no standardized behavior). - const rgb = if (slice.len == 5 or !self.colon) - slice[2..5] - else rgb: { - self.idx += 1; - break :rgb slice[3..6]; - }; + 2 => if (self.parseDirectColor( + .underline_color, + slice, + colon, + )) |v| return v, - // We use @truncate because the value should be 0 to 255. If - // it isn't, the behavior is undefined so we just... truncate it. - return Attribute{ - .underline_color = .{ - .r = @truncate(rgb[0]), - .g = @truncate(rgb[1]), - .b = @truncate(rgb[2]), - }, - }; - }, // `5` indicates indexed color. 5 => if (slice.len >= 3) { self.idx += 2; - return Attribute{ + return .{ .@"256_underline_color" = @truncate(slice[2]), }; }, else => {}, }, - 59 => return Attribute{ .reset_underline_color = {} }, + 59 => return .reset_underline_color, - 90...97 => return Attribute{ + 90...97 => return .{ // 82 instead of 90 to offset to "bright" colors .@"8_bright_fg" = @enumFromInt(slice[0] - 82), }, - 100...107 => return Attribute{ + 100...107 => return .{ .@"8_bright_bg" = @enumFromInt(slice[0] - 92), }, else => {}, } - return Attribute{ .unknown = .{ .full = self.params, .partial = slice } }; + return .{ .unknown = .{ .full = self.params, .partial = slice } }; + } + + fn parseDirectColor( + self: *Parser, + comptime tag: Attribute.Tag, + slice: []const u16, + colon: bool, + ) ?Attribute { + // Any direct color style must have at least 5 values. + if (slice.len < 5) return null; + + // Only used for direct color sets (38, 48, 58) and subparam 2. + assert(slice[1] == 2); + + // Note: We use @truncate because the value should be 0 to 255. If + // it isn't, the behavior is undefined so we just... truncate it. + + // If we don't have a colon, then we expect exactly 3 semicolon + // separated values. + if (!colon) { + self.idx += 4; + return @unionInit(Attribute, @tagName(tag), .{ + .r = @truncate(slice[2]), + .g = @truncate(slice[3]), + .b = @truncate(slice[4]), + }); + } + + // We have a colon, we might have either 5 or 6 values depending + // on if the colorspace is present. + const count = self.countColon(); + switch (count) { + 3 => { + self.idx += 4; + return @unionInit(Attribute, @tagName(tag), .{ + .r = @truncate(slice[2]), + .g = @truncate(slice[3]), + .b = @truncate(slice[4]), + }); + }, + + 4 => { + self.idx += 5; + return @unionInit(Attribute, @tagName(tag), .{ + .r = @truncate(slice[3]), + .g = @truncate(slice[4]), + .b = @truncate(slice[5]), + }); + }, + + else => { + self.consumeUnknownColon(); + return null; + }, + } + } + + /// Returns true if the present position has a colon separator. + /// This always returns false for the last value since it has no + /// separator. + fn isColon(self: *Parser) bool { + // The `- 1` here is because the last value has no separator. + if (self.idx >= self.params.len - 1) return false; + return self.params_sep.isSet(self.idx); + } + + fn countColon(self: *Parser) usize { + var count: usize = 0; + var idx = self.idx; + while (idx < self.params.len - 1 and self.params_sep.isSet(idx)) : (idx += 1) { + count += 1; + } + return count; + } + + /// Consumes all the remaining parameters separated by a colon and + /// returns an unknown attribute. + fn consumeUnknownColon(self: *Parser) void { + const count = self.countColon(); + self.idx += count + 1; } }; @@ -329,7 +372,7 @@ fn testParse(params: []const u16) Attribute { } fn testParseColon(params: []const u16) Attribute { - var p: Parser = .{ .params = params, .colon = true }; + var p: Parser = .{ .params = params, .params_sep = SepList.initFull() }; return p.next().?; } @@ -366,6 +409,35 @@ test "sgr: Parser multiple" { try testing.expect(p.next() == null); } +test "sgr: unsupported with colon" { + var p: Parser = .{ + .params = &[_]u16{ 0, 4, 1 }, + .params_sep = sep: { + var list = SepList.initEmpty(); + list.set(0); + break :sep list; + }, + }; + try testing.expect(p.next().? == .unknown); + try testing.expect(p.next().? == .bold); + try testing.expect(p.next() == null); +} + +test "sgr: unsupported with multiple colon" { + var p: Parser = .{ + .params = &[_]u16{ 0, 4, 2, 1 }, + .params_sep = sep: { + var list = SepList.initEmpty(); + list.set(0); + list.set(1); + break :sep list; + }, + }; + try testing.expect(p.next().? == .unknown); + try testing.expect(p.next().? == .bold); + try testing.expect(p.next() == null); +} + test "sgr: bold" { { const v = testParse(&[_]u16{1}); @@ -439,6 +511,37 @@ test "sgr: underline styles" { } } +test "sgr: underline style with more" { + var p: Parser = .{ + .params = &[_]u16{ 4, 2, 1 }, + .params_sep = sep: { + var list = SepList.initEmpty(); + list.set(0); + break :sep list; + }, + }; + + try testing.expect(p.next().? == .underline); + try testing.expect(p.next().? == .bold); + try testing.expect(p.next() == null); +} + +test "sgr: underline style with too many colons" { + var p: Parser = .{ + .params = &[_]u16{ 4, 2, 3, 1 }, + .params_sep = sep: { + var list = SepList.initEmpty(); + list.set(0); + list.set(1); + break :sep list; + }, + }; + + try testing.expect(p.next().? == .unknown); + try testing.expect(p.next().? == .bold); + try testing.expect(p.next() == null); +} + test "sgr: blink" { { const v = testParse(&[_]u16{5}); @@ -592,13 +695,13 @@ test "sgr: underline, bg, and fg" { test "sgr: direct color fg missing color" { // This used to crash - var p: Parser = .{ .params = &[_]u16{ 38, 5 }, .colon = false }; + var p: Parser = .{ .params = &[_]u16{ 38, 5 } }; while (p.next()) |_| {} } test "sgr: direct color bg missing color" { // This used to crash - var p: Parser = .{ .params = &[_]u16{ 48, 5 }, .colon = false }; + var p: Parser = .{ .params = &[_]u16{ 48, 5 } }; while (p.next()) |_| {} } @@ -608,7 +711,7 @@ test "sgr: direct fg/bg/underline ignore optional color space" { // Colon version should skip the optional color space identifier { // 3 8 : 2 : Pi : Pr : Pg : Pb - const v = testParseColon(&[_]u16{ 38, 2, 0, 1, 2, 3, 4 }); + const v = testParseColon(&[_]u16{ 38, 2, 0, 1, 2, 3 }); try testing.expect(v == .direct_color_fg); try testing.expectEqual(@as(u8, 1), v.direct_color_fg.r); try testing.expectEqual(@as(u8, 2), v.direct_color_fg.g); @@ -616,7 +719,7 @@ test "sgr: direct fg/bg/underline ignore optional color space" { } { // 4 8 : 2 : Pi : Pr : Pg : Pb - const v = testParseColon(&[_]u16{ 48, 2, 0, 1, 2, 3, 4 }); + const v = testParseColon(&[_]u16{ 48, 2, 0, 1, 2, 3 }); try testing.expect(v == .direct_color_bg); try testing.expectEqual(@as(u8, 1), v.direct_color_bg.r); try testing.expectEqual(@as(u8, 2), v.direct_color_bg.g); @@ -624,7 +727,7 @@ test "sgr: direct fg/bg/underline ignore optional color space" { } { // 5 8 : 2 : Pi : Pr : Pg : Pb - const v = testParseColon(&[_]u16{ 58, 2, 0, 1, 2, 3, 4 }); + const v = testParseColon(&[_]u16{ 58, 2, 0, 1, 2, 3 }); try testing.expect(v == .underline_color); try testing.expectEqual(@as(u8, 1), v.underline_color.r); try testing.expectEqual(@as(u8, 2), v.underline_color.g); @@ -634,7 +737,7 @@ test "sgr: direct fg/bg/underline ignore optional color space" { // Semicolon version should not parse optional color space identifier { // 3 8 ; 2 ; Pr ; Pg ; Pb - const v = testParse(&[_]u16{ 38, 2, 0, 1, 2, 3, 4 }); + const v = testParse(&[_]u16{ 38, 2, 0, 1, 2, 3 }); try testing.expect(v == .direct_color_fg); try testing.expectEqual(@as(u8, 0), v.direct_color_fg.r); try testing.expectEqual(@as(u8, 1), v.direct_color_fg.g); @@ -642,7 +745,7 @@ test "sgr: direct fg/bg/underline ignore optional color space" { } { // 4 8 ; 2 ; Pr ; Pg ; Pb - const v = testParse(&[_]u16{ 48, 2, 0, 1, 2, 3, 4 }); + const v = testParse(&[_]u16{ 48, 2, 0, 1, 2, 3 }); try testing.expect(v == .direct_color_bg); try testing.expectEqual(@as(u8, 0), v.direct_color_bg.r); try testing.expectEqual(@as(u8, 1), v.direct_color_bg.g); @@ -650,10 +753,114 @@ test "sgr: direct fg/bg/underline ignore optional color space" { } { // 5 8 ; 2 ; Pr ; Pg ; Pb - const v = testParse(&[_]u16{ 58, 2, 0, 1, 2, 3, 4 }); + const v = testParse(&[_]u16{ 58, 2, 0, 1, 2, 3 }); try testing.expect(v == .underline_color); try testing.expectEqual(@as(u8, 0), v.underline_color.r); try testing.expectEqual(@as(u8, 1), v.underline_color.g); try testing.expectEqual(@as(u8, 2), v.underline_color.b); } } + +test "sgr: direct fg colon with too many colons" { + var p: Parser = .{ + .params = &[_]u16{ 38, 2, 0, 1, 2, 3, 4, 1 }, + .params_sep = sep: { + var list = SepList.initEmpty(); + for (0..6) |idx| list.set(idx); + break :sep list; + }, + }; + + try testing.expect(p.next().? == .unknown); + try testing.expect(p.next().? == .bold); + try testing.expect(p.next() == null); +} + +test "sgr: direct fg colon with colorspace and extra param" { + var p: Parser = .{ + .params = &[_]u16{ 38, 2, 0, 1, 2, 3, 1 }, + .params_sep = sep: { + var list = SepList.initEmpty(); + for (0..5) |idx| list.set(idx); + break :sep list; + }, + }; + + { + const v = p.next().?; + std.log.warn("WHAT={}", .{v}); + try testing.expect(v == .direct_color_fg); + try testing.expectEqual(@as(u8, 1), v.direct_color_fg.r); + try testing.expectEqual(@as(u8, 2), v.direct_color_fg.g); + try testing.expectEqual(@as(u8, 3), v.direct_color_fg.b); + } + + try testing.expect(p.next().? == .bold); + try testing.expect(p.next() == null); +} + +test "sgr: direct fg colon no colorspace and extra param" { + var p: Parser = .{ + .params = &[_]u16{ 38, 2, 1, 2, 3, 1 }, + .params_sep = sep: { + var list = SepList.initEmpty(); + for (0..4) |idx| list.set(idx); + break :sep list; + }, + }; + + { + const v = p.next().?; + try testing.expect(v == .direct_color_fg); + try testing.expectEqual(@as(u8, 1), v.direct_color_fg.r); + try testing.expectEqual(@as(u8, 2), v.direct_color_fg.g); + try testing.expectEqual(@as(u8, 3), v.direct_color_fg.b); + } + + try testing.expect(p.next().? == .bold); + try testing.expect(p.next() == null); +} + +// Kakoune sent this complex SGR sequence that caused invalid behavior. +test "sgr: kakoune input" { + // This used to crash + var p: Parser = .{ + .params = &[_]u16{ 0, 4, 3, 38, 2, 175, 175, 215, 58, 2, 0, 190, 80, 70 }, + .params_sep = sep: { + var list = SepList.initEmpty(); + list.set(1); + list.set(8); + list.set(9); + list.set(10); + list.set(11); + list.set(12); + break :sep list; + }, + }; + + { + const v = p.next().?; + try testing.expect(v == .unset); + } + { + const v = p.next().?; + try testing.expect(v == .underline); + try testing.expectEqual(Attribute.Underline.curly, v.underline); + } + { + const v = p.next().?; + try testing.expect(v == .direct_color_fg); + try testing.expectEqual(@as(u8, 175), v.direct_color_fg.r); + try testing.expectEqual(@as(u8, 175), v.direct_color_fg.g); + try testing.expectEqual(@as(u8, 215), v.direct_color_fg.b); + } + { + const v = p.next().?; + try testing.expect(v == .underline_color); + try testing.expectEqual(@as(u8, 190), v.underline_color.r); + try testing.expectEqual(@as(u8, 80), v.underline_color.g); + try testing.expectEqual(@as(u8, 70), v.underline_color.b); + } + + //try testing.expect(p.next() == null); +} diff --git a/src/terminal/stream.zig b/src/terminal/stream.zig index f75d86c0a4..eb5ab2c656 100644 --- a/src/terminal/stream.zig +++ b/src/terminal/stream.zig @@ -253,15 +253,11 @@ pub fn Stream(comptime Handler: type) type { // A parameter separator: ':', ';' => if (self.parser.params_idx < 16) { self.parser.params[self.parser.params_idx] = self.parser.param_acc; + if (c == ':') self.parser.params_sep.set(self.parser.params_idx); self.parser.params_idx += 1; self.parser.param_acc = 0; self.parser.param_acc_idx = 0; - - // Keep track of separator state. - const sep: Parser.ParamSepState = @enumFromInt(c); - if (self.parser.params_idx == 1) self.parser.params_sep = sep; - if (self.parser.params_sep != sep) self.parser.params_sep = .mixed; }, // Explicitly ignored: 0x7F => {}, @@ -937,7 +933,10 @@ pub fn Stream(comptime Handler: type) type { 'm' => switch (input.intermediates.len) { 0 => if (@hasDecl(T, "setAttribute")) { // log.info("parse SGR params={any}", .{action.params}); - var p: sgr.Parser = .{ .params = input.params, .colon = input.sep == .colon }; + var p: sgr.Parser = .{ + .params = input.params, + .params_sep = input.params_sep, + }; while (p.next()) |attr| { // log.info("SGR attribute: {}", .{attr}); try self.handler.setAttribute(attr); @@ -1605,7 +1604,7 @@ pub fn Stream(comptime Handler: type) type { } else log.warn("unimplemented OSC callback: {}", .{cmd}); }, - .progress, .sleep, .show_message_box, .change_conemu_tab_title => { + .progress, .sleep, .show_message_box, .change_conemu_tab_title, .wait_input => { log.warn("unimplemented OSC callback: {}", .{cmd}); }, } diff --git a/src/termio/Exec.zig b/src/termio/Exec.zig index 1a3b8cad00..5a2d2a5070 100644 --- a/src/termio/Exec.zig +++ b/src/termio/Exec.zig @@ -179,8 +179,17 @@ pub fn threadExit(self: *Exec, td: *termio.Termio.ThreadData) void { // Quit our read thread after exiting the subprocess so that // we don't get stuck waiting for data to stop flowing if it is // a particularly noisy process. - _ = posix.write(exec.read_thread_pipe, "x") catch |err| - log.warn("error writing to read thread quit pipe err={}", .{err}); + _ = posix.write(exec.read_thread_pipe, "x") catch |err| switch (err) { + // BrokenPipe means that our read thread is closed already, + // which is completely fine since that is what we were trying + // to achieve. + error.BrokenPipe => {}, + + else => log.warn( + "error writing to read thread quit pipe err={}", + .{err}, + ), + }; if (comptime builtin.os.tag == .windows) { // Interrupt the blocking read so the thread can see the quit message @@ -673,6 +682,8 @@ pub const ThreadData = struct { pub const Config = struct { command: ?[]const u8 = null, + env: EnvMap, + env_override: configpkg.RepeatableStringMap = .{}, shell_integration: configpkg.Config.ShellIntegration = .detect, shell_integration_features: configpkg.Config.ShellIntegrationFeatures = .{}, working_directory: ?[]const u8 = null, @@ -694,7 +705,7 @@ const Subprocess = struct { arena: std.heap.ArenaAllocator, cwd: ?[]const u8, - env: EnvMap, + env: ?EnvMap, args: [][]const u8, grid_size: renderer.GridSize, screen_size: renderer.ScreenSize, @@ -712,19 +723,9 @@ const Subprocess = struct { errdefer arena.deinit(); const alloc = arena.allocator(); - // Set our env vars. For Flatpak builds running in Flatpak we don't - // inherit our environment because the login shell on the host side - // will get it. - var env = env: { - if (comptime build_config.flatpak) { - if (internal_os.isFlatpak()) { - break :env std.process.EnvMap.init(alloc); - } - } - - break :env try std.process.getEnvMap(alloc); - }; - errdefer env.deinit(); + // Get our env. If a default env isn't provided by the caller + // then we get it ourselves. + var env = cfg.env; // If we have a resources dir then set our env var if (cfg.resources_dir) |dir| { @@ -838,35 +839,11 @@ const Subprocess = struct { try env.put("TERM_PROGRAM", "ghostty"); try env.put("TERM_PROGRAM_VERSION", build_config.version_string); - // When embedding in macOS and running via XCode, XCode injects - // a bunch of things that break our shell process. We remove those. - if (comptime builtin.target.isDarwin() and build_config.artifact == .lib) { - if (env.get("__XCODE_BUILT_PRODUCTS_DIR_PATHS") != null) { - env.remove("__XCODE_BUILT_PRODUCTS_DIR_PATHS"); - env.remove("__XPC_DYLD_LIBRARY_PATH"); - env.remove("DYLD_FRAMEWORK_PATH"); - env.remove("DYLD_INSERT_LIBRARIES"); - env.remove("DYLD_LIBRARY_PATH"); - env.remove("LD_LIBRARY_PATH"); - env.remove("SECURITYSESSIONID"); - env.remove("XPC_SERVICE_NAME"); - } - - // Remove this so that running `ghostty` within Ghostty works. - env.remove("GHOSTTY_MAC_APP"); - } - // VTE_VERSION is set by gnome-terminal and other VTE-based terminals. // We don't want our child processes to think we're running under VTE. + // This is not apprt-specific, so we do it here. env.remove("VTE_VERSION"); - // Don't leak these GTK environment variables to child processes. - if (comptime build_config.app_runtime == .gtk) { - env.remove("GDK_DEBUG"); - env.remove("GDK_DISABLE"); - env.remove("GSK_RENDERER"); - } - // Setup our shell integration, if we can. const integrated_shell: ?shell_integration.Shell, const shell_command: []const u8 = shell: { const default_shell_command = cfg.command orelse switch (builtin.os.tag) { @@ -875,7 +852,11 @@ const Subprocess = struct { }; const force: ?shell_integration.Shell = switch (cfg.shell_integration) { - .none => break :shell .{ null, default_shell_command }, + .none => { + // Even if shell integration is none, we still want to set up the feature env vars + try shell_integration.setupFeatures(&env, cfg.shell_integration_features); + break :shell .{ null, default_shell_command }; + }, .detect => null, .bash => .bash, .elvish => .elvish, @@ -909,6 +890,15 @@ const Subprocess = struct { log.warn("shell could not be detected, no automatic shell integration will be injected", .{}); } + // Add the environment variables that override any others. + { + var it = cfg.env_override.iterator(); + while (it.next()) |entry| try env.put( + entry.key_ptr.*, + entry.value_ptr.*, + ); + } + // Build our args list const args = args: { const cap = 9; // the most we'll ever use @@ -971,12 +961,12 @@ const Subprocess = struct { // which we may not want. If we specify "-l" then we can avoid // this behavior but now the shell isn't a login shell. // - // There is another issue: `login(1)` only checks for ".hushlogin" - // in the working directory. This means that if we specify "-l" - // then we won't get hushlogin honored if its in the home - // directory (which is standard). To get around this, we - // check for hushlogin ourselves and if present specify the - // "-q" flag to login(1). + // There is another issue: `login(1)` on macOS 14.3 and earlier + // checked for ".hushlogin" in the working directory. This means + // that if we specify "-l" then we won't get hushlogin honored + // if its in the home directory (which is standard). To get + // around this, we check for hushlogin ourselves and if present + // specify the "-q" flag to login(1). // // So to get all the behaviors we want, we specify "-l" but // execute "bash" (which is built-in to macOS). We then use @@ -1073,6 +1063,7 @@ const Subprocess = struct { pub fn deinit(self: *Subprocess) void { self.stop(); if (self.pty) |*pty| pty.deinit(); + if (self.env) |*env| env.deinit(); self.arena.deinit(); self.* = undefined; } @@ -1094,6 +1085,10 @@ const Subprocess = struct { }); self.pty = pty; errdefer { + if (comptime builtin.os.tag != .windows) { + _ = posix.close(pty.slave); + } + pty.deinit(); self.pty = null; } @@ -1151,7 +1146,7 @@ const Subprocess = struct { var cmd: Command = .{ .path = self.args[0], .args = self.args, - .env = &self.env, + .env = if (self.env) |*env| env else null, .cwd = cwd, .stdin = if (builtin.os.tag == .windows) null else .{ .handle = pty.slave }, .stdout = if (builtin.os.tag == .windows) null else .{ .handle = pty.slave }, @@ -1178,6 +1173,19 @@ const Subprocess = struct { log.info("subcommand cgroup={s}", .{self.linux_cgroup orelse "-"}); } + if (comptime builtin.os.tag != .windows) { + // Once our subcommand is started we can close the slave + // side. This prevents the slave fd from being leaked to + // future children. + _ = posix.close(pty.slave); + } + + // Successful start we can clear out some memory. + if (self.env) |*env| { + env.deinit(); + self.env = null; + } + self.command = cmd; return switch (builtin.os.tag) { .windows => .{ @@ -1452,6 +1460,13 @@ pub const ReadThread = struct { log.info("read thread got quit signal", .{}); return; } + + // If our pty fd is closed, then we're also done with our + // read thread. + if (pollfds[0].revents & posix.POLL.HUP != 0) { + log.info("pty fd closed, read thread exiting", .{}); + return; + } } } diff --git a/src/termio/Termio.zig b/src/termio/Termio.zig index ab61ae4ca7..8a2e6cc7a7 100644 --- a/src/termio/Termio.zig +++ b/src/termio/Termio.zig @@ -220,7 +220,7 @@ pub fn init(self: *Termio, alloc: Allocator, opts: termio.Options) !void { .renderer_mailbox = opts.renderer_mailbox, .surface_mailbox = opts.surface_mailbox, .size = opts.size, - .backend = opts.backend, + .backend = backend, .mailbox = opts.mailbox, .terminal_stream = .{ .handler = handler, diff --git a/src/termio/shell_integration.zig b/src/termio/shell_integration.zig index 8cd2a92ae2..423e2f5186 100644 --- a/src/termio/shell_integration.zig +++ b/src/termio/shell_integration.zig @@ -58,67 +58,73 @@ pub fn setup( break :exe std.fs.path.basename(command[0..idx]); }; - const result: ShellIntegration = shell: { - if (std.mem.eql(u8, "bash", exe)) { - // Apple distributes their own patched version of Bash 3.2 - // on macOS that disables the ENV-based POSIX startup path. - // This means we're unable to perform our automatic shell - // integration sequence in this specific environment. - // - // If we're running "/bin/bash" on Darwin, we can assume - // we're using Apple's Bash because /bin is non-writable - // on modern macOS due to System Integrity Protection. - if (comptime builtin.target.isDarwin()) { - if (std.mem.eql(u8, "/bin/bash", command)) { - return null; - } - } + const result = try setupShell(alloc_arena, resource_dir, command, env, exe); - const new_command = try setupBash( - alloc_arena, - command, - resource_dir, - env, - ) orelse return null; - break :shell .{ - .shell = .bash, - .command = new_command, - }; - } + // Setup our feature env vars + try setupFeatures(env, features); - if (std.mem.eql(u8, "elvish", exe)) { - try setupXdgDataDirs(alloc_arena, resource_dir, env); - break :shell .{ - .shell = .elvish, - .command = try alloc_arena.dupe(u8, command), - }; - } + return result; +} - if (std.mem.eql(u8, "fish", exe)) { - try setupXdgDataDirs(alloc_arena, resource_dir, env); - break :shell .{ - .shell = .fish, - .command = try alloc_arena.dupe(u8, command), - }; +fn setupShell( + alloc_arena: Allocator, + resource_dir: []const u8, + command: []const u8, + env: *EnvMap, + exe: []const u8, +) !?ShellIntegration { + if (std.mem.eql(u8, "bash", exe)) { + // Apple distributes their own patched version of Bash 3.2 + // on macOS that disables the ENV-based POSIX startup path. + // This means we're unable to perform our automatic shell + // integration sequence in this specific environment. + // + // If we're running "/bin/bash" on Darwin, we can assume + // we're using Apple's Bash because /bin is non-writable + // on modern macOS due to System Integrity Protection. + if (comptime builtin.target.isDarwin()) { + if (std.mem.eql(u8, "/bin/bash", command)) { + return null; + } } - if (std.mem.eql(u8, "zsh", exe)) { - try setupZsh(resource_dir, env); - break :shell .{ - .shell = .zsh, - .command = try alloc_arena.dupe(u8, command), - }; - } + const new_command = try setupBash( + alloc_arena, + command, + resource_dir, + env, + ) orelse return null; + return .{ + .shell = .bash, + .command = new_command, + }; + } - return null; - }; + if (std.mem.eql(u8, "elvish", exe)) { + try setupXdgDataDirs(alloc_arena, resource_dir, env); + return .{ + .shell = .elvish, + .command = try alloc_arena.dupe(u8, command), + }; + } - // Setup our feature env vars - if (!features.cursor) try env.put("GHOSTTY_SHELL_INTEGRATION_NO_CURSOR", "1"); - if (!features.sudo) try env.put("GHOSTTY_SHELL_INTEGRATION_NO_SUDO", "1"); - if (!features.title) try env.put("GHOSTTY_SHELL_INTEGRATION_NO_TITLE", "1"); + if (std.mem.eql(u8, "fish", exe)) { + try setupXdgDataDirs(alloc_arena, resource_dir, env); + return .{ + .shell = .fish, + .command = try alloc_arena.dupe(u8, command), + }; + } - return result; + if (std.mem.eql(u8, "zsh", exe)) { + try setupZsh(resource_dir, env); + return .{ + .shell = .zsh, + .command = try alloc_arena.dupe(u8, command), + }; + } + + return null; } test "force shell" { @@ -138,6 +144,58 @@ test "force shell" { } } +/// Setup shell integration feature environment variables without +/// performing full shell integration setup. +pub fn setupFeatures( + env: *EnvMap, + features: config.ShellIntegrationFeatures, +) !void { + if (!features.cursor) try env.put("GHOSTTY_SHELL_INTEGRATION_NO_CURSOR", "1"); + if (!features.sudo) try env.put("GHOSTTY_SHELL_INTEGRATION_NO_SUDO", "1"); + if (!features.title) try env.put("GHOSTTY_SHELL_INTEGRATION_NO_TITLE", "1"); +} + +test "setup features" { + const testing = std.testing; + + var arena = ArenaAllocator.init(testing.allocator); + defer arena.deinit(); + const alloc = arena.allocator(); + + // Test: all features enabled (no environment variables should be set) + { + var env = EnvMap.init(alloc); + defer env.deinit(); + + try setupFeatures(&env, .{ .cursor = true, .sudo = true, .title = true }); + try testing.expect(env.get("GHOSTTY_SHELL_INTEGRATION_NO_CURSOR") == null); + try testing.expect(env.get("GHOSTTY_SHELL_INTEGRATION_NO_SUDO") == null); + try testing.expect(env.get("GHOSTTY_SHELL_INTEGRATION_NO_TITLE") == null); + } + + // Test: all features disabled + { + var env = EnvMap.init(alloc); + defer env.deinit(); + + try setupFeatures(&env, .{ .cursor = false, .sudo = false, .title = false }); + try testing.expectEqualStrings("1", env.get("GHOSTTY_SHELL_INTEGRATION_NO_CURSOR").?); + try testing.expectEqualStrings("1", env.get("GHOSTTY_SHELL_INTEGRATION_NO_SUDO").?); + try testing.expectEqualStrings("1", env.get("GHOSTTY_SHELL_INTEGRATION_NO_TITLE").?); + } + + // Test: mixed features + { + var env = EnvMap.init(alloc); + defer env.deinit(); + + try setupFeatures(&env, .{ .cursor = false, .sudo = true, .title = false }); + try testing.expectEqualStrings("1", env.get("GHOSTTY_SHELL_INTEGRATION_NO_CURSOR").?); + try testing.expect(env.get("GHOSTTY_SHELL_INTEGRATION_NO_SUDO") == null); + try testing.expectEqualStrings("1", env.get("GHOSTTY_SHELL_INTEGRATION_NO_TITLE").?); + } +} + /// Setup the bash automatic shell integration. This works by /// starting bash in POSIX mode and using the ENV environment /// variable to load our bash integration script. This prevents @@ -145,8 +203,6 @@ test "force shell" { /// our script's responsibility (along with disabling POSIX /// mode). /// -/// This approach requires bash version 4 or later. -/// /// This returns a new (allocated) shell command string that /// enables the integration or null if integration failed. fn setupBash( @@ -188,12 +244,6 @@ fn setupBash( // Unsupported options: // -c -c is always non-interactive // --posix POSIX mode (a la /bin/sh) - // - // Some additional cases we don't yet cover: - // - // - If additional file arguments are provided (after a `-` or `--` flag), - // and the `i` shell option isn't being explicitly set, we can assume a - // non-interactive shell session and skip loading our shell integration. var rcfile: ?[]const u8 = null; while (iter.next()) |arg| { if (std.mem.eql(u8, arg, "--posix")) { @@ -210,6 +260,14 @@ fn setupBash( return null; } try args.append(arg); + } else if (std.mem.eql(u8, arg, "-") or std.mem.eql(u8, arg, "--")) { + // All remaining arguments should be passed directly to the shell + // command. We shouldn't perform any further option processing. + try args.append(arg); + while (iter.next()) |remaining_arg| { + try args.append(remaining_arg); + } + break; } else { try args.append(arg); } @@ -372,6 +430,30 @@ test "bash: HISTFILE" { } } +test "bash: additional arguments" { + const testing = std.testing; + const alloc = testing.allocator; + + var env = EnvMap.init(alloc); + defer env.deinit(); + + // "-" argument separator + { + const command = try setupBash(alloc, "bash - --arg file1 file2", ".", &env); + defer if (command) |c| alloc.free(c); + + try testing.expectEqualStrings("bash --posix - --arg file1 file2", command.?); + } + + // "--" argument separator + { + const command = try setupBash(alloc, "bash -- --arg file1 file2", ".", &env); + defer if (command) |c| alloc.free(c); + + try testing.expectEqualStrings("bash --posix -- --arg file1 file2", command.?); + } +} + /// Setup automatic shell integration for shells that include /// their modules from paths in `XDG_DATA_DIRS` env variable. /// diff --git a/src/termio/stream_handler.zig b/src/termio/stream_handler.zig index 849e5c107f..e9bb353fba 100644 --- a/src/termio/stream_handler.zig +++ b/src/termio/stream_handler.zig @@ -1418,11 +1418,13 @@ pub const StreamHandler = struct { var buf = std.ArrayList(u8).init(self.alloc); defer buf.deinit(); const writer = buf.writer(); - try writer.writeAll("\x1b]21"); for (request.list.items) |item| { switch (item) { .query => |key| { + // If the writer buffer is empty, we need to write our prefix + if (buf.items.len == 0) try writer.writeAll("\x1b]21"); + const color: terminal.color.RGB = switch (key) { .palette => |palette| self.terminal.color_palette.colors[palette], .special => |special| switch (special) { @@ -1517,14 +1519,16 @@ pub const StreamHandler = struct { } } - try writer.writeAll(request.terminator.string()); - - self.messageWriter(.{ - .write_alloc = .{ - .alloc = self.alloc, - .data = try buf.toOwnedSlice(), - }, - }); + // If we had any writes to our buffer, we queue them now + if (buf.items.len > 0) { + try writer.writeAll(request.terminator.string()); + self.messageWriter(.{ + .write_alloc = .{ + .alloc = self.alloc, + .data = try buf.toOwnedSlice(), + }, + }); + } // Note: we don't have to do a queueRender here because every // processed stream will queue a render once it is done processing diff --git a/src/unicode/props.zig b/src/unicode/props.zig index d77bf4c8ae..8c7621b795 100644 --- a/src/unicode/props.zig +++ b/src/unicode/props.zig @@ -131,7 +131,9 @@ pub fn get(cp: u21) Properties { /// Runnable binary to generate the lookup tables and output to stdout. pub fn main() !void { - const alloc = std.heap.c_allocator; + var arena_state = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena_state.deinit(); + const alloc = arena_state.allocator(); const gen: lut.Generator( Properties, diff --git a/typos.toml b/typos.toml index 87b41336bd..d449e3ffa5 100644 --- a/typos.toml +++ b/typos.toml @@ -43,6 +43,7 @@ Strat = "Strat" grey = "gray" greyscale = "grayscale" DECID = "DECID" +flate = "flate" [type.swift.extend-words] inout = "inout"