├── .devcontainer ├── .devenv │ └── .gitignore ├── .direnv │ └── .gitignore └── devcontainer.json ├── .devenv └── .gitignore ├── .direnv └── .gitignore ├── .envrc ├── .github ├── CODEOWNERS ├── dependabot.yaml ├── local │ ├── Makefile.local │ └── rules.local └── workflows │ ├── check_linked_issue.yaml │ ├── dependabot.yaml │ ├── docs.yaml │ ├── new_version.yaml │ └── release.yaml ├── .gitignore ├── .gitmodules ├── .pre-commit-config.yaml ├── .prettierrc.json ├── LICENSE ├── Makefile ├── README.md ├── book.toml ├── config ├── 00-aic8800-reset-ttyas1.conf ├── 00-aic8800-reset-ttys1.conf ├── 00-lan-led_enp1s0.conf ├── 00-lan-led_eth0.conf ├── 00-rgb0-rainbow.conf ├── 00-thermal_governor-step_wise.conf ├── 00-wan-led_enp1s0.conf ├── 00-wan-led_eth0.conf ├── 01-hciattach-ttys1.conf ├── before.txt ├── config.txt ├── hw_intfc.conf └── uEnv.txt ├── debian ├── .gitignore ├── changelog ├── common-lintian-overrides ├── compat ├── control ├── copyright ├── rsetup-config-aic8800-ttyas1.install ├── rsetup-config-aic8800-ttys1.install ├── rsetup-config-first-boot.docs ├── rsetup-config-first-boot.postinst ├── rsetup-config-hciattach-ttys1.install ├── rsetup-config-lan-led-enp1s0.install ├── rsetup-config-lan-led-eth0.install ├── rsetup-config-rgb0-rainbow.install ├── rsetup-config-thermal-governor-step-wise.install ├── rsetup-config-wan-led-enp1s0.install ├── rsetup-config-wan-led-eth0.install ├── rsetup.docs ├── rsetup.install ├── rsetup.links ├── rsetup.lintian-overrides ├── rsetup.postinst ├── rsetup.rsetup-aic8800-reset@.service ├── rsetup.rsetup-hciattach@.service ├── rsetup.service ├── rules └── source │ ├── format │ └── lintian-overrides ├── devenv.lock ├── devenv.nix ├── docs ├── SUMMARY.md └── special │ └── stress_test.md ├── overlay └── .gitkeep ├── po ├── .gitignore ├── messages.pot └── zh-CN.po ├── src └── usr │ ├── bin │ └── rsetup │ ├── lib │ ├── librtui │ │ └── .gitignore │ └── rsetup │ │ ├── cli │ │ ├── account.sh │ │ ├── baota.sh │ │ ├── docker.sh │ │ ├── kernel.sh │ │ ├── main.sh │ │ ├── rconfig.sh │ │ ├── ssh.sh │ │ ├── system.sh │ │ ├── test │ │ │ ├── gstreamer.sh │ │ │ ├── main.sh │ │ │ ├── mpp.sh │ │ │ └── stress.sh │ │ ├── u-boot-menu.sh │ │ └── wi-fi.sh │ │ ├── mod │ │ ├── aic8800.sh │ │ ├── block_helpers.sh │ │ ├── config.sh │ │ ├── debug_utils.sh │ │ ├── dtbo_is_compatible │ │ ├── hwid.sh │ │ ├── overlay.sh │ │ ├── parse_dtbo │ │ └── pkg.sh │ │ └── tui │ │ ├── comm │ │ └── comm.sh │ │ ├── hardware │ │ ├── gpio.sh │ │ └── hardware.sh │ │ ├── local │ │ └── local.sh │ │ ├── main.sh │ │ ├── overlay │ │ └── overlay.sh │ │ ├── system │ │ └── system.sh │ │ ├── task │ │ ├── baota │ │ │ └── baota.sh │ │ ├── docker │ │ │ └── docker.sh │ │ ├── ssh │ │ │ └── ssh.sh │ │ └── task.sh │ │ └── user │ │ └── user.sh │ └── share │ ├── applications │ └── rsetup.desktop │ ├── bash-completion │ └── completions │ │ └── rsetup │ ├── librtui │ └── .gitignore │ └── man │ └── man8 │ ├── .gitignore │ └── rsetup.8.md └── theme ├── book.js ├── css ├── chrome.css ├── general.css ├── language-picker.css ├── mdbook-admonish.css ├── print.css └── variables.css ├── favicon.png ├── favicon.svg ├── fonts ├── OPEN-SANS-LICENSE.txt ├── SOURCE-CODE-PRO-LICENSE.txt ├── fonts.css ├── open-sans-v17-all-charsets-300.woff2 ├── open-sans-v17-all-charsets-300italic.woff2 ├── open-sans-v17-all-charsets-600.woff2 ├── open-sans-v17-all-charsets-600italic.woff2 ├── open-sans-v17-all-charsets-700.woff2 ├── open-sans-v17-all-charsets-700italic.woff2 ├── open-sans-v17-all-charsets-800.woff2 ├── open-sans-v17-all-charsets-800italic.woff2 ├── open-sans-v17-all-charsets-italic.woff2 ├── open-sans-v17-all-charsets-regular.woff2 └── source-code-pro-v11-all-charsets-500.woff2 ├── highlight.css ├── highlight.js └── index.hbs /.devcontainer/.devenv/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /.devcontainer/.direnv/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "customizations": { 3 | "vscode": { 4 | "settings": { 5 | "terminal.integrated.defaultProfile.linux": "bash" 6 | }, 7 | "extensions": [ 8 | "mkhl.direnv", 9 | "github.vscode-github-actions", 10 | "grafana.vscode-jsonnet", 11 | "ms-vscode.makefile-tools", 12 | "vivaxy.vscode-conventional-commits" 13 | ] 14 | } 15 | }, 16 | "image": "mcr.microsoft.com/devcontainers/base:bookworm", 17 | "features": { 18 | "ghcr.io/devcontainers/features/nix:1": { 19 | "version": "latest", 20 | "packages": "cachix,direnv,devenv", 21 | "extraNixConfig": [ 22 | "experimental-features = nix-command flakes", 23 | // Uncomment below to speed up container building in China 24 | // "substituters = https://mirrors.ustc.edu.cn/nix-channels/store https://devenv.cachix.org https://cache.nixos.org", 25 | "trusted-users = root vscode" 26 | ] 27 | } 28 | }, 29 | "mounts": [ 30 | { 31 | "source": "${localWorkspaceFolder}/.devcontainer/.devenv", 32 | "target": "${containerWorkspaceFolder}/.devenv", 33 | "type": "bind" 34 | }, 35 | { 36 | "source": "${localWorkspaceFolder}/.devcontainer/.direnv", 37 | "target": "${containerWorkspaceFolder}/.direnv", 38 | "type": "bind" 39 | } 40 | ], 41 | "workspaceMount": "source=${localWorkspaceFolder}/..,target=/workspaces,type=bind,consistency=cached", 42 | "runArgs": [ 43 | // https://github.com/moby/moby/issues/27195#issuecomment-1410745778 44 | "--ulimit", "nofile=1024:524288" 45 | ], 46 | "overrideCommand": true, 47 | "updateContentCommand": "make devcontainer_setup" 48 | } 49 | -------------------------------------------------------------------------------- /.devenv/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /.direnv/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /.envrc: -------------------------------------------------------------------------------- 1 | source_url "https://raw.githubusercontent.com/cachix/devenv/d1f7b48e35e6dee421cfd0f51481d17f77586997/direnvrc" "sha256-YBzqskFZxmNb3kYVoKD9ZixoPXJh1C9ZvTLGFRkauZ0=" 2 | 3 | use devenv -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @RadxaYuntian 2 | -------------------------------------------------------------------------------- /.github/dependabot.yaml: -------------------------------------------------------------------------------- 1 | updates: 2 | - directory: "/" 3 | package-ecosystem: "github-actions" 4 | schedule: 5 | interval: "daily" 6 | version: 2 -------------------------------------------------------------------------------- /.github/local/Makefile.local: -------------------------------------------------------------------------------- 1 | # 2 | # Development 3 | # 4 | .PHONY: overlay 5 | overlay: 6 | $(eval OVERLAY_DIR := $(shell mktemp -d)) 7 | sudo mount -t overlay overlay -o lowerdir=/:./src:./overlay "$(OVERLAY_DIR)" 8 | sudo mount -o ro --bind ./externals/librtui/src/lib/librtui "$(OVERLAY_DIR)/usr/lib/librtui" 9 | sudo mount -o ro --bind ./externals/librtui/src/share/librtui "$(OVERLAY_DIR)/usr/share/librtui" 10 | sudo systemd-nspawn --link-journal no -D $(OVERLAY_DIR) $(OVERLAY_CMD) || true 11 | sudo umount -R $(OVERLAY_DIR) 12 | rmdir $(OVERLAY_DIR) 13 | 14 | .PHONY: run 15 | run: OVERLAY_CMD := rsetup 16 | run: overlay 17 | 18 | .PHONY: debug 19 | debug: OVERLAY_CMD := bash -c "DEBUG=true /usr/bin/rsetup" 20 | debug: overlay 21 | 22 | .PHONY: shell 23 | shell: OVERLAY_CMD := bash 24 | shell: overlay 25 | 26 | # 27 | # Build 28 | # 29 | build: build-man 30 | 31 | SRC-MAN := src/usr/share/man/man8 32 | SRCS-MAN := $(wildcard $(SRC-MAN)/*.md) 33 | MANS := $(SRCS-MAN:.md=) 34 | .PHONY: build-man 35 | build-man: $(MANS) 36 | 37 | $(SRC-MAN)/%: $(SRC-MAN)/%.md 38 | pandoc "$<" -o "$@" --from markdown --to man -s 39 | 40 | # 41 | # Documentation 42 | # 43 | .PHONY: serve 44 | serve: 45 | mdbook serve 46 | 47 | .PHONY: serve_zh-CN 48 | serve_zh-CN: 49 | MDBOOK_BOOK__LANGUAGE=zh-CN mdbook serve -d book/zh-CN 50 | 51 | .PHONY: translate 52 | translate: 53 | MDBOOK_OUTPUT='{"xgettext": {"pot-file": "messages.pot"}}' mdbook build -d po 54 | for i in po/*.po; \ 55 | do \ 56 | msgmerge --update $$i po/messages.pot; \ 57 | done 58 | 59 | # 60 | # Clean 61 | # 62 | .PHONY: clean 63 | clean: clean-man clean-deb 64 | 65 | .PHONY: clean-man 66 | clean-man: 67 | rm -rf $(MANS) 68 | -------------------------------------------------------------------------------- /.github/local/rules.local: -------------------------------------------------------------------------------- 1 | override_dh_installsystemd: 2 | dh_installsystemd --name=rsetup-aic8800-reset@ 3 | dh_installsystemd --name=rsetup-hciattach@ 4 | dh_installsystemd --name=rsetup 5 | -------------------------------------------------------------------------------- /.github/workflows/check_linked_issue.yaml: -------------------------------------------------------------------------------- 1 | jobs: 2 | check_pull_requests: 3 | name: "Check linked issues" 4 | permissions: 5 | issues: "write" 6 | pull-requests: "write" 7 | runs-on: "ubuntu-latest" 8 | steps: 9 | - name: "Check linked issues" 10 | uses: "nearform-actions/github-action-check-linked-issues@v1" 11 | name: "Check linked issues" 12 | "on": 13 | pull_request_target: 14 | types: 15 | - "opened" 16 | - "reopened" 17 | permissions: {} -------------------------------------------------------------------------------- /.github/workflows/dependabot.yaml: -------------------------------------------------------------------------------- 1 | jobs: 2 | dependabot: 3 | if: "github.actor == 'dependabot[bot]'" 4 | runs-on: "ubuntu-latest" 5 | steps: 6 | - id: "metadata" 7 | name: "Dependabot metadata" 8 | uses: "dependabot/fetch-metadata@v2" 9 | with: 10 | github-token: "${{ secrets.GITHUB_TOKEN }}" 11 | - env: 12 | GH_TOKEN: "${{secrets.GITHUB_TOKEN}}" 13 | PR_URL: "${{github.event.pull_request.html_url}}" 14 | name: "Approve a PR & Enable auto-merge for Dependabot PRs" 15 | run: | 16 | gh pr review --approve "$PR_URL" 17 | gh pr merge --auto --merge "$PR_URL" 18 | name: "Dependabot auto-merge" 19 | "on": 20 | pull_request: {} 21 | permissions: 22 | contents: "write" 23 | pull-requests: "write" -------------------------------------------------------------------------------- /.github/workflows/docs.yaml: -------------------------------------------------------------------------------- 1 | concurrency: 2 | cancel-in-progress: true 3 | group: "${{ github.workflow }}-${{ github.ref }}" 4 | jobs: 5 | build: 6 | permissions: 7 | id-token: "write" 8 | pages: "write" 9 | runs-on: "ubuntu-latest" 10 | steps: 11 | - name: "Checkout" 12 | uses: "actions/checkout@v4" 13 | with: 14 | fetch-depth: 0 15 | - name: "Setup mdBook" 16 | uses: "peaceiris/actions-mdbook@v2" 17 | with: 18 | mdbook-version: "latest" 19 | - name: "Install mdbook plugins" 20 | run: | 21 | plugins=( 22 | mdbook-admonish 23 | mdbook-linkcheck 24 | mdbook-i18n-helpers 25 | mdbook-toc 26 | mdbook-cmdrun 27 | ) 28 | for i in "${plugins[@]}" 29 | do 30 | cargo install "$i" 31 | done 32 | shell: "bash" 33 | - name: "Build" 34 | run: | 35 | mdbook build 36 | for po_lang in zh-CN 37 | do 38 | POT_CREATION_DATE=$(grep --max-count 1 '^"POT-Creation-Date:' po/$po_lang.po | sed -E 's/".*: (.*)\\n"/\1/') 39 | if [[ $POT_CREATION_DATE == "" ]]; then 40 | POT_CREATION_DATE=now 41 | fi 42 | echo "::group::Building $po_lang translation as of $POT_CREATION_DATE" 43 | rm -r docs/ 44 | git restore --source "$(git rev-list -n 1 --before "$POT_CREATION_DATE" @)" docs/ 45 | # Set language and adjust site URL. Clear the redirects 46 | # since they are in sync with the source files, not the 47 | # translation. 48 | MDBOOK_BOOK__LANGUAGE=$po_lang \ 49 | MDBOOK_OUTPUT__HTML__SITE_URL=/rsetup/$po_lang/ \ 50 | MDBOOK_OUTPUT__HTML__REDIRECT='{}' \ 51 | mdbook build -d book/$po_lang 52 | mv book/$po_lang/html book/html/$po_lang 53 | echo "::endgroup::" 54 | done 55 | shell: "bash" 56 | - name: "Setup Pages" 57 | uses: "actions/configure-pages@v5" 58 | - name: "Upload artifact" 59 | uses: "actions/upload-pages-artifact@v3" 60 | with: 61 | path: "./book/html" 62 | - if: "github.event_name != 'pull_request'" 63 | name: "Deploy to GitHub Pages" 64 | uses: "actions/deploy-pages@v4" 65 | name: "Deploy documentation" 66 | "on": 67 | merge_group: {} 68 | pull_request: 69 | paths: 70 | - "docs/**" 71 | - "theme/**" 72 | - "po/**" 73 | - "book.toml" 74 | - ".github/workflows/docs.yaml" 75 | push: 76 | branches: 77 | - "main" 78 | paths: 79 | - "docs/**" 80 | - "theme/**" 81 | - "po/**" 82 | - "book.toml" 83 | - ".github/workflows/docs.yaml" 84 | workflow_dispatch: {} 85 | permissions: {} -------------------------------------------------------------------------------- /.github/workflows/new_version.yaml: -------------------------------------------------------------------------------- 1 | jobs: 2 | release: 3 | permissions: 4 | contents: "write" 5 | runs-on: "ubuntu-latest" 6 | steps: 7 | - name: "Checkout" 8 | uses: "actions/checkout@v4" 9 | with: 10 | fetch-depth: 0 11 | submodules: "recursive" 12 | token: "${{secrets.GIT_PUSH_TOKEN}}" 13 | - if: "github.event.inputs.update == 'true'" 14 | name: "Update submodules" 15 | run: | 16 | git config user.name "github-actions[bot]" 17 | git config user.email "41898282+github-actions[bot]@users.noreply.github.com" 18 | git submodule update --remote 19 | if git diff --quiet; then 20 | echo "Submodules are the latest. Nothing to update." 21 | exit 22 | fi 23 | git add . 24 | git commit -m "chore: update submodules" 25 | shell: "bash" 26 | - name: "Set up QEMU Emulation" 27 | uses: "docker/setup-qemu-action@v3" 28 | with: 29 | image: "tonistiigi/binfmt:latest" 30 | - name: "Test" 31 | uses: "devcontainers/ci@v0.3" 32 | with: 33 | push: "never" 34 | runCmd: | 35 | sudo apt-get update 36 | sudo apt-get install --no-install-recommends -y git-buildpackage 37 | export DEBEMAIL="dev@radxa.com" 38 | export DEBFULLNAME='"Radxa Computer Co., Ltd"' 39 | git config user.name "github-actions[bot]" 40 | git config user.email "41898282+github-actions[bot]@users.noreply.github.com" 41 | make dch 42 | make test deb 43 | - if: "github.event.inputs.release == 'true'" 44 | name: "Push" 45 | run: | 46 | git push 47 | shell: "bash" 48 | name: "Create release" 49 | "on": 50 | workflow_dispatch: 51 | inputs: 52 | release: 53 | default: true 54 | description: "Release new version" 55 | required: true 56 | type: "boolean" 57 | update: 58 | default: false 59 | description: "Update submodule" 60 | required: true 61 | type: "boolean" 62 | permissions: {} 63 | run-name: "${{ inputs.update && 'Update submodule' || '' }}${{ inputs.update && inputs.release && ' & ' || '' }}${{ inputs.release && 'Release new version' }}${{ !inputs.update && !inputs.release && 'Test for new release' || '' }}" -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | jobs: 2 | build: 3 | outputs: 4 | distro: "${{ steps.distro_check.outputs.distro }}" 5 | runs-on: "ubuntu-latest" 6 | steps: 7 | - name: "Checkout" 8 | uses: "actions/checkout@v4" 9 | with: 10 | fetch-depth: 0 11 | fetch-tags: true 12 | submodules: "recursive" 13 | - name: "Set up QEMU Emulation" 14 | uses: "docker/setup-qemu-action@v3" 15 | with: 16 | image: "tonistiigi/binfmt:latest" 17 | - name: "Test" 18 | uses: "devcontainers/ci@v0.3" 19 | with: 20 | push: "never" 21 | runCmd: | 22 | sudo apt-get update 23 | sudo apt-get install --no-install-recommends -y git-buildpackage 24 | export DEBEMAIL="dev@radxa.com" 25 | export DEBFULLNAME='"Radxa Computer Co., Ltd"' 26 | git config user.name "github-actions[bot]" 27 | git config user.email "41898282+github-actions[bot]@users.noreply.github.com" 28 | git branch -m GITHUB_RUNNER || true 29 | git branch -D main || true 30 | git switch -c main || true 31 | make dch 32 | make test deb 33 | git reset --hard HEAD~1 34 | rm ../*.deb 35 | - name: "Build" 36 | uses: "devcontainers/ci@v0.3" 37 | with: 38 | push: "never" 39 | runCmd: | 40 | make deb 41 | - id: "artifacts_path" 42 | name: "Workaround actions/upload-artifact#176" 43 | run: | 44 | echo "artifacts_path=$(realpath ..)" >> "$GITHUB_OUTPUT" 45 | shell: "bash" 46 | - name: "Upload artifacts" 47 | uses: "actions/upload-artifact@v4" 48 | with: 49 | name: "${{ github.event.repository.name }}" 50 | path: | 51 | ${{ steps.artifacts_path.outputs.artifacts_path }}/*.deb 52 | - id: "distro_check" 53 | name: "Check if the latest version is releasable" 54 | run: | 55 | version="$(dpkg-parsechangelog -S Version)" 56 | version="${version//\~/.}" 57 | if [[ -n "$(git tag -l "$version")" ]] 58 | then 59 | echo "distro=UNRELEASED" >> "$GITHUB_OUTPUT" 60 | else 61 | echo "distro=$(dpkg-parsechangelog -S Distribution)" >> "$GITHUB_OUTPUT" 62 | fi 63 | shell: "bash" 64 | release: 65 | if: "${{ github.event_name != 'pull_request' && needs.build.outputs.distro != 'UNRELEASED' }}" 66 | needs: "build" 67 | permissions: 68 | contents: "write" 69 | runs-on: "ubuntu-latest" 70 | steps: 71 | - name: "Checkout" 72 | uses: "actions/checkout@v4" 73 | - name: "Download generated debs" 74 | uses: "actions/download-artifact@v4" 75 | with: 76 | name: "${{ github.event.repository.name }}" 77 | path: ".artifacts" 78 | - name: "Prepare for release" 79 | run: | 80 | version="$(dpkg-parsechangelog -S Version)" 81 | version="${version//\~/.}" 82 | echo "version=$version" >> $GITHUB_ENV 83 | echo "changes<> $GITHUB_ENV 84 | echo '```' >> $GITHUB_ENV 85 | echo "$(dpkg-parsechangelog -S Changes)" >> $GITHUB_ENV 86 | echo '```' >> $GITHUB_ENV 87 | echo "EOF" >> $GITHUB_ENV 88 | echo "$version" > VERSION 89 | if [[ -f pkg.conf.template ]] 90 | then 91 | sed "s/VERSION/$version/g" pkg.conf.template > pkg.conf 92 | fi 93 | shell: "bash" 94 | - name: "Release" 95 | uses: "softprops/action-gh-release@v2" 96 | with: 97 | body_path: "README.md" 98 | draft: false 99 | fail_on_unmatched_files: false 100 | files: | 101 | .artifacts/**/*.deb 102 | pkg.conf 103 | VERSION 104 | prerelease: false 105 | tag_name: "${{ env.version }}" 106 | target_commitish: "${{ github.sha }}" 107 | token: "${{ secrets.GITHUB_TOKEN }}" 108 | - name: "Append changelog" 109 | uses: "softprops/action-gh-release@v2" 110 | with: 111 | append_body: true 112 | body: | 113 | ## Changelog for ${{ env.version }} 114 | ${{ env.changes }} 115 | tag_name: "${{ env.version }}" 116 | - name: "Update Test repos" 117 | uses: "RadxaOS-SDK/rsdk/.github/actions/infra-repo-update@main" 118 | with: 119 | test-repo: true 120 | token: "${{ secrets.RADXA_APT_TEST_REPO_TOKEN }}" 121 | name: "Build & Release" 122 | "on": 123 | merge_group: {} 124 | pull_request: 125 | paths-ignore: 126 | - "docs/**" 127 | - "theme/**" 128 | - "po/**" 129 | - "book.toml" 130 | - ".github/workflows/docs.yaml" 131 | push: 132 | branches: 133 | - "main" 134 | paths-ignore: 135 | - "docs/**" 136 | - "theme/**" 137 | - "po/**" 138 | - "book.toml" 139 | - ".github/workflows/docs.yaml" 140 | workflow_dispatch: {} 141 | permissions: {} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Devenv 2 | /.devenv* 3 | /devenv.local.nix 4 | 5 | # direnv 6 | /.direnv 7 | 8 | # pre-commit 9 | /.pre-commit-config.yaml 10 | 11 | # Local VS Code configuration 12 | /.vscode* 13 | 14 | # Kernel build artifacts 15 | /*.deb 16 | /*.buildinfo 17 | /*.changes 18 | /.pc 19 | 20 | # Output folder 21 | /out 22 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "externals/librtui"] 2 | path = externals/librtui 3 | url = https://github.com/radxa-pkg/librtui 4 | branch = main 5 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/detailyang/pre-commit-shell 3 | rev: v1.0.6 4 | hooks: 5 | - id: shell-lint 6 | args: [-x] 7 | - repo: https://github.com/pre-commit/mirrors-prettier 8 | rev: v3.0.3 9 | hooks: 10 | - id: prettier 11 | - repo: https://github.com/pre-commit/pre-commit-hooks 12 | rev: v4.4.0 13 | hooks: 14 | - id: check-added-large-files 15 | args: [--maxkb=2048] 16 | - id: check-case-conflict 17 | - id: check-json 18 | - id: check-merge-conflict 19 | - id: check-yaml 20 | - id: end-of-file-fixer 21 | - id: fix-byte-order-marker 22 | - id: mixed-line-ending 23 | - id: pretty-format-json 24 | args: [--autofix, --no-ensure-ascii] 25 | - id: trailing-whitespace 26 | args: [--markdown-linebreak-ext=md] 27 | - repo: https://github.com/pre-commit/mirrors-prettier 28 | rev: v3.0.3 29 | hooks: 30 | - id: prettier 31 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "overrides": [ 3 | { 4 | "files": "**/*.hbs", 5 | "options": { 6 | "parser": "angular" 7 | } 8 | } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | -include .github/local/Makefile.local 2 | -include Makefile.extra 3 | 4 | PROJECT ?= rsetup 5 | CUSTOM_DEBUILD_ENV ?= DEB_BUILD_OPTIONS='parallel=1' 6 | 7 | .DEFAULT_GOAL := all 8 | .PHONY: all 9 | all: build 10 | 11 | .PHONY: devcontainer_setup 12 | devcontainer_setup: 13 | sudo dpkg --add-architecture arm64 14 | sudo apt-get update 15 | sudo apt-get build-dep . -y 16 | 17 | # 18 | # Test 19 | # 20 | .PHONY: test 21 | test: 22 | 23 | # 24 | # Build 25 | # 26 | .PHONY: build 27 | build: pre_build main_build post_build 28 | 29 | .PHONY: pre_build 30 | pre_build: 31 | # Fix file permissions when created from template 32 | chmod +x debian/rules 33 | 34 | .PHONY: main_build 35 | main_build: 36 | 37 | .PHONY: post_build 38 | post_build: 39 | 40 | # 41 | # Clean 42 | # 43 | .PHONY: distclean 44 | distclean: clean 45 | 46 | .PHONY: clean 47 | clean: clean-deb 48 | 49 | .PHONY: clean-deb 50 | clean-deb: 51 | rm -rf debian/.debhelper debian/$(PROJECT)*/ debian/tmp/ debian/debhelper-build-stamp debian/files debian/*.debhelper.log debian/*.*.debhelper debian/*.substvars 52 | 53 | # 54 | # Release 55 | # 56 | .PHONY: dch 57 | dch: debian/changelog 58 | gbp dch --ignore-branch --multimaint-merge --release --spawn-editor=never \ 59 | --git-log='--no-merges --perl-regexp --invert-grep --grep=^(chore:\stemplates\sgenerated)' \ 60 | --dch-opt=--upstream --commit --commit-msg="feat: release %(version)s" 61 | 62 | .PHONY: deb 63 | deb: debian 64 | $(CUSTOM_DEBUILD_ENV) debuild --no-lintian --lintian-hook "lintian --fail-on error,warning --suppress-tags-from-file $(PWD)/debian/common-lintian-overrides -- %p_%v_*.changes" --no-sign -b 65 | 66 | .PHONY: release 67 | release: 68 | gh workflow run .github/workflows/new_version.yaml --ref $(shell git branch --show-current) 69 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rsetup 2 | 3 | [![Release](https://github.com/radxa-pkg/rsetup/actions/workflows/release.yaml/badge.svg)](https://github.com/radxa-pkg/rsetup/actions/workflows/release.yaml) 4 | 5 | ## Build 6 | 7 | 1. `git clone --recurse-submodules https://github.com/radxa-pkg/rsetup.git` 8 | 2. Open in [`devcontainer`](https://code.visualstudio.com/docs/devcontainers/containers) 9 | 3. `make deb` 10 | -------------------------------------------------------------------------------- /book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["Radxa Limited"] 3 | language = "en" 4 | multilingual = true 5 | src = "docs" 6 | title = "rsetup" 7 | 8 | [build] 9 | extra-watch-dirs = ["po"] 10 | 11 | [output.html] 12 | git-repository-url = "https://github.com/radxa-repo/rsetup/tree/main" 13 | edit-url-template = "https://github.com/radxa-repo/rsetup/edit/main/{path}" 14 | additional-css = [ 15 | "./theme/css/mdbook-admonish.css", 16 | "./theme/css/language-picker.css", 17 | ] 18 | 19 | [preprocessor] 20 | 21 | [preprocessor.gettext] 22 | after = ["links"] 23 | 24 | [preprocessor.admonish] 25 | command = "mdbook-admonish" 26 | assets_version = "3.0.0" # do not edit: managed by `mdbook-admonish install` 27 | -------------------------------------------------------------------------------- /config/00-aic8800-reset-ttyas1.conf: -------------------------------------------------------------------------------- 1 | # Allow config to continue when /dev/ttyAS1 is not available 2 | no_fail 3 | # Reset AIC8800 Bluetooth on /dev/ttyAS1 4 | enable_service rsetup-aic8800-reset@ttyAS1 5 | -------------------------------------------------------------------------------- /config/00-aic8800-reset-ttys1.conf: -------------------------------------------------------------------------------- 1 | # Allow config to continue when /dev/ttyS1 is not available 2 | no_fail 3 | # Reset AIC8800 Bluetooth on /dev/ttyS1 4 | enable_service rsetup-aic8800-reset@ttyS1 5 | -------------------------------------------------------------------------------- /config/00-lan-led_enp1s0.conf: -------------------------------------------------------------------------------- 1 | # Allow config to continue when lan-led LED is not available 2 | no_fail 3 | # Set lan-led LED's netdev to enp1s0 4 | set_led_netdev lan-led enp1s0 5 | -------------------------------------------------------------------------------- /config/00-lan-led_eth0.conf: -------------------------------------------------------------------------------- 1 | # Allow config to continue when lan-led LED is not available 2 | no_fail 3 | # Set lan-led LED's netdev to eth0 4 | set_led_netdev lan-led eth0 5 | -------------------------------------------------------------------------------- /config/00-rgb0-rainbow.conf: -------------------------------------------------------------------------------- 1 | # Allow config to continue when rgb0 LED is not available 2 | no_fail 3 | # Set rgb0 LED's pattern to rainbow 4 | set_led_trigger rgb0-red pattern 5 | set_led_pattern rgb0-red 255 833 255 833 0 833 0 833 0 833 255 833 6 | set_led_trigger rgb0-green pattern 7 | set_led_pattern rgb0-green 0 833 255 833 255 833 255 833 0 833 0 833 8 | set_led_trigger rgb0-blue pattern 9 | set_led_pattern rgb0-blue 0 833 0 833 0 833 255 833 255 833 255 833 10 | -------------------------------------------------------------------------------- /config/00-thermal_governor-step_wise.conf: -------------------------------------------------------------------------------- 1 | # Set default thermal governor to step_wise 2 | set_thermal_governor step_wise 3 | -------------------------------------------------------------------------------- /config/00-wan-led_enp1s0.conf: -------------------------------------------------------------------------------- 1 | # Allow config to continue when wan-led LED is not available 2 | no_fail 3 | # Set wan-led LED's netdev to enp1s0 4 | set_led_netdev wan-led enp1s0 5 | -------------------------------------------------------------------------------- /config/00-wan-led_eth0.conf: -------------------------------------------------------------------------------- 1 | # Allow config to continue when wan-led LED is not available 2 | no_fail 3 | # Set wan-led LED's netdev to eth0 4 | set_led_netdev wan-led eth0 5 | -------------------------------------------------------------------------------- /config/01-hciattach-ttys1.conf: -------------------------------------------------------------------------------- 1 | # Allow config to continue when /dev/ttyS1 is not available 2 | no_fail 3 | # Attach UART Bluetooth on /dev/ttyS1 4 | enable_service rsetup-hciattach@ttyS1 5 | -------------------------------------------------------------------------------- /config/before.txt: -------------------------------------------------------------------------------- 1 | # ============================== 2 | # Radxa First Boot Configuration 3 | # ============================== 4 | 5 | # Allow config to continue even when some commands fail 6 | no_fail 7 | log "Running before.txt as first boot configuration" 8 | 9 | # Update generic hostname 10 | # Command: 11 | # update_generic_hostname 12 | update_generic_hostname rock-2 radxa-rk3528 radxa-rk3576 13 | 14 | # Create default accounts 15 | # Commands: 16 | # add_user 17 | # user_append_group 18 | # 19 | add_user radxa radxa 20 | user_append_group radxa sudo 21 | user_append_group radxa audio 22 | user_append_group radxa video 23 | user_append_group radxa plugdev 24 | user_append_group radxa render 25 | user_append_group radxa gpio 26 | user_append_group radxa i2c 27 | user_append_group radxa spidev 28 | user_append_group radxa pwm 29 | 30 | add_user rock rock 31 | user_append_group rock sudo 32 | user_append_group rock audio 33 | user_append_group rock video 34 | user_append_group rock plugdev 35 | user_append_group rock render 36 | user_append_group rock gpio 37 | user_append_group rock i2c 38 | user_append_group rock spidev 39 | user_append_group rock pwm 40 | 41 | # Resize root partition at the filesystem level 42 | # 43 | resize_root 44 | 45 | # Disable services 46 | # Command: 47 | # disable_service 48 | # 49 | disable_service ssh 50 | disable_service ssh.socket 51 | disable_service smbd 52 | disable_service nmbd 53 | # Disable systemd-networkd due to systemd-networkd-wait-online blocking network.target 54 | # We use NetworkManger, so systemd-networkd can be safely disabled 55 | disable_service systemd-networkd 56 | 57 | # Generate unique hardware fingerprint 58 | # 59 | regenerate_ssh_hostkey 60 | 61 | # Configure locale 62 | # Command: 63 | # update_locale 64 | # 65 | update_locale en_US.UTF-8 66 | 67 | # Connect to Wi-Fi 68 | # Command: 69 | # connect_wi-fi [password] 70 | # 71 | #connect_wi-fi private_network password 72 | 73 | # Enable SSH on headless first boot 74 | # Keyword: 75 | # if|if_not 76 | # Condition: 77 | # headless: When no display connector is connected 78 | # Command: 79 | # enable_service 80 | # 81 | if headless enable_service ssh 82 | 83 | # Remove first-boot package 84 | # Command: 85 | # remove_packages 86 | # 87 | remove_packages rsetup-config-first-boot 88 | -------------------------------------------------------------------------------- /config/config.txt: -------------------------------------------------------------------------------- 1 | # rsetup config file 2 | # Configurations here will be applied on every boot. 3 | # 4 | # Additionally you can create `before.txt` and `after.txt` in this folder. 5 | # They will run before and after this file. 6 | # They will be deleted after the configuration process is completed. 7 | # This makes them suitable for one-time setup. 8 | # 9 | # Example can be found in the default `before.txt`: 10 | # https://github.com/radxa-pkg/rsetup/blob/main/config/before.txt 11 | -------------------------------------------------------------------------------- /config/hw_intfc.conf: -------------------------------------------------------------------------------- 1 | # hw_intfc.conf has been retired. 2 | # Please use `rsetup` command to confiure device overlays. 3 | -------------------------------------------------------------------------------- /config/uEnv.txt: -------------------------------------------------------------------------------- 1 | # uEnv.txt has been retired. 2 | # Please use: 3 | # `rsetup` command to confiure device overlays; 4 | # `sudo nano /etc/kernel/cmdline && sudo u-boot-update` to update kernel arguments. 5 | -------------------------------------------------------------------------------- /debian/.gitignore: -------------------------------------------------------------------------------- 1 | /.debhelper 2 | /debhelper-build-stamp 3 | /files 4 | /rsetup*/ 5 | /*.debhelper.log 6 | /*.postrm.debhelper 7 | /*.substvars 8 | -------------------------------------------------------------------------------- /debian/common-lintian-overrides: -------------------------------------------------------------------------------- 1 | # We do not track this on GitHub 2 | initial-upload-closes-no-bugs 3 | 4 | # We might use our own distribution name 5 | bad-distribution-in-changes-file 6 | 7 | # Our package is built on GitHub-hosted runner, 8 | # which uses Ubuntu, and will default to zstd compression. 9 | # This was not supported in Debian 11. 10 | custom-compression-in-debian-rules 11 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 12 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: rsetup 2 | Maintainer: "Radxa Computer Co., Ltd" 3 | Section: admin 4 | Priority: standard 5 | Standards-Version: 4.6.0 6 | Build-Depends: debhelper (>=12~), 7 | devscripts, 8 | lintian, 9 | dh-exec, 10 | pandoc, 11 | shellcheck, 12 | 13 | Package: rsetup 14 | Architecture: all 15 | Section: admin 16 | Priority: standard 17 | Depends: device-tree-compiler, 18 | gdisk, 19 | jq, 20 | librtui (>= 0.2.0), 21 | parted, 22 | pkexec | policykit-1 (<< 122-1), 23 | python3, 24 | python3-yaml, 25 | u-boot-menu, 26 | ${misc:Depends}, 27 | Suggests: aicrf-test, 28 | gpiod, 29 | sudo, 30 | x-terminal-emulator, 31 | Description: Radxa system setup utility 32 | Radxa system setup utility (rsetup) provides an user friendly menu to 33 | perform many essential system configuration tasks. It also provides 34 | an on-boot configuration service to allow customization. 35 | 36 | Package: rsetup-config-thermal-governor-step-wise 37 | Architecture: all 38 | Section: admin 39 | Priority: standard 40 | Depends: rsetup (= ${binary:Version}), 41 | ${misc:Depends}, 42 | Description: Radxa system config - Set thermal_governor to step_wise 43 | This package sets thermal_governor to step_wise on boot. 44 | 45 | Package: rsetup-config-first-boot 46 | Architecture: all 47 | Section: admin 48 | Priority: standard 49 | Depends: rsetup (= ${binary:Version}), 50 | ${misc:Depends}, 51 | Description: Radxa system config - First boot configuration 52 | This package contains the default first boot script. 53 | 54 | Package: rsetup-config-aic8800-ttyas1 55 | Architecture: all 56 | Section: admin 57 | Priority: standard 58 | Depends: rsetup (= ${binary:Version}), 59 | ${misc:Depends}, 60 | Description: Radxa system config - Attach AIC8800 Bluetooth on /dev/ttyAS1 61 | This package attaches AIC8800 Bluetooth on /dev/ttyAS1 on boot. 62 | 63 | Package: rsetup-config-aic8800-ttys1 64 | Architecture: all 65 | Section: admin 66 | Priority: standard 67 | Depends: rsetup (= ${binary:Version}), 68 | ${misc:Depends}, 69 | Provides: rsetup-config-hciattach-ttys1 (= 0.4.0) 70 | Conflicts: rsetup-config-hciattach-ttys1 (<< 0.4.0) 71 | Replaces: rsetup-config-hciattach-ttys1 (<< 0.4.0) 72 | Description: Radxa system config - Attach AIC8800 Bluetooth on /dev/ttyS1 73 | This package attaches AIC8800 Bluetooth on /dev/ttyS1 on boot. 74 | 75 | Package: rsetup-config-rgb0-rainbow 76 | Architecture: all 77 | Section: admin 78 | Priority: standard 79 | Depends: rsetup (= ${binary:Version}), 80 | ${misc:Depends}, 81 | Description: Radxa system config - Set rgb0 LED's pattern to rainbow 82 | This package set rgb0 LED's pattern to rainbow on boot. 83 | 84 | Package: rsetup-config-wan-led-eth0 85 | Architecture: all 86 | Section: admin 87 | Priority: standard 88 | Depends: rsetup (= ${binary:Version}), 89 | ${misc:Depends}, 90 | Provides: rsetup-config-wan-led-netdev 91 | Description: Radxa system config - Set wan-led LED's netdev to eth0 92 | This package set wan-led LED's netdev to eth0 on boot. 93 | 94 | Package: rsetup-config-wan-led-enp1s0 95 | Architecture: all 96 | Section: admin 97 | Priority: standard 98 | Depends: rsetup (= ${binary:Version}), 99 | ${misc:Depends}, 100 | Provides: rsetup-config-wan-led-netdev 101 | Description: Radxa system config - Set wan-led LED's netdev to enp1s0 102 | This package set wan-led LED's netdev to enp1s0 on boot. 103 | 104 | Package: rsetup-config-lan-led-eth0 105 | Architecture: all 106 | Section: admin 107 | Priority: standard 108 | Depends: rsetup (= ${binary:Version}), 109 | ${misc:Depends}, 110 | Provides: rsetup-config-lan-led-netdev 111 | Description: Radxa system config - Set lan-led LED's netdev to eth0 112 | This package set lan-led LED's netdev to eth0 on boot. 113 | 114 | Package: rsetup-config-lan-led-enp1s0 115 | Architecture: all 116 | Section: admin 117 | Priority: standard 118 | Depends: rsetup (= ${binary:Version}), 119 | ${misc:Depends}, 120 | Provides: rsetup-config-lan-led-netdev 121 | Description: Radxa system config - Set lan-led LED's netdev to enp1s0 122 | This package set lan-led LED's netdev to enp1s0 on boot. 123 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: rsetup 3 | Source: https://github.com/radxa-pkg/rsetup 4 | 5 | Files: * 6 | Copyright: © 2025 Radxa Computer Co., Ltd 7 | License: GPL-3+ 8 | 9 | License: GPL-3+ 10 | This program is free software; you can redistribute it and/or modify 11 | it under the terms of the GNU General Public License as published by 12 | the Free Software Foundation; either version 3, or (at your option) 13 | any later version. 14 | . 15 | This program is distributed in the hope that it will be useful, but 16 | WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 | General Public License for more details. 19 | . 20 | You should have received a copy of the GNU General Public License 21 | along with this program; if not, write to the Free Software 22 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 23 | 02110-1301, USA. 24 | . 25 | On Debian systems, the complete text of the GNU General Public License 26 | can be found in /usr/share/common-licenses/GPL-3. 27 | -------------------------------------------------------------------------------- /debian/rsetup-config-aic8800-ttyas1.install: -------------------------------------------------------------------------------- 1 | config/00-aic8800-reset-ttyas1.conf /usr/lib/rsetup/conf.d/on_boot 2 | -------------------------------------------------------------------------------- /debian/rsetup-config-aic8800-ttys1.install: -------------------------------------------------------------------------------- 1 | config/00-aic8800-reset-ttys1.conf /usr/lib/rsetup/conf.d/on_boot 2 | -------------------------------------------------------------------------------- /debian/rsetup-config-first-boot.docs: -------------------------------------------------------------------------------- 1 | config/before.txt 2 | -------------------------------------------------------------------------------- /debian/rsetup-config-first-boot.postinst: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | action="$1" 6 | 7 | if [ "$action" = configure ] 8 | then 9 | if [ ! -e /config ] 10 | then 11 | mkdir /config 12 | fi 13 | if [ ! -e /config/before.txt ] 14 | then 15 | cp /usr/share/doc/rsetup-config-first-boot/before.txt /config/before.txt 16 | fi 17 | fi 18 | 19 | #DEBHELPER# 20 | 21 | exit 0 22 | -------------------------------------------------------------------------------- /debian/rsetup-config-hciattach-ttys1.install: -------------------------------------------------------------------------------- 1 | config/01-hciattach-ttys1.conf /usr/lib/rsetup/conf.d/on_boot 2 | -------------------------------------------------------------------------------- /debian/rsetup-config-lan-led-enp1s0.install: -------------------------------------------------------------------------------- 1 | config/00-lan-led_enp1s0.conf /usr/lib/rsetup/conf.d/on_boot 2 | -------------------------------------------------------------------------------- /debian/rsetup-config-lan-led-eth0.install: -------------------------------------------------------------------------------- 1 | config/00-lan-led_eth0.conf /usr/lib/rsetup/conf.d/on_boot 2 | -------------------------------------------------------------------------------- /debian/rsetup-config-rgb0-rainbow.install: -------------------------------------------------------------------------------- 1 | config/00-rgb0-rainbow.conf /usr/lib/rsetup/conf.d/on_boot 2 | -------------------------------------------------------------------------------- /debian/rsetup-config-thermal-governor-step-wise.install: -------------------------------------------------------------------------------- 1 | config/00-thermal_governor-step_wise.conf /usr/lib/rsetup/conf.d/on_boot 2 | -------------------------------------------------------------------------------- /debian/rsetup-config-wan-led-enp1s0.install: -------------------------------------------------------------------------------- 1 | config/00-wan-led_enp1s0.conf /usr/lib/rsetup/conf.d/on_boot 2 | -------------------------------------------------------------------------------- /debian/rsetup-config-wan-led-eth0.install: -------------------------------------------------------------------------------- 1 | config/00-wan-led_eth0.conf /usr/lib/rsetup/conf.d/on_boot 2 | -------------------------------------------------------------------------------- /debian/rsetup.docs: -------------------------------------------------------------------------------- 1 | config/config.txt 2 | config/hw_intfc.conf 3 | config/uEnv.txt 4 | -------------------------------------------------------------------------------- /debian/rsetup.install: -------------------------------------------------------------------------------- 1 | src/usr/bin /usr 2 | src/usr/lib/rsetup /usr/lib 3 | src/usr/share/applications /usr/share 4 | src/usr/share/man/man8/rsetup.8 /usr/share/man/man8 5 | src/usr/share/bash-completion /usr/share 6 | -------------------------------------------------------------------------------- /debian/rsetup.links: -------------------------------------------------------------------------------- 1 | #!/usr/bin/dh-exec 2 | 3 | /usr/bin/rsetup /etc/kernel/postinst.d/yz-update-overlays 4 | /usr/bin/rsetup /etc/kernel/postrm.d/yz-update-overlays 5 | -------------------------------------------------------------------------------- /debian/rsetup.lintian-overrides: -------------------------------------------------------------------------------- 1 | # Small bug number is very probable for us. 2 | rsetup: improbable-bug-number-in-closes 3 | 4 | # We depends on x-terminal-emulator 5 | rsetup: desktop-command-not-in-package 6 | 7 | rsetup: groff-message 8 | -------------------------------------------------------------------------------- /debian/rsetup.postinst: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | action="$1" 6 | 7 | if [ "$action" = configure ] 8 | then 9 | if [ ! -e /config ] 10 | then 11 | mkdir /config 12 | fi 13 | if [ ! -e /config/config.txt ] 14 | then 15 | cp /usr/share/doc/rsetup/config.txt /config/config.txt 16 | fi 17 | if [ ! -e /boot ] 18 | then 19 | mkdir /boot 20 | fi 21 | if [ ! -e /boot/hw_intfc.conf ] 22 | then 23 | cp /usr/share/doc/rsetup/hw_intfc.conf /boot/hw_intfc.conf 24 | fi 25 | if [ ! -e /boot/uEnv.txt ] 26 | then 27 | cp /usr/share/doc/rsetup/uEnv.txt /boot/uEnv.txt 28 | fi 29 | fi 30 | 31 | #DEBHELPER# 32 | 33 | exit 0 34 | -------------------------------------------------------------------------------- /debian/rsetup.rsetup-aic8800-reset@.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Reset AIC8800 Bluetooth on %I 3 | Documentation=https://github.com/radxa-pkg/rsetup/ 4 | Wants=rsetup-hciattach@%i.service 5 | Before=rsetup-hciattach@%i.service 6 | 7 | [Service] 8 | ExecStart=/usr/bin/rsetup __aic8800_reset %I 9 | 10 | [Install] 11 | WantedBy=multi-user.target 12 | -------------------------------------------------------------------------------- /debian/rsetup.rsetup-hciattach@.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=hciattach on %I 3 | Documentation=https://github.com/radxa-pkg/rsetup/ 4 | BindsTo=dev-%i.device 5 | After=dev-%i.device 6 | 7 | [Service] 8 | Type=forking 9 | ExecStart=/usr/bin/hciattach -s 1500000 /dev/%I any 1500000 flow nosleep 10 | 11 | [Install] 12 | WantedBy=multi-user.target 13 | -------------------------------------------------------------------------------- /debian/rsetup.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=rsetup configuration service 3 | Documentation=https://github.com/radxa-pkg/rsetup/ 4 | 5 | [Service] 6 | Type=simple 7 | ExecStart=/usr/bin/rsetup __on_boot 8 | 9 | [Install] 10 | WantedBy=multi-user.target 11 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | include /usr/share/dpkg/pkg-info.mk 4 | include /usr/share/dpkg/architecture.mk 5 | -include .github/local/rules.local 6 | 7 | %: 8 | dh $@ 9 | 10 | override_dh_builddeb: 11 | dh_builddeb -- -Zxz 12 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (native) 2 | -------------------------------------------------------------------------------- /debian/source/lintian-overrides: -------------------------------------------------------------------------------- 1 | # Our package is built on GitHub-hosted runner, 2 | # which uses Ubuntu, and will default to zstd compression. 3 | # This is currently not supported in Debian. 4 | rsetup source: custom-compression-in-debian-rules 5 | -------------------------------------------------------------------------------- /devenv.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "devenv": { 4 | "locked": { 5 | "dir": "src/modules", 6 | "lastModified": 1749934215, 7 | "owner": "cachix", 8 | "repo": "devenv", 9 | "rev": "0ad2d684f722b41578b34670428161d996382e64", 10 | "type": "github" 11 | }, 12 | "original": { 13 | "dir": "src/modules", 14 | "owner": "cachix", 15 | "repo": "devenv", 16 | "type": "github" 17 | } 18 | }, 19 | "flake-compat": { 20 | "flake": false, 21 | "locked": { 22 | "lastModified": 1747046372, 23 | "owner": "edolstra", 24 | "repo": "flake-compat", 25 | "rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885", 26 | "type": "github" 27 | }, 28 | "original": { 29 | "owner": "edolstra", 30 | "repo": "flake-compat", 31 | "type": "github" 32 | } 33 | }, 34 | "git-hooks": { 35 | "inputs": { 36 | "flake-compat": "flake-compat", 37 | "gitignore": "gitignore", 38 | "nixpkgs": [ 39 | "nixpkgs" 40 | ] 41 | }, 42 | "locked": { 43 | "lastModified": 1749636823, 44 | "owner": "cachix", 45 | "repo": "git-hooks.nix", 46 | "rev": "623c56286de5a3193aa38891a6991b28f9bab056", 47 | "type": "github" 48 | }, 49 | "original": { 50 | "owner": "cachix", 51 | "repo": "git-hooks.nix", 52 | "type": "github" 53 | } 54 | }, 55 | "gitignore": { 56 | "inputs": { 57 | "nixpkgs": [ 58 | "git-hooks", 59 | "nixpkgs" 60 | ] 61 | }, 62 | "locked": { 63 | "lastModified": 1709087332, 64 | "owner": "hercules-ci", 65 | "repo": "gitignore.nix", 66 | "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", 67 | "type": "github" 68 | }, 69 | "original": { 70 | "owner": "hercules-ci", 71 | "repo": "gitignore.nix", 72 | "type": "github" 73 | } 74 | }, 75 | "nixpkgs": { 76 | "locked": { 77 | "lastModified": 1746807397, 78 | "owner": "cachix", 79 | "repo": "devenv-nixpkgs", 80 | "rev": "c5208b594838ea8e6cca5997fbf784b7cca1ca90", 81 | "type": "github" 82 | }, 83 | "original": { 84 | "owner": "cachix", 85 | "ref": "rolling", 86 | "repo": "devenv-nixpkgs", 87 | "type": "github" 88 | } 89 | }, 90 | "root": { 91 | "inputs": { 92 | "devenv": "devenv", 93 | "git-hooks": "git-hooks", 94 | "nixpkgs": "nixpkgs", 95 | "pre-commit-hooks": [ 96 | "git-hooks" 97 | ] 98 | } 99 | } 100 | }, 101 | "root": "root", 102 | "version": 7 103 | } 104 | -------------------------------------------------------------------------------- /devenv.nix: -------------------------------------------------------------------------------- 1 | { pkgs, lib, config, ... }: 2 | 3 | { 4 | imports = lib.optional (builtins.pathExists ./.github/local/devenv.nix) ./.github/local/devenv.nix; 5 | 6 | # https://devenv.sh/packages/ 7 | packages = with pkgs; [ 8 | bash-completion 9 | mdbook 10 | mdbook-admonish 11 | mdbook-cmdrun 12 | mdbook-i18n-helpers 13 | mdbook-linkcheck 14 | mdbook-toc 15 | ncurses 16 | ]; 17 | 18 | git-hooks = { 19 | hooks = { 20 | commitizen.enable = true; 21 | shellcheck = { 22 | enable = true; 23 | entry = lib.mkForce "${pkgs.shellcheck}/bin/shellcheck -x"; 24 | }; 25 | shfmt.enable = true; 26 | statix.enable = true; 27 | typos = { 28 | enable = true; 29 | excludes = [ 30 | "theme/highlight.js" 31 | ]; 32 | settings.ignored-words = [ 33 | "Synopsys" 34 | "HSI" 35 | ]; 36 | }; 37 | }; 38 | }; 39 | 40 | starship.enable = true; 41 | } 42 | -------------------------------------------------------------------------------- /docs/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | # Special topics 4 | 5 | - [Stress test image](special/stress_test.md) 6 | -------------------------------------------------------------------------------- /docs/special/stress_test.md: -------------------------------------------------------------------------------- 1 | # Stress test image 2 | 3 | As of 0.4.1, `rsetup` can be used to create stress test images. This functionality 4 | relies on proper `gpiod` pin definition, as well as correct GPIO LEDs device tree 5 | definition, both are part of the system release requirements. As such, this can 6 | be used as a unified method to create stress test images. 7 | 8 | ## `factory_stress` 9 | 10 | This function is implemented in `factory_stress`, where the detailed command 11 | definition can be found in the source code. 12 | 13 | To run this command, you need to supply a GPIO pin name, which will be connected 14 | to the GND/VCC pin during the device boot, as well as the default state when no 15 | wire is connected. 16 | 17 | The factory stress test procedure begins with the pin connected, which is recognized 18 | by `factory_stress` as a signal to start the stress test, and update LEDs' pattern. 19 | The QA engineer will then disconnect the wire once the LED pattern indicates the 20 | test is started. This way, if the device fails the stress test and is rebooted, 21 | `factory_stress` will not be started again due to the GPIO value does not match 22 | the specified value. 23 | 24 | ## Prepare the image 25 | 26 | This is generally broken into 2 stages: find the GPIO pin's default status, and 27 | update the image. 28 | 29 | ### Find the default GPIO status 30 | 31 | While in theory any GPIO pins can be used, we should look for pins that are easy 32 | to identify. One such example is pin 40 which is at the end of the header, as well 33 | as being close to a GND pin to pull down (if the default state is high). 34 | 35 | For example, on Radxa ZERO, if we want to use pin 7, we should run the following 36 | command on ZERO: 37 | 38 | ```bash 39 | radxa@radxa-zero:~$ gpioget $(gpiofind PIN_7) 40 | 1 41 | ``` 42 | 43 | Since the default state is 1, we will instruct the QA engineer to connect pin 7 44 | and pin 9 (which is GND) at the start of the test. 45 | 46 | ### Update the image with stress test config 47 | 48 | To run the stress test at the boot time, first, mount and `systemd-nspawn` into 49 | the image (you can use `bsp` with `./bsp install stress.img` for this). 50 | 51 | You should then install the required stress test utilities. Currently, they are 52 | `stress-ng` and `memtester`: 53 | 54 | ```bash 55 | sudo apt-get update && sudo apt-get install -y stress-ng memtester 56 | ``` 57 | 58 | You should then delete `/config/before.txt`, which will cause a long boot delay 59 | due to the need to complete the first boot configuration. You can put a very basic 60 | system configuration there (so you can log in to check the issue), and then start 61 | the stress test. Continuing the above example of Radxa ZERO, this is the command 62 | we will use: 63 | 64 | ```bash 65 | sudo rm /config/before.txt 66 | cat << EOF | sudo tee -a /config/config.txt 67 | no_fail 68 | add_user radxa radxa 69 | user_append_group radxa sudo 70 | factory_stress PIN_7 0 71 | EOF 72 | ``` 73 | 74 | Save and unmount the image. You can now deliver the stress test image to the QA 75 | engineer. 76 | 77 | ## Usage 78 | 79 | 1. Connect the GPIO jumper wire as instructed, and boot the system. 80 | 81 | 2. The device will power on normally. Once it reaches the Linux kernel, the LED 82 | should be heartbeating: quick 2 blinks then after some delay, repeat. 83 | 84 | 3. The stress service will then start after the system is fully booted. It will 85 | change the light pattern in 2 ways: 86 | 87 | - Off: This indicates the stress test has failed to start. Please contact for 88 | support. 89 | - Switching at 0.5s interval: This indicates the stress is running. 90 | 91 | 4. Disconnect the GPIO jumper wire. The device status can be checked in a few hours. 92 | 93 | ## Final LED status 94 | 95 | - Switching at 0.5s interval: This indicates the stress is running. **PASS** 96 | - On: This indicates the system was locked up and unable to reboot. **FAIL** 97 | - Off: Either the system was locked up and unable to reboot, or the device rebooted. **FAIL** 98 | -------------------------------------------------------------------------------- /overlay/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radxa-pkg/rsetup/ddc60c82399b088826ef15d9d57b562ab8806d25/overlay/.gitkeep -------------------------------------------------------------------------------- /po/.gitignore: -------------------------------------------------------------------------------- 1 | /*.po~ 2 | -------------------------------------------------------------------------------- /po/messages.pot: -------------------------------------------------------------------------------- 1 | 2 | msgid "" 3 | msgstr "" 4 | "Project-Id-Version: rsetup\n" 5 | "POT-Creation-Date: 2024-03-13T15:33:53+08:00\n" 6 | "PO-Revision-Date: \n" 7 | "Last-Translator: \n" 8 | "Language-Team: \n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "Language: en\n" 13 | "Plural-Forms: nplurals=1; plural=0;\n" 14 | 15 | #: docs/SUMMARY.md:1 16 | msgid "Summary" 17 | msgstr "" 18 | 19 | #: docs/SUMMARY.md:3 20 | msgid "Special topics" 21 | msgstr "" 22 | 23 | #: docs/SUMMARY.md:5 docs/special/stress_test.md:1 24 | msgid "Stress test image" 25 | msgstr "" 26 | 27 | #: docs/special/stress_test.md:3 28 | msgid "" 29 | "As of 0.4.1, `rsetup` can be used to create stress test images. This " 30 | "functionality relies on proper `gpiod` pin definition, as well as correct " 31 | "GPIO LEDs device tree definition, both are part of the system release " 32 | "requirements. As such, this can be used as a unified method to create stress " 33 | "test images." 34 | msgstr "" 35 | 36 | #: docs/special/stress_test.md:8 37 | msgid "`factory_stress`" 38 | msgstr "" 39 | 40 | #: docs/special/stress_test.md:10 41 | msgid "" 42 | "This function is implemented in `factory_stress`, where the detailed command " 43 | "definition can be found in the source code." 44 | msgstr "" 45 | 46 | #: docs/special/stress_test.md:13 47 | msgid "" 48 | "To run this command, you need to supply a GPIO pin name, which will be " 49 | "connected to the GND/VCC pin during the device boot, as well as the default " 50 | "state when no wire is connected." 51 | msgstr "" 52 | 53 | #: docs/special/stress_test.md:17 54 | msgid "" 55 | "The factory stress test procedure begins with the pin connected, which is " 56 | "recognized by `factory_stress` as a signal to start the stress test, and " 57 | "update LEDs' pattern. The QA engineer will then disconnect the wire once the " 58 | "LED pattern indicates the test is started. This way, if the device fails the " 59 | "stress test and is rebooted, `factory_stress` will not be started again due " 60 | "to the GPIO value does not match the specified value." 61 | msgstr "" 62 | 63 | #: docs/special/stress_test.md:24 64 | msgid "Prepare the image" 65 | msgstr "" 66 | 67 | #: docs/special/stress_test.md:26 68 | msgid "" 69 | "This is generally broken into 2 stages: find the GPIO pin's default status, " 70 | "and update the image." 71 | msgstr "" 72 | 73 | #: docs/special/stress_test.md:29 74 | msgid "Find the default GPIO status" 75 | msgstr "" 76 | 77 | #: docs/special/stress_test.md:31 78 | msgid "" 79 | "While in theory any GPIO pins can be used, we should look for pins that are " 80 | "easy to identify. One such example is pin 40 which is at the end of the " 81 | "header, as well as being close to a GND pin to pull down (if the default " 82 | "state is high)." 83 | msgstr "" 84 | 85 | #: docs/special/stress_test.md:35 86 | msgid "" 87 | "For example, on Radxa ZERO, if we want to use pin 7, we should run the " 88 | "following command on ZERO:" 89 | msgstr "" 90 | 91 | #: docs/special/stress_test.md:43 92 | msgid "" 93 | "Since the default state is 1, we will instruct the QA engineer to connect " 94 | "pin 7 and pin 9 (which is GND) at the start of the test." 95 | msgstr "" 96 | 97 | #: docs/special/stress_test.md:46 98 | msgid "Update the image with stress test config" 99 | msgstr "" 100 | 101 | #: docs/special/stress_test.md:48 102 | msgid "" 103 | "To run the stress test at the boot time, first, mount and `systemd-nspawn` " 104 | "into the image (you can use `bsp` with `./bsp install stress.img` for this)." 105 | msgstr "" 106 | 107 | #: docs/special/stress_test.md:51 108 | msgid "" 109 | "You should then install the required stress test utilities. Currently, they " 110 | "are `stress-ng` and `memtester`:" 111 | msgstr "" 112 | 113 | #: docs/special/stress_test.md:58 114 | msgid "" 115 | "You should then delete `/config/before.txt`, which will cause a long boot " 116 | "delay due to the need to complete the first boot configuration. You can put " 117 | "a very basic system configuration there (so you can log in to check the " 118 | "issue), and then start the stress test. Continuing the above example of " 119 | "Radxa ZERO, this is the command we will use:" 120 | msgstr "" 121 | 122 | #: docs/special/stress_test.md:66 123 | msgid "<< EOF" 124 | msgstr "" 125 | 126 | #: docs/special/stress_test.md:66 127 | msgid "" 128 | "no_fail\n" 129 | "add_user radxa radxa\n" 130 | "user_append_group radxa sudo\n" 131 | "factory_stress PIN_7 0\n" 132 | "EOF" 133 | msgstr "" 134 | 135 | #: docs/special/stress_test.md:74 136 | msgid "" 137 | "Save and unmount the image. You can now deliver the stress test image to the " 138 | "QA engineer." 139 | msgstr "" 140 | 141 | #: docs/special/stress_test.md:77 142 | msgid "Usage" 143 | msgstr "" 144 | 145 | #: docs/special/stress_test.md:79 146 | msgid "Connect the GPIO jumper wire as instructed, and boot the system." 147 | msgstr "" 148 | 149 | #: docs/special/stress_test.md:81 150 | msgid "" 151 | "The device will power on normally. Once it reaches the Linux kernel, the LED " 152 | "should be heartbeating: quick 2 blinks then after some delay, repeat." 153 | msgstr "" 154 | 155 | #: docs/special/stress_test.md:84 156 | msgid "" 157 | "The stress service will then start after the system is fully booted. It will " 158 | "change the light pattern in 2 ways:" 159 | msgstr "" 160 | 161 | #: docs/special/stress_test.md:87 162 | msgid "" 163 | "Off: This indicates the stress test has failed to start. Please contact for " 164 | "support." 165 | msgstr "" 166 | 167 | #: docs/special/stress_test.md:89 168 | msgid "Switching at 0.5s interval: This indicates the stress is running." 169 | msgstr "" 170 | 171 | #: docs/special/stress_test.md:91 172 | msgid "" 173 | "Disconnect the GPIO jumper wire. The device status can be checked in a few " 174 | "hours." 175 | msgstr "" 176 | 177 | #: docs/special/stress_test.md:93 178 | msgid "Final LED status" 179 | msgstr "" 180 | 181 | #: docs/special/stress_test.md:95 182 | msgid "" 183 | "Switching at 0.5s interval: This indicates the stress is running. **PASS**" 184 | msgstr "" 185 | 186 | #: docs/special/stress_test.md:96 187 | msgid "" 188 | "On: This indicates the system was locked up and unable to reboot. **FAIL**" 189 | msgstr "" 190 | 191 | #: docs/special/stress_test.md:97 192 | msgid "" 193 | "Off: Either the system was locked up and unable to reboot, or the device " 194 | "rebooted. **FAIL**" 195 | msgstr "" 196 | -------------------------------------------------------------------------------- /po/zh-CN.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: rsetup\n" 4 | "POT-Creation-Date: 2024-03-13T15:33:53+08:00\n" 5 | "PO-Revision-Date: 2024-03-12 20:07+0800\n" 6 | "Last-Translator: \n" 7 | "Language-Team: Language zh-CN\n" 8 | "Language: zh-CN\n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "Plural-Forms: nplurals=1; plural=0;\n" 13 | 14 | #: docs/SUMMARY.md:1 15 | msgid "Summary" 16 | msgstr "" 17 | 18 | #: docs/SUMMARY.md:3 19 | msgid "Special topics" 20 | msgstr "特别话题" 21 | 22 | #: docs/SUMMARY.md:5 docs/special/stress_test.md:1 23 | msgid "Stress test image" 24 | msgstr "用于压力测试的镜像" 25 | 26 | #: docs/special/stress_test.md:3 27 | msgid "" 28 | "As of 0.4.1, `rsetup` can be used to create stress test images. This " 29 | "functionality relies on proper `gpiod` pin definition, as well as correct " 30 | "GPIO LEDs device tree definition, both are part of the system release " 31 | "requirements. As such, this can be used as a unified method to create stress " 32 | "test images." 33 | msgstr "" 34 | "自0.4.1版开始,`rsetup`可被用于生成压力测试镜像。此功能依赖于有效标注的" 35 | "`gpiod`引脚定义,以及正确的GPIO LED设备树定义。当前这两项均为我们发布系统时所" 36 | "需要实现的需求。基于此,`rsetup`可被用来作为通用的压力测试镜像生成方法。" 37 | 38 | #: docs/special/stress_test.md:8 39 | msgid "`factory_stress`" 40 | msgstr "" 41 | 42 | #: docs/special/stress_test.md:10 43 | msgid "" 44 | "This function is implemented in `factory_stress`, where the detailed command " 45 | "definition can be found in the source code." 46 | msgstr "" 47 | "压力测试相关的功能是在`factory_stress`中实现。详细的命令使用说明请参考源代" 48 | "码。" 49 | 50 | #: docs/special/stress_test.md:13 51 | msgid "" 52 | "To run this command, you need to supply a GPIO pin name, which will be " 53 | "connected to the GND/VCC pin during the device boot, as well as the default " 54 | "state when no wire is connected." 55 | msgstr "" 56 | "要运行该命令,需要提供一个 GPIO 引脚名称(在设备启动期间将连接至 GND/VCC 引" 57 | "脚),以及未连接导线时的默认状态。" 58 | 59 | #: docs/special/stress_test.md:17 60 | msgid "" 61 | "The factory stress test procedure begins with the pin connected, which is " 62 | "recognized by `factory_stress` as a signal to start the stress test, and " 63 | "update LEDs' pattern. The QA engineer will then disconnect the wire once the " 64 | "LED pattern indicates the test is started. This way, if the device fails the " 65 | "stress test and is rebooted, `factory_stress` will not be started again due " 66 | "to the GPIO value does not match the specified value." 67 | msgstr "" 68 | "工厂压力测试程序需首先连接指定引脚,`factory_stress`会将其识别为开始压力测试" 69 | "的信号,并更新 LED 指示灯的模式。一旦 LED 指示灯显示测试开始,质量保证工程师" 70 | "应当断开连接。如此,若设备未能通过压力测试并重新启动,`factory_stress`就不会" 71 | "因 GPIO 值与指定值不匹配而再次启动。" 72 | 73 | #: docs/special/stress_test.md:24 74 | msgid "Prepare the image" 75 | msgstr "准备镜像" 76 | 77 | #: docs/special/stress_test.md:26 78 | msgid "" 79 | "This is generally broken into 2 stages: find the GPIO pin's default status, " 80 | "and update the image." 81 | msgstr "这通常分为两个阶段:查找 GPIO 引脚的默认状态和更新镜像。" 82 | 83 | #: docs/special/stress_test.md:29 84 | msgid "Find the default GPIO status" 85 | msgstr "查找 GPIO 引脚的默认状态" 86 | 87 | #: docs/special/stress_test.md:31 88 | msgid "" 89 | "While in theory any GPIO pins can be used, we should look for pins that are " 90 | "easy to identify. One such example is pin 40 which is at the end of the " 91 | "header, as well as being close to a GND pin to pull down (if the default " 92 | "state is high)." 93 | msgstr "" 94 | "虽然理论上可以使用任何 GPIO 引脚,但我们应该使用易于识别的引脚。40 号引脚就是" 95 | "一个很好的例子,它位于针座的末端,而且靠近 GND 引脚来进行下拉(如果默认状态为" 96 | "高电平的话)。" 97 | 98 | #: docs/special/stress_test.md:35 99 | msgid "" 100 | "For example, on Radxa ZERO, if we want to use pin 7, we should run the " 101 | "following command on ZERO:" 102 | msgstr "" 103 | "例如,在 Radxa ZERO 上,如果我们要使用引脚 7,则应在 ZERO 上运行以下命令:" 104 | 105 | #: docs/special/stress_test.md:43 106 | msgid "" 107 | "Since the default state is 1, we will instruct the QA engineer to connect " 108 | "pin 7 and pin 9 (which is GND) at the start of the test." 109 | msgstr "" 110 | "由于默认状态为 1,我们需指示 QA 工程师在测试开始时连接引脚 7 和引脚 9(接地)" 111 | 112 | #: docs/special/stress_test.md:46 113 | msgid "Update the image with stress test config" 114 | msgstr "更新镜像" 115 | 116 | #: docs/special/stress_test.md:48 117 | msgid "" 118 | "To run the stress test at the boot time, first, mount and `systemd-nspawn` " 119 | "into the image (you can use `bsp` with `./bsp install stress.img` for this)." 120 | msgstr "" 121 | "要在启动时运行压力测试,首先要挂载并使用 `systemd-nspawn` 来进入到镜像中(可以使用 " 122 | "`bsp` 和 `./bsp install stress.img` 来完成这个操作)。" 123 | 124 | #: docs/special/stress_test.md:51 125 | msgid "" 126 | "You should then install the required stress test utilities. Currently, they " 127 | "are `stress-ng` and `memtester`:" 128 | msgstr "" 129 | "然后安装所需的压力测试工具。目前所使用的是 `stress-ng` 和 `memtester`:" 130 | 131 | #: docs/special/stress_test.md:58 132 | msgid "" 133 | "You should then delete `/config/before.txt`, which will cause a long boot " 134 | "delay due to the need to complete the first boot configuration. You can put " 135 | "a very basic system configuration there (so you can log in to check the " 136 | "issue), and then start the stress test. Continuing the above example of " 137 | "Radxa ZERO, this is the command we will use:" 138 | msgstr "" 139 | "由于默认的首次启动配置耗时较长,应删除自带的 `/config/before.txt`。您可以在这里添加一个非常" 140 | "基本的系统配置(以便登录检查问题),然后才开始进行压力测试。接续上面 Radxa ZERO 的例子,请使用" 141 | "以下命令:" 142 | 143 | #: docs/special/stress_test.md:66 144 | msgid "<< EOF" 145 | msgstr "" 146 | 147 | #: docs/special/stress_test.md:66 148 | msgid "" 149 | "no_fail\n" 150 | "add_user radxa radxa\n" 151 | "user_append_group radxa sudo\n" 152 | "factory_stress PIN_7 0\n" 153 | "EOF" 154 | msgstr "" 155 | 156 | #: docs/special/stress_test.md:74 157 | msgid "" 158 | "Save and unmount the image. You can now deliver the stress test image to the " 159 | "QA engineer." 160 | msgstr "保存并解除镜像挂载。您现在可以将压力测试镜像交付给 QA 工程师了。" 161 | 162 | #: docs/special/stress_test.md:77 163 | msgid "Usage" 164 | msgstr "使用" 165 | 166 | #: docs/special/stress_test.md:79 167 | msgid "Connect the GPIO jumper wire as instructed, and boot the system." 168 | msgstr "按照指示连接 GPIO 跳线,然后启动系统。" 169 | 170 | #: docs/special/stress_test.md:81 171 | msgid "" 172 | "The device will power on normally. Once it reaches the Linux kernel, the LED " 173 | "should be heartbeating: quick 2 blinks then after some delay, repeat." 174 | msgstr "设备将正常开机。当进入 Linux 内核时,LED 指示灯会以心跳模式跳动:快速闪烁 2 次,稍作延迟后重复。" 175 | 176 | #: docs/special/stress_test.md:84 177 | msgid "" 178 | "The stress service will then start after the system is fully booted. It will " 179 | "change the light pattern in 2 ways:" 180 | msgstr "系统完全启动后,压力测试服务就会启动。它将以两种方式之一改变灯光闪烁模式:" 181 | 182 | #: docs/special/stress_test.md:87 183 | msgid "" 184 | "Off: This indicates the stress test has failed to start. Please contact for " 185 | "support." 186 | msgstr "常灭: 表示压力测试启动失败。请联系技术支持。" 187 | 188 | #: docs/special/stress_test.md:89 189 | msgid "Switching at 0.5s interval: This indicates the stress is running." 190 | msgstr "每隔 0.5 秒切换一次: 这表示压力测试正在运行。" 191 | 192 | #: docs/special/stress_test.md:91 193 | msgid "" 194 | "Disconnect the GPIO jumper wire. The device status can be checked in a few " 195 | "hours." 196 | msgstr "断开 GPIO 跳线。几小时后可再次检查设备压力测试状态。" 197 | 198 | #: docs/special/stress_test.md:93 199 | msgid "Final LED status" 200 | msgstr "最终LED状态" 201 | 202 | #: docs/special/stress_test.md:95 203 | msgid "" 204 | "Switching at 0.5s interval: This indicates the stress is running. **PASS**" 205 | msgstr "每隔 0.5 秒切换一次: 这表示压力测试正在运行。**通过**" 206 | 207 | #: docs/special/stress_test.md:96 208 | msgid "" 209 | "On: This indicates the system was locked up and unable to reboot. **FAIL**" 210 | msgstr "常亮: 这表示系统死锁,无法重新启动。**失败**" 211 | 212 | #: docs/special/stress_test.md:97 213 | msgid "" 214 | "Off: Either the system was locked up and unable to reboot, or the device " 215 | "rebooted. **FAIL**" 216 | msgstr "常灭: 要么系统死锁,无法重新启动;要么设备已重启。**失败**" 217 | -------------------------------------------------------------------------------- /src/usr/bin/rsetup: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | shopt -s nullglob 5 | 6 | LC_ALL="C" 7 | LANG="C" 8 | LANGUAGE="C" 9 | 10 | DEBUG="${DEBUG:-false}" 11 | 12 | # shellcheck source=src/usr/lib/rsetup/cli/main.sh 13 | source "/usr/lib/rsetup/cli/main.sh" 14 | 15 | RSETUP_EXEC_NAME="$(basename "$0")" 16 | if (( $# == 0 )) && [[ "$RSETUP_EXEC_NAME" == "rsetup" ]] 17 | then 18 | if (( EUID != 0 )) 19 | then 20 | if [[ -n "${DISPLAY:-}" ]] && [[ -z "${SSH_TTY:-}" ]] 21 | then 22 | exec pkexec "$0" "$@" 23 | else 24 | exec sudo "$0" "$@" 25 | fi 26 | fi 27 | # shellcheck source=src/usr/lib/rsetup/tui/main.sh 28 | source "/usr/lib/rsetup/tui/main.sh" 29 | tui_start __tui_main "RSETUP" 30 | else 31 | if [[ "$RSETUP_EXEC_NAME" == "rsetup" ]] 32 | then 33 | __parameter_type_check "$1" "function" 34 | "$@" 35 | else 36 | __parameter_type_check "__$RSETUP_EXEC_NAME" "function" 37 | "__$RSETUP_EXEC_NAME" "$@" 38 | fi 39 | fi 40 | -------------------------------------------------------------------------------- /src/usr/lib/librtui/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /src/usr/lib/rsetup/cli/account.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | ALLOWED_RCONFIG_FUNC+=("add_user" "update_password" "user_append_group") 4 | 5 | add_user() { 6 | local username="$1" 7 | local password="$2" 8 | 9 | adduser --gecos "$username" --disabled-password "$username" 10 | 11 | if [[ -n "$password" ]] 12 | then 13 | update_password "$username" "$password" 14 | else 15 | passwd -d "$username" 16 | fi 17 | } 18 | 19 | update_password() { 20 | __parameter_count_check 2 "$@" 21 | 22 | local username="$1" 23 | local password="$2" 24 | 25 | echo "$username:$password" | chpasswd 26 | } 27 | 28 | user_append_group() { 29 | __parameter_count_check 2 "$@" 30 | 31 | local username="$1" 32 | local group="$2" 33 | 34 | usermod -aG "$group" "$username" 35 | } 36 | -------------------------------------------------------------------------------- /src/usr/lib/rsetup/cli/baota.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | install_baota() { 4 | __parameter_count_check 0 "$@" 5 | temp="$(mktemp)" 6 | wget -O "$temp" "https://download.bt.cn/install/install_lts.sh" 7 | bash "$temp" "ed8484bec" 8 | rm "$temp" 9 | } 10 | 11 | uninstall_baota() { 12 | __parameter_count_check 0 "$@" 13 | /etc/init.d/bt stop 14 | update-rc.d bt remove 15 | rm -f /etc/init.d/bt 16 | rm -rf /www/server/panel 17 | } 18 | -------------------------------------------------------------------------------- /src/usr/lib/rsetup/cli/docker.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | uninstall_docker() { 4 | __parameter_count_check 0 "$@" 5 | apt-get remove docker.io 6 | } 7 | 8 | install_docker() { 9 | __parameter_count_check 0 "$@" 10 | apt-get install docker.io 11 | } 12 | 13 | enable_docker() { 14 | __parameter_count_check 0 "$@" 15 | systemctl enable --now docker 16 | } 17 | 18 | disable_docker() { 19 | __parameter_count_check 0 "$@" 20 | systemctl disable --now docker 21 | } 22 | -------------------------------------------------------------------------------- /src/usr/lib/rsetup/cli/kernel.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | __yz-update-overlays() { 4 | # shellcheck disable=SC2034 5 | local ABI="${1:-}" IMAGE="${2:-}" 6 | 7 | exec &2 8 | eval set -- "$DEB_MAINT_PARAMS" 9 | # shellcheck disable=SC2034 10 | local ACTION="${1:-}" VERSION="${2:-}" 11 | 12 | case $ACTION in 13 | configure) 14 | if ! rebuild_overlays "$ABI" "$(get_soc_vendor)" 15 | then 16 | echo "Failed to update overlays." 17 | echo "Please make sure u-boot-menu is installed on your system." 18 | fi 19 | ;; 20 | remove) 21 | ;; 22 | esac 23 | } 24 | -------------------------------------------------------------------------------- /src/usr/lib/rsetup/cli/main.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | # shellcheck source=externals/librtui/src/lib/librtui/utils/utils.sh 4 | source "/usr/lib/librtui/utils/utils.sh" 5 | 6 | # shellcheck source=src/usr/lib/rsetup/mod/aic8800.sh 7 | source "/usr/lib/rsetup/mod/aic8800.sh" 8 | 9 | source "/usr/lib/rsetup/cli/rconfig.sh" 10 | source "/usr/lib/rsetup/cli/ssh.sh" 11 | source "/usr/lib/rsetup/cli/baota.sh" 12 | source "/usr/lib/rsetup/cli/system.sh" 13 | source "/usr/lib/rsetup/cli/account.sh" 14 | source "/usr/lib/rsetup/cli/docker.sh" 15 | source "/usr/lib/rsetup/cli/u-boot-menu.sh" 16 | source "/usr/lib/rsetup/cli/wi-fi.sh" 17 | source "/usr/lib/rsetup/cli/kernel.sh" 18 | 19 | source "/usr/lib/rsetup/cli/test/main.sh" 20 | 21 | export PATH="$PATH:/usr/lib/rsetup/mod" 22 | 23 | if $DEBUG 24 | then 25 | source "/usr/lib/rsetup/mod/debug_utils.sh" 26 | fi 27 | -------------------------------------------------------------------------------- /src/usr/lib/rsetup/cli/rconfig.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | # Provides remove_packages 4 | # shellcheck source=src/usr/lib/rsetup/mod/pkg.sh 5 | source "/usr/lib/rsetup/mod/pkg.sh" 6 | 7 | ALLOWED_RCONFIG_FUNC+=("request_reboot" "headless" "no_fail" "log") 8 | 9 | RCONFIG_REBOOT="false" 10 | 11 | headless() { 12 | local i 13 | for i in /sys/class/drm/*/status 14 | do 15 | if [[ "$(cat "$i")" == "connected" ]] 16 | then 17 | return 1 18 | fi 19 | done 20 | } 21 | 22 | rconfig_allowed_func() { 23 | if [[ $(type -t "$1") != function ]] || ! __in_array "$1" "${ALLOWED_RCONFIG_FUNC[@]}" >/dev/null 24 | then 25 | echo "'$1' is not an allowed command." 26 | return 1 27 | fi 28 | } 29 | 30 | request_reboot() { 31 | RCONFIG_REBOOT="${1:-true}" 32 | } 33 | 34 | no_fail() { 35 | no_fail="${1:-true}" 36 | } 37 | 38 | process_config() { 39 | local argv no_fail="false" 40 | while read -r 41 | do 42 | read -r -a argv <<< "$REPLY" 43 | if [[ -z ${argv+IS_SET} ]] 44 | then 45 | local argv=("") 46 | fi 47 | 48 | case "${argv[0]}" 49 | in 50 | \#*|"") 51 | continue 52 | ;; 53 | if|if_not) 54 | if ! rconfig_allowed_func "${argv[1]}" 55 | then 56 | continue 57 | elif [[ "${argv[0]}" == "if" ]] && ! "${argv[1]}" 58 | then 59 | continue 60 | elif [[ "${argv[0]}" == "if_not" ]] && "${argv[1]}" 61 | then 62 | continue 63 | else 64 | argv=( "${argv[@]:2}" ) 65 | fi 66 | ;; 67 | esac 68 | 69 | if rconfig_allowed_func "${argv[0]}" 70 | then 71 | echo "Running ${argv[0]} with" "${argv[@]:1}" "..." 72 | if $DEBUG 73 | then 74 | echo "${argv[*]}" 75 | else 76 | "${argv[@]}" || "$no_fail" 77 | fi 78 | fi 79 | done < <(grep "" "$1") 80 | } 81 | 82 | __on_boot() { 83 | __parameter_count_check 0 "$@" 84 | 85 | local ext_user_conf_dir="/config" distro_conf_dir="/usr/lib/rsetup/conf.d/on_boot" 86 | 87 | for i in "$distro_conf_dir"/*.conf "$ext_user_conf_dir/before.txt" "$ext_user_conf_dir/config.txt" "$ext_user_conf_dir/after.txt" 88 | do 89 | if [[ -e "$i" ]] 90 | then 91 | process_config "$i" 92 | fi 93 | done 94 | 95 | rm -f "$ext_user_conf_dir/before.txt" "$ext_user_conf_dir/after.txt" 96 | 97 | if [[ "$RCONFIG_REBOOT" == "true" ]] 98 | then 99 | reboot 100 | fi 101 | } 102 | 103 | log() { 104 | printf "%s\n" "$*" >&2 105 | } 106 | -------------------------------------------------------------------------------- /src/usr/lib/rsetup/cli/ssh.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | ALLOWED_RCONFIG_FUNC+=("regenerate_ssh_hostkey") 4 | 5 | regenerate_ssh_hostkey() { 6 | echo "Removing existing SSH host keys..." 7 | rm -f /etc/ssh/ssh_host_* || true 8 | echo "Regenerating SSH host keys..." 9 | dpkg-reconfigure -f noninteractive openssh-server 10 | } 11 | 12 | install_ssh() { 13 | __parameter_count_check 0 "$@" 14 | apt-get install openssh-server 15 | } 16 | 17 | uninstall_ssh() { 18 | __parameter_count_check 0 "$@" 19 | apt-get remove openssh-server 20 | } 21 | 22 | enable_ssh() { 23 | __parameter_count_check 0 "$@" 24 | systemctl enable --now ssh 25 | } 26 | 27 | disable_ssh() { 28 | __parameter_count_check 0 "$@" 29 | systemctl disable --now ssh 30 | } 31 | -------------------------------------------------------------------------------- /src/usr/lib/rsetup/cli/system.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | # shellcheck source=src/usr/lib/rsetup/mod/block_helpers.sh 4 | source "/usr/lib/rsetup/mod/block_helpers.sh" 5 | source "/usr/lib/rsetup/mod/hwid.sh" 6 | 7 | ALLOWED_RCONFIG_FUNC+=( 8 | "update_generic_hostname" 9 | "update_hostname" 10 | "update_locale" 11 | "enable_service" 12 | "disable_service" 13 | "resize_root" 14 | "set_thermal_governor" 15 | "set_led_trigger" 16 | "set_led_pattern" 17 | "set_led_netdev" 18 | ) 19 | 20 | system_update() { 21 | echo -e "\n=======================" 22 | if ! apt-get update 23 | then 24 | echo "Unable to update package list." 25 | return 1 26 | fi 27 | if ! apt-get dist-upgrade --allow-downgrades 28 | then 29 | echo "Unable to upgrade packages." 30 | return 2 31 | fi 32 | if ! apt-get dist-upgrade --allow-downgrades 33 | then 34 | echo "Unable to upgrade pinned packages." 35 | return 3 36 | fi 37 | } 38 | 39 | update_bootloader() { 40 | local pid device 41 | pid="${1:-$(get_product_id)}" 42 | __assert_f "/usr/lib/u-boot/$pid/setup.sh" 43 | 44 | device="${2:-$(__get_block_dev)}" 45 | 46 | "/usr/lib/u-boot/$pid/setup.sh" update_bootloader "$device" 47 | } 48 | 49 | erase_spinor() { 50 | local pid 51 | pid="${1:-$(get_product_id)}" 52 | __assert_f "/usr/lib/u-boot/$pid/setup.sh" 53 | 54 | "/usr/lib/u-boot/$pid/setup.sh" erase_spinor 55 | } 56 | 57 | update_spinor() { 58 | local pid 59 | pid="${1:-$(get_product_id)}" 60 | __assert_f "/usr/lib/u-boot/$pid/setup.sh" 61 | 62 | "/usr/lib/u-boot/$pid/setup.sh" update_spinor 63 | } 64 | 65 | erase_emmc_boot() { 66 | local pid device 67 | pid="${1:-$(get_product_id)}" 68 | __assert_f "/usr/lib/u-boot/$pid/setup.sh" 69 | 70 | for device in /dev/mmcblk*boot0 71 | do 72 | "/usr/lib/u-boot/$pid/setup.sh" erase_emmc_boot "$device" 73 | done 74 | } 75 | 76 | update_emmc_boot() { 77 | local pid device 78 | pid="${1:-$(get_product_id)}" 79 | __assert_f "/usr/lib/u-boot/$pid/setup.sh" 80 | 81 | for device in /dev/mmcblk*boot0 82 | do 83 | "/usr/lib/u-boot/$pid/setup.sh" update_emmc_boot "$device" 84 | done 85 | } 86 | 87 | update_generic_hostname() { 88 | __parameter_count_at_least_check 1 "$@" 89 | 90 | local current_hostname 91 | current_hostname="$(hostname)" 92 | 93 | if __in_array "$current_hostname" "$@" >/dev/null 94 | then 95 | update_hostname "$(get_product_id)" 96 | fi 97 | } 98 | 99 | update_hostname() { 100 | __parameter_count_check 1 "$@" 101 | 102 | local hostname="$1" 103 | 104 | echo "$hostname" > "/etc/hostname" 105 | cat << EOF > "/etc/hosts" 106 | 127.0.0.1 localhost 107 | 127.0.1.1 $hostname 108 | 109 | # The following lines are desirable for IPv6 capable hosts 110 | #::1 localhost ip6-localhost ip6-loopback 111 | #fe00::0 ip6-localnet 112 | #ff00::0 ip6-mcastprefix 113 | #ff02::1 ip6-allnodes 114 | #ff02::2 ip6-allrouters 115 | EOF 116 | } 117 | 118 | update_locale() { 119 | __parameter_count_check 1 "$@" 120 | 121 | local locale="$1" 122 | echo "locales locales/default_environment_locale select $locale" | debconf-set-selections 123 | echo "locales locales/locales_to_be_generated multiselect $locale UTF-8" | debconf-set-selections 124 | rm "/etc/locale.gen" 125 | dpkg-reconfigure --frontend noninteractive locales 126 | } 127 | 128 | enable_service() { 129 | __parameter_count_check 1 "$@" 130 | 131 | local service="$1" 132 | systemctl enable --now "$service" 133 | } 134 | 135 | disable_service() { 136 | __parameter_count_check 1 "$@" 137 | 138 | local service="$1" 139 | systemctl disable --now "$service" 140 | } 141 | 142 | resize_root() { 143 | local root_dev filesystem 144 | root_dev="$(__get_root_dev)" 145 | filesystem="$(blkid -s TYPE -o value "$root_dev")" 146 | 147 | echo "Resizing root filesystem..." 148 | case "$filesystem" in 149 | ext4) 150 | resize2fs "$root_dev" 151 | ;; 152 | btrfs) 153 | btrfs filesystem resize max / 154 | ;; 155 | *) 156 | echo "Unknown filesystem." >&2 157 | return 1 158 | ;; 159 | esac 160 | } 161 | 162 | set_thermal_governor() { 163 | __parameter_count_check 1 "$@" 164 | 165 | local new_policy="$1" i 166 | for i in /sys/class/thermal/thermal_zone*/policy 167 | do 168 | echo "$new_policy" > "$i" 169 | done 170 | } 171 | 172 | RBUILD_DRIVER_ROOT_PATH="/sys/bus/platform/drivers" 173 | 174 | RBUILD_LED_GPIO_DRIVER="leds-gpio" 175 | RBUILD_LED_PWM_DRIVER="leds_pwm" 176 | 177 | set_led_trigger() { 178 | __parameter_count_check 2 "$@" 179 | 180 | local led="$1" trigger="$2" node 181 | for node in "$RBUILD_DRIVER_ROOT_PATH/$RBUILD_LED_GPIO_DRIVER"/*/leds/"$led"/trigger "$RBUILD_DRIVER_ROOT_PATH/$RBUILD_LED_PWM_DRIVER"/*/leds/"$led"/trigger 182 | do 183 | echo "$trigger" > "$node" 184 | done 185 | } 186 | 187 | set_led_pattern() { 188 | local led="$1" node 189 | shift 190 | 191 | set_led_trigger "$led" pattern 192 | 193 | for node in "$RBUILD_DRIVER_ROOT_PATH/$RBUILD_LED_GPIO_DRIVER"/*/leds/"$led"/pattern "$RBUILD_DRIVER_ROOT_PATH/$RBUILD_LED_PWM_DRIVER"/*/leds/"$led"/pattern 194 | do 195 | echo "$*" > "$node" 196 | done 197 | } 198 | 199 | set_led_netdev() { 200 | __parameter_count_check 2 "$@" 201 | local led="$1" netdev="$2" 202 | 203 | set_led_trigger "$led" netdev 204 | 205 | for node in "$RBUILD_DRIVER_ROOT_PATH/$RBUILD_LED_GPIO_DRIVER"/*/leds/"$led"; do 206 | echo "$netdev" > "$node/device_name" 207 | echo "1" > "$node/link" 208 | echo "1" > "$node/tx" 209 | echo "1" > "$node/rx" 210 | done 211 | } 212 | 213 | set_getty_autologin() { 214 | local user="$1" switch="$2" service="$3" 215 | local config_dir="/etc/systemd/system/$service.d" 216 | 217 | if [[ "$switch" == "ON" ]]; then 218 | mkdir -p "$config_dir" 219 | local cmd 220 | if grep -q "serial" <<< "$service"; then 221 | cmd="-/sbin/agetty -o '-p -f -- \\\\u' --autologin $user --noclear --keep-baud 1500000,115200,57600,38400,9600 %I \$TERM" 222 | else 223 | cmd="-/sbin/agetty -o '-p -f -- \\\\u' --autologin $user --noclear %I \$TERM" 224 | fi 225 | cat << EOF | tee "$config_dir/50-rsetup-autologin.conf" >/dev/null 226 | # Auto generated by rsetup 227 | # DO NOT MODIFY! 228 | [Service] 229 | ExecStart= 230 | ExecStart=$cmd 231 | EOF 232 | else 233 | rm -f "$config_dir/50-rsetup-autologin.conf" 234 | fi 235 | 236 | if ! systemctl daemon-reload; then 237 | echo "If you are running in chroot environment, where systemd is not running, then you can ignore above error." >&2 238 | fi 239 | } 240 | 241 | set_sddm_autologin() { 242 | local user="$1" switch="$2" 243 | local config_dir="/etc/sddm.conf.d" 244 | 245 | if [[ "$switch" == "ON" ]]; then 246 | mkdir -p "$config_dir" 247 | cat << EOF | tee "$config_dir/50-rsetup-autologin.conf" >/dev/null 248 | # Auto generated by rsetup 249 | # DO NOT MODIFY! 250 | [Autologin] 251 | User=$user 252 | Session=plasma 253 | EOF 254 | else 255 | rm -f "$config_dir/50-rsetup-autologin.conf" 256 | fi 257 | } 258 | 259 | set_gdm_autologin() { 260 | local user="$1" switch="$2" 261 | local config_dir="/etc/gdm3" 262 | 263 | if [[ ! -f $config_dir/daemon.conf ]]; then 264 | echo "gdm is not installed. Auto login will not be configured." >&2 265 | return 1 266 | fi 267 | 268 | if [[ "$switch" == "ON" ]]; then 269 | sed -i '/^# Rsetup/,/# Rsetup$/d' "$config_dir/daemon.conf" 270 | cat << EOF | tee -a "$config_dir/daemon.conf" >/dev/null 271 | # Rsetup 272 | # Auto generated by rsetup 273 | # DO NOT MODIFY! 274 | [daemon] 275 | AutomaticLogin=$user 276 | AutomaticLoginEnable=true 277 | # Rsetup 278 | EOF 279 | else 280 | sed -i '/^# Rsetup/,/# Rsetup$/d' "$config_dir/daemon.conf" 281 | fi 282 | } 283 | 284 | set_lightdm_autologin() { 285 | local user="$1" switch="$2" 286 | local config_dir="/etc/lightdm/lightdm.conf.d/" 287 | 288 | if [[ "$switch" == "ON" ]]; then 289 | mkdir -p "$config_dir" 290 | cat << EOF | tee -a "$config_dir/50-rsetup-autologin.conf" >/dev/null 291 | # Auto generated by rsetup 292 | # DO NOT MODIFY! 293 | [Seat:*] 294 | autologin-user=$user 295 | autologin-user-timeout=0 296 | EOF 297 | else 298 | rm -f "$config_dir/50-rsetup-autologin.conf" 299 | fi 300 | } 301 | 302 | get_autologin_status() { 303 | local service="$1" 304 | 305 | case "$service" in 306 | *getty@*.service) 307 | if grep -q -e "--autologin" <(grep -v '^#' "/etc/systemd/system/${service}.d/50-rsetup-autologin.conf") 308 | then 309 | echo "ON" 310 | else 311 | echo "OFF" 312 | fi 313 | ;; 314 | sddm.service) 315 | if grep -q -e "[Autologin]" -e "User=" -e "Session=plasma" <(grep -v '^#' "/etc/sddm.conf.d/50-rsetup-autologin.conf") 316 | then 317 | echo "ON" 318 | else 319 | echo "OFF" 320 | fi 321 | ;; 322 | gdm.service) 323 | if grep -q -e "AutomaticLogin=" -e "AutomaticLoginEnable=true" <(grep -v '^#' "/etc/gdm3/daemon.conf") 324 | then 325 | echo "ON" 326 | else 327 | echo "OFF" 328 | fi 329 | ;; 330 | lightdm.service) 331 | if grep -q -e "[Seat:*]" -e "autologin-user=" -e "autologin-user-timeout=0" <(grep -v '^#' "/etc/lightdm/lightdm.conf.d/50-rsetup-autologin.conf") 332 | then 333 | echo "ON" 334 | else 335 | echo "OFF" 336 | fi 337 | ;; 338 | esac 339 | } 340 | 341 | set_autologin_status() { 342 | local user="$1" service="$2" switch="$3" 343 | 344 | case "$service" in 345 | *getty@*.service) 346 | set_getty_autologin "$user" "$switch" "$service" 347 | ;; 348 | sddm.service|gdm.service|lightdm.service) 349 | "set_${service/.service}_autologin" "$user" "$switch" 350 | ;; 351 | *) 352 | echo "Invalid options: $service" >&2 353 | return 1 354 | ;; 355 | esac 356 | } 357 | -------------------------------------------------------------------------------- /src/usr/lib/rsetup/cli/test/gstreamer.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | gst_audio_resample() { 4 | local sample_rate="${1:-48000}" 5 | gst-launch-1.0 audiotestsrc ! audioresample ! audio/x-raw, rate="$sample_rate" ! autoaudiosink 6 | } 7 | -------------------------------------------------------------------------------- /src/usr/lib/rsetup/cli/test/main.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | # shellcheck source=src/usr/lib/rsetup/cli/test/gstreamer.sh 4 | source "/usr/lib/rsetup/cli/test/gstreamer.sh" 5 | source "/usr/lib/rsetup/cli/test/mpp.sh" 6 | source "/usr/lib/rsetup/cli/test/stress.sh" 7 | -------------------------------------------------------------------------------- /src/usr/lib/rsetup/cli/test/mpp.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | mpp_enable_kernel_logging() { 4 | sudo tee /sys/module/rk_vcodec/parameters/mpp_dev_debug <<<"0x100" >/dev/null 5 | echo "Please check dmesg output for kernel debug output." 6 | } 7 | 8 | mpp_enable_logging() { 9 | export mpi_debug=1 10 | export mpp_debug=1 11 | export mpp_dec_debug=1 12 | export h264d_debug=1 13 | export mpp_syslog_perror=1 14 | export mpp_log_level=6 15 | export GST_DEBUG="2,*mpp*:4,*fps*:7" 16 | export LIBV4L_RKMPP_LOG_LEVEL=6 17 | export DISPLAY="${DISPLAY:-:0}" 18 | } 19 | 20 | mpp_play() { 21 | mpp_enable_logging 22 | xdg-open "$1" 23 | } 24 | 25 | mpp_gst_play() { 26 | mpp_enable_logging 27 | gst-play-1.0 "$1" 28 | } 29 | 30 | mpp_chromium() { 31 | mpp_enable_logging 32 | chromium --enable-logging --vmodule=/media/gpu=4 --v=1 33 | } 34 | -------------------------------------------------------------------------------- /src/usr/lib/rsetup/cli/test/stress.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | ALLOWED_RCONFIG_FUNC+=("factory_stress") 4 | 5 | 6 | # Run factory stress test 7 | # 8 | # Test will only run when a trigger GPIO is set to 9 | # Test status is reflected by on-board LEDs: 10 | # LED behaviours: 11 | # 500ms On-Off blink stress test running 12 | # Solid On/Off stress test failed / not running 13 | # Device default incorrect GPIO trigger pin 14 | # 15 | # Commands: 16 | # factory_stress 17 | # 18 | factory_stress() { 19 | __parameter_count_check 2 "$@" 20 | local triggered="false" pin_name="$1" trigger_value="$2" ret="0" pin_value="-1" gpio_line=() 21 | 22 | read -ra gpio_line < <(gpiofind "$pin_name") || ret=$? 23 | pin_value="$(gpioget "${gpio_line[@]}" 2>/dev/null)" || ret=$? 24 | if (( ret != 0 )) 25 | then 26 | echo "Unable to find the trigger pin '$pin_name'. Quit." 27 | return "$ret" 28 | fi 29 | 30 | if [[ "$pin_value" == "$trigger_value" ]] 31 | then 32 | triggered="true" 33 | fi 34 | 35 | for i in "$RBUILD_DRIVER_ROOT_PATH/$RBUILD_LED_GPIO_DRIVER"/*/leds/* 36 | do 37 | if [[ -f "$i/trigger" ]] 38 | then 39 | if [[ "$triggered" == "true" ]] 40 | then 41 | set_led_trigger "$(basename "$i")" "timer" 42 | else 43 | set_led_trigger "$(basename "$i")" "none" 44 | fi 45 | fi 46 | done 47 | 48 | if [[ "$triggered" != "true" ]] 49 | then 50 | echo "Stress test triggering condition unmet. Skip." 51 | return 52 | fi 53 | 54 | local available_memory core_count 55 | available_memory=$(( $(grep MemAvailable /proc/meminfo | awk '{print $2}') )) 56 | core_count="$(nproc)" 57 | 58 | stress-ng --cpu $((core_count)) --io $((core_count / 4)) & 59 | memtester $(( available_memory * 95 / 100 ))K & 60 | echo "Factory stress test now running in the background..." 61 | wait -n 62 | 63 | echo "Factory stress test ended unexpectedly." 64 | echo "Stop remaining tests and disable LEDs to report this." 65 | 66 | for i in "$RBUILD_DRIVER_ROOT_PATH/$RBUILD_LED_GPIO_DRIVER"/*/leds/* 67 | do 68 | if [[ -f "$i/trigger" ]] 69 | then 70 | set_led_trigger "$(basename "$i")" "none" 71 | fi 72 | done 73 | return 1 74 | } 75 | -------------------------------------------------------------------------------- /src/usr/lib/rsetup/cli/u-boot-menu.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | # shellcheck source=src/usr/lib/rsetup/mod/overlay.sh 4 | source "/usr/lib/rsetup/mod/overlay.sh" 5 | 6 | ALLOWED_RCONFIG_FUNC+=("load_u-boot_setting") 7 | 8 | is_overlay_unchanged() { 9 | [[ "$(__array_to_ordered_text "${RTUI_CHECKLIST_STATE_NEW[@]}")" == \ 10 | "$(__array_to_ordered_text "${RTUI_CHECKLIST_STATE_OLD[@]}")" ]] 11 | } 12 | 13 | check_overlay_conflict_init() { 14 | RSETUP_OVERLAY_RESOURCES=() 15 | RSETUP_OVERLAY_RESOURCE_OWNER=() 16 | } 17 | 18 | check_overlay_conflict() { 19 | local name resources res i 20 | mapfile -t resources < <(parse_dtbo "exclusive" "$1") 21 | mapfile -t name < <(parse_dtbo --default-value "file" "title" "$1") 22 | 23 | for res in "${resources[@]}" 24 | do 25 | if [[ "$res" == "null" ]] 26 | then 27 | continue 28 | fi 29 | if i="$(__in_array "$res" "${RSETUP_OVERLAY_RESOURCES[@]}")" 30 | then 31 | msgbox "Resource conflict detected! 32 | 33 | '${name[0]}' and '${RSETUP_OVERLAY_RESOURCE_OWNER[$i]}' both require the exclusive ownership of the following resource: 34 | 35 | ${RSETUP_OVERLAY_RESOURCES[$i]} 36 | 37 | Please only enable one of them." 38 | return 1 39 | else 40 | RSETUP_OVERLAY_RESOURCES+=("$res") 41 | RSETUP_OVERLAY_RESOURCE_OWNER+=("${name[0]}") 42 | fi 43 | done 44 | } 45 | 46 | load_u-boot_setting() { 47 | # shellcheck source=/dev/null 48 | source "/etc/default/u-boot" 49 | 50 | if [[ -z "${U_BOOT_FDT_OVERLAYS_DIR:-}" ]] 51 | then 52 | eval "$(grep "^U_BOOT_FDT_OVERLAYS_DIR" "$(which u-boot-update)")" 53 | U_BOOT_FDT_OVERLAYS_DIR="${U_BOOT_FDT_OVERLAYS_DIR:-}" 54 | fi 55 | } 56 | 57 | disable_overlays() { 58 | load_u-boot_setting 59 | 60 | for i in "$U_BOOT_FDT_OVERLAYS_DIR"/*.dtbo 61 | do 62 | mv -- "$i" "${i}.disabled" 63 | done 64 | } 65 | 66 | rebuild_overlays() { 67 | load_u-boot_setting 68 | 69 | local version="$1" vendor="${2:-}" dtbos i 70 | local old_overlays new_overlays enabled_overlays=() 71 | old_overlays="$(realpath "$U_BOOT_FDT_OVERLAYS_DIR")" 72 | new_overlays="${old_overlays}_new" 73 | 74 | echo "Rebuilding overlay data folder for '$version'..." 75 | 76 | if [[ -d "$old_overlays" ]] 77 | then 78 | cp -aR "$old_overlays" "$new_overlays" 79 | else 80 | mkdir -p "$old_overlays" "$new_overlays" 81 | fi 82 | 83 | if [[ -f "$new_overlays/managed.list" ]] 84 | then 85 | echo "Removing managed overlays..." 86 | mapfile -t RSETUP_MANAGED_OVERLAYS < "$new_overlays/managed.list" 87 | 88 | for i in "${RSETUP_MANAGED_OVERLAYS[@]}" 89 | do 90 | if [[ -f "$new_overlays/$i" ]] 91 | then 92 | enabled_overlays+=( "$i" ) 93 | fi 94 | rm -f "$new_overlays/$i" "$new_overlays/$i.disabled" 95 | done 96 | fi 97 | 98 | if [[ -n "$vendor" ]] 99 | then 100 | dtbos=( "/usr/lib/linux-image-$version/$vendor/overlays/"*.dtbo* ) 101 | else 102 | dtbos=( "/usr/lib/linux-image-$version/"*"/overlays/"*.dtbo* ) 103 | fi 104 | rm -f "$new_overlays/managed.list" 105 | touch "$new_overlays/managed.list" 106 | 107 | echo "Building list of compatible overlays..." 108 | mapfile -t dtbos < <(dtbo_is_compatible "${dtbos[@]}") 109 | 110 | # This loop is a bottleneck due to expensive operations 111 | # Enabling parallelism brings total execution time from 38.453s to 21.633s 112 | local nproc 113 | nproc=$(( $(nproc) )) 114 | echo "Installing compatible overlays..." 115 | for i in "${dtbos[@]}" 116 | do 117 | while (( $(jobs -r | wc -l) > nproc )) 118 | do 119 | sleep 0.1 120 | done 121 | ( 122 | cp "$i" "$new_overlays/$(basename "$i").disabled" 123 | exec 100>>"$new_overlays/managed.list" 124 | flock 100 125 | basename "$i" >&100 126 | ) & 127 | done 128 | wait 129 | 130 | # This loop is not a bottleneck 131 | # We add parallelism to make it uniform 132 | echo "Reenable previously enabled overlays..." 133 | for i in "${enabled_overlays[@]}" 134 | do 135 | while (( $(jobs -r | wc -l) > nproc )) 136 | do 137 | sleep 0.1 138 | done 139 | ( 140 | if [[ -f "$new_overlays/${i}.disabled" ]] 141 | then 142 | mv -- "$new_overlays/${i}.disabled" "$new_overlays/$i" 143 | fi 144 | ) & 145 | done 146 | wait 147 | 148 | echo "Commiting changes..." 149 | rm -rf "${old_overlays}_old" 150 | mv "$old_overlays" "${old_overlays}_old" 151 | mv "$new_overlays" "$old_overlays" 152 | 153 | echo "Overlay data folder has been successfully rebuilt." 154 | } 155 | 156 | enable_overlays() { 157 | __parameter_count_at_least_check 1 "$@" 158 | 159 | local enable_overlays_list i dir_name file_name input_check=true 160 | 161 | load_u-boot_setting 162 | 163 | for i in "$@" 164 | do 165 | dir_name="$(dirname "$i")" 166 | file_name="$(basename "${i%.disabled}")" 167 | 168 | if [[ "$dir_name" == "." ]] 169 | then 170 | dir_name="$U_BOOT_FDT_OVERLAYS_DIR" 171 | fi 172 | 173 | if [[ "$(realpath "$dir_name")" != "$(realpath "$U_BOOT_FDT_OVERLAYS_DIR")" ]] 174 | then 175 | echo "$i: only overlays within '$U_BOOT_FDT_OVERLAYS_DIR' can be enabled." >&2 176 | input_check=false 177 | elif [[ -e "$dir_name/$file_name.disabled" ]] 178 | then 179 | enable_overlays_list+=("$U_BOOT_FDT_OVERLAYS_DIR/$file_name") 180 | elif [[ -e "$dir_name/$file_name" ]] 181 | then 182 | echo "$file_name: already enabled." >&2 183 | else 184 | echo "$file_name: cannot find such overlay in '$U_BOOT_FDT_OVERLAYS_DIR'" 185 | input_check=false 186 | fi 187 | done 188 | 189 | if ! $input_check 190 | then 191 | return "$ERROR_ILLEGAL_PARAMETERS" 192 | fi 193 | 194 | for i in "${enable_overlays_list[@]}" 195 | do 196 | mv "$i.disabled" "$i" 197 | done 198 | 199 | u-boot-update 200 | } 201 | -------------------------------------------------------------------------------- /src/usr/lib/rsetup/cli/wi-fi.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | ALLOWED_RCONFIG_FUNC+=("connect_wi-fi") 4 | 5 | connect_wi-fi() { 6 | local i ssid="$1" total_retry=10 7 | local command=(nmcli device wifi connect "$ssid") 8 | case $# in 9 | 1|2) 10 | nmcli radio wifi on 11 | 12 | if (( $# == 2 )) 13 | then 14 | command+=(password "$2") 15 | fi 16 | 17 | for ((i = 0; i < total_retry; i++)) 18 | do 19 | if "${command[@]}" 20 | then 21 | echo "Wi-Fi successfully connected to $ssid." 22 | return 23 | else 24 | echo "Failed to connect to $ssid. Retry $((i + 1)) of $total_retry." >&2 25 | sleep 1 26 | fi 27 | done 28 | 29 | echo "Wi-Fi failed to connect to $ssid after total of $total_retry retries. Quit." >&2 30 | return 1 31 | ;; 32 | 0|*) 33 | echo "Usage: ${FUNCNAME[0]} [ssid] " >&2 34 | return 1 35 | ;; 36 | esac 37 | } 38 | -------------------------------------------------------------------------------- /src/usr/lib/rsetup/mod/aic8800.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | __aic8800_reset() { 4 | __parameter_count_check 1 "$@" 5 | 6 | while read -r 7 | do 8 | echo "bt_test > $REPLY" 9 | 10 | case "$(tr -d '\r' <<< "$REPLY")" 11 | in 12 | "hci recv thread ready (nil)") 13 | echo "Device reset successfully." 14 | return 15 | ;; 16 | "dev_open fail") 17 | echo "Unable to open /dev/$1. Is Bluetooth already up?" 18 | return 1 19 | ;; 20 | esac 21 | done < <(timeout 1 bt_test -s uart 1500000 "/dev/$1") 22 | 23 | echo "Command timed out." 24 | return 2 25 | } 26 | -------------------------------------------------------------------------------- /src/usr/lib/rsetup/mod/block_helpers.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | __get_root_dev() { 4 | realpath "$(findmnt --nofsroot --noheadings --output SOURCE /)" 5 | } 6 | 7 | __get_block_dev() { 8 | echo "/dev/$(udevadm info --query=path "--name=$(__get_root_dev)" | awk -F'/' '{print $(NF-1)}')" 9 | } 10 | -------------------------------------------------------------------------------- /src/usr/lib/rsetup/mod/config.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | config_transaction_start() { 4 | RBUILD_CONFIG="/config/config.txt.new" 5 | cp /config/config.txt "$RBUILD_CONFIG" 6 | } 7 | 8 | config_transaction_abort() { 9 | rm -f "$RBUILD_CONFIG" 10 | unset RBUILD_CONFIG 11 | } 12 | 13 | config_transaction_commit() { 14 | cp /config/config.txt /config/config.txt.old 15 | mv "$RBUILD_CONFIG" /config/config.txt 16 | unset RBUILD_CONFIG 17 | } 18 | 19 | remove_config() { 20 | local regex="$1" 21 | shift 22 | while (( $# > 0 )) 23 | do 24 | regex="$regex\s+$1" 25 | shift 26 | done 27 | sed -E -i "/^\s*$regex.*$/d" "$RBUILD_CONFIG" 28 | } 29 | 30 | add_config() { 31 | echo "$@" >> "$RBUILD_CONFIG" 32 | } 33 | 34 | enable_config() { 35 | "$@" 36 | add_config "$@" 37 | } 38 | 39 | save_unique_config() { 40 | local command="$1" 41 | shift 42 | local arguments=( "$@" ) 43 | config_transaction_start 44 | remove_config "$command" 45 | add_config "$command" "${arguments[@]}" 46 | config_transaction_commit 47 | } 48 | 49 | enable_unique_config() { 50 | local command="$1" 51 | shift 52 | local arguments=( "$@" ) ret=0 53 | "$command" "${arguments[@]}" || ret=$? 54 | if (( ret == 0 )) 55 | then 56 | save_unique_config "$command" "${arguments[@]}" 57 | else 58 | return $ret 59 | fi 60 | } 61 | -------------------------------------------------------------------------------- /src/usr/lib/rsetup/mod/debug_utils.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | which() { 4 | case $1 in 5 | u-boot-update) 6 | echo "/usr/sbin/u-boot-update" 7 | ;; 8 | *) 9 | command which "$@" 10 | ;; 11 | esac 12 | } 13 | 14 | u-boot-update() { 15 | echo "Running u-boot-update..." 16 | } 17 | -------------------------------------------------------------------------------- /src/usr/lib/rsetup/mod/dtbo_is_compatible: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: UTF-8 -*- 3 | 4 | import threading, yaml, os, concurrent.futures 5 | from sys import argv 6 | 7 | results = {} 8 | results_lock = threading.Lock() 9 | 10 | def process_file(file, compatible, index): 11 | try: 12 | for dtbo_compatible in yaml.load(os.popen("dtc -I dtb -O dts " + file + " 2>/dev/null | dtc -I dts -O yaml 2>/dev/null").read(), Loader=yaml.CLoader)[0]["metadata"]["compatible"][0].split("\x00"): 13 | if dtbo_compatible in compatible: 14 | with results_lock: 15 | results[index] = file 16 | break 17 | # If the error type is KeyError it is assumed to be a third-party overlay installed by the user, which is compatible by default. 18 | except KeyError: 19 | with results_lock: 20 | results[index] = file 21 | 22 | def main(): 23 | files = argv[1:] 24 | 25 | try: 26 | compatible = open("/proc/device-tree/compatible").read().split("\x00")[0:-1] 27 | # If this file does not exist, it is assumed that the image is generated inside the chroot 28 | except FileNotFoundError: 29 | for file in files: 30 | print(file) 31 | exit(0) 32 | 33 | yaml.CLoader.add_constructor('!u8', yaml.constructor.FullConstructor.construct_yaml_seq) 34 | 35 | with concurrent.futures.ThreadPoolExecutor(max_workers=os.cpu_count()) as executor: 36 | futures = [executor.submit(process_file, file, compatible, i) for i, file in enumerate(files)] 37 | concurrent.futures.wait(futures) 38 | for key, result in sorted(results.items()): 39 | print(result) 40 | 41 | if __name__ == "__main__": 42 | main() 43 | -------------------------------------------------------------------------------- /src/usr/lib/rsetup/mod/hwid.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | get_soc_vendor() { 4 | if [[ -n "${RSETUP_SOC_VENDOR_OVERRIDE:=}" ]] || [[ ! -f /proc/device-tree/compatible ]] 5 | then 6 | echo "$RSETUP_SOC_VENDOR_OVERRIDE" 7 | else 8 | tr $"\0" $"\n" < /proc/device-tree/compatible | tail -n 1 | cut -d "," -f 1 9 | fi 10 | } 11 | 12 | get_product_ids() { 13 | if [[ -n "${RSETUP_PRODUCT_ID_OVERRIDE:=}" ]] || [[ ! -f /proc/device-tree/compatible ]] 14 | then 15 | echo "$RSETUP_PRODUCT_ID_OVERRIDE" 16 | else 17 | local REPLY 18 | 19 | while read -r 20 | do 21 | case "$(cut -d "," -f 1 <<< "$REPLY")" in 22 | radxa) 23 | REPLY="$(cut -d "," -f 2 <<< "$REPLY")" 24 | if [[ $REPLY =~ ^rock ]] 25 | then 26 | echo "$REPLY" 27 | else 28 | echo "radxa-$REPLY" 29 | fi 30 | ;; 31 | *) 32 | echo "${REPLY//,/-}" 33 | ;; 34 | esac 35 | done < <(tr $"\0" $"\n" < /proc/device-tree/compatible) 36 | fi 37 | } 38 | 39 | get_product_id() { 40 | local products 41 | mapfile -t products < <(get_product_ids) 42 | echo "${products[0]}" 43 | } 44 | -------------------------------------------------------------------------------- /src/usr/lib/rsetup/mod/overlay.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | compile_dtb() { 4 | local temp dts="$1" dtbo="${2:-${1%.dts}.dtbo}" 5 | temp="$(mktemp)" 6 | # shellcheck disable=SC2064 7 | trap "rm -f $temp" RETURN EXIT 8 | 9 | if ! cpp -nostdinc -undef -x assembler-with-cpp -E -I "/usr/src/linux-headers-$(uname -r)/include" -I "/usr/lib/modules/$(uname -r)/build/include" "$dts" "$temp" 10 | then 11 | return 1 12 | fi 13 | 14 | if ! dtc -q -@ -I dts -O dtb -o "$dtbo" "$temp" 15 | then 16 | return 2 17 | fi 18 | } 19 | -------------------------------------------------------------------------------- /src/usr/lib/rsetup/mod/parse_dtbo: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: UTF-8 -*- 3 | 4 | import threading, yaml, re, os, argparse, concurrent.futures 5 | 6 | results = {} 7 | results_lock = threading.Lock() 8 | 9 | parser = argparse.ArgumentParser(description='Parse dtbo metadata data') 10 | parser.add_argument('--show-overlays', action='store_true', default=False, help='Control print DTBO title, enable status and DTBO filename') 11 | parser.add_argument('--default-value', metavar='string', default="null", help='error default string') 12 | parser.add_argument('field', metavar='string', help='Metadata field') 13 | parser.add_argument('filename', nargs='+', metavar='filename', help='DTBO filename list') 14 | args = parser.parse_args() 15 | 16 | class Result: 17 | def __init__(self, field, file): 18 | self._field = field 19 | if ".disabled" == os.path.splitext(file)[-1]: 20 | self._status = "OFF" 21 | else: 22 | self._status = "ON" 23 | self._basename = re.match(r'(.+\.dtbo)', os.path.basename(file)).group(1) 24 | 25 | @property 26 | def field(self): 27 | return self._field 28 | 29 | @property 30 | def status(self): 31 | return self._status 32 | 33 | @property 34 | def basename(self): 35 | return self._basename 36 | 37 | def __str__(self) -> str: 38 | if args.show_overlays: 39 | return f'{self.field}\n{self.status}\n{self.basename}' 40 | else: 41 | return self.field 42 | 43 | def process_file(file, args, index): 44 | try: 45 | field = yaml.load(os.popen("dtc -I dtb -O dts " + file + " 2>/dev/null | dtc -I dts -O yaml 2>/dev/null").read(), Loader=yaml.CLoader)[0]["metadata"][args.field][0].replace("\0", "\n") 46 | # If parsing a field fails, the return value will be handled according to the --default-value option 47 | except Exception as e: 48 | if args.default_value == "file": 49 | field = file 50 | else: 51 | field = args.default_value 52 | with results_lock: 53 | results[index] = Result(field, file) 54 | 55 | def main(): 56 | yaml.CLoader.add_constructor('!u8', yaml.constructor.FullConstructor.construct_yaml_seq) 57 | 58 | with concurrent.futures.ThreadPoolExecutor(max_workers=os.cpu_count()) as executor: 59 | futures = [executor.submit(process_file, file, args, i) for i, file in enumerate(args.filename)] 60 | concurrent.futures.wait(futures) 61 | for key, result in sorted(results.items()): 62 | print(result) 63 | 64 | if __name__ == "__main__": 65 | main() 66 | -------------------------------------------------------------------------------- /src/usr/lib/rsetup/mod/pkg.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | ALLOWED_RCONFIG_FUNC+=("remove_packages") 4 | 5 | __depends_package() { 6 | local title="$1" p missing_packages=() 7 | shift 8 | for p in "$@" 9 | do 10 | if [[ "$(dpkg --get-selections "$p" | awk '{print $2}')" != "install" ]] 11 | then 12 | missing_packages+=( "$p" ) 13 | fi 14 | done 15 | 16 | if (( ${#missing_packages[@]} != 0 )) 17 | then 18 | if ! yesno "'$title' requires the following packages: 19 | 20 | ${missing_packages[*]} 21 | 22 | Do you want to install them right now?" 23 | then 24 | return 1 25 | fi 26 | apt-get update 27 | apt-get install --no-install-recommends -y "${missing_packages[@]}" 28 | fi 29 | } 30 | 31 | remove_packages() { 32 | apt-get autoremove -y "$@" 33 | } 34 | -------------------------------------------------------------------------------- /src/usr/lib/rsetup/tui/comm/comm.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | __comm_network() { 4 | nmtui 5 | } 6 | 7 | __comm_bluetooth() { 8 | msgbox "Configure Bluetooth" 9 | } 10 | 11 | __comm() { 12 | menu_init 13 | menu_add __comm_network "Network" 14 | if $DEBUG 15 | then 16 | menu_add __comm_bluetooth "Bluetooth" 17 | fi 18 | menu_show "Manage Connectivity" 19 | } 20 | -------------------------------------------------------------------------------- /src/usr/lib/rsetup/tui/hardware/gpio.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | __hardware_gpio_fmt() { 4 | local i="$1" 5 | if (( i < 4 )) 6 | then 7 | # shellcheck disable=SC2028 8 | echo "%s %s %s %s\\n" 9 | elif (( i == 4 )) 10 | then 11 | # shellcheck disable=SC2028 12 | echo "%s %s %s %s\\n" 13 | else 14 | # shellcheck disable=SC2028 15 | echo "%s %s %s %s\\n" 16 | fi 17 | } 18 | 19 | __hardware_gpio_set() { 20 | local i level="$1" gpio=() states=() 21 | 22 | for i in {1..40} 23 | do 24 | if gpiofind "PIN_$i" >/dev/null 25 | then 26 | read -ra gpio < <(gpiofind "PIN_$i") 27 | if gpioset "${gpio[0]}" "${gpio[1]}=$level" 28 | then 29 | gpioset -m signal "${gpio[0]}" "${gpio[1]}=$level" & 30 | states+=("$level") 31 | else 32 | states+=("E") 33 | fi 34 | else 35 | states+=(" ") 36 | fi 37 | done 38 | 39 | msgbox "Following GPIO pins have their state changed temporarily: 40 | 41 | 0: Low | 1: High | E: Error 42 | 43 | State Pin State 44 | $(for i in {0..19} 45 | do 46 | # shellcheck disable=SC2059 47 | printf "$(__hardware_gpio_fmt "$i")" \ 48 | "${states[i * 2]}" "$(( i * 2 + 1 ))" \ 49 | "$(( i * 2 + 2 ))" "${states[i * 2 + 1]}" 50 | done)" #" # Workaround VS Code incorrect code highlighting 51 | 52 | jobs -r -p | xargs -I{} kill -- {} 53 | } 54 | 55 | __hardware_gpio_set_high() { 56 | __hardware_gpio_set 1 57 | } 58 | 59 | __hardware_gpio_set_low() { 60 | __hardware_gpio_set 0 61 | } 62 | 63 | __hardware_gpio_get() { 64 | local i level gpio=() states=() 65 | 66 | for i in {1..40} 67 | do 68 | if gpiofind "PIN_$i" >/dev/null 69 | then 70 | read -ra gpio < <(gpiofind "PIN_$i") 71 | if level="$(gpioget "${gpio[@]}" 2>/dev/null)" 72 | then 73 | states+=("$level") 74 | else 75 | states+=("E") 76 | fi 77 | else 78 | states+=(" ") 79 | fi 80 | done 81 | 82 | msgbox "Following is the current reading of all supported GPIO pins: 83 | 84 | 0: Low | 1: High | E: Error 85 | 86 | State Pin State 87 | $(for i in {0..19} 88 | do 89 | # shellcheck disable=SC2059 90 | printf "$(__hardware_gpio_fmt "$i")" \ 91 | "${states[i * 2]}" "$(( i * 2 + 1 ))" \ 92 | "$(( i * 2 + 2 ))" "${states[i * 2 + 1]}" 93 | done)" #" # Workaround VS Code incorrect code highlighting 94 | } 95 | 96 | __hardware_gpio() { 97 | if loginctl | grep -e ttyS -e ttyAML -e ttyFIQ >/dev/null && \ 98 | ! yesno "A serial session has been detected. 99 | 100 | Testing GPIO with provided functions may cause the serial console to malfunction. 101 | It will only return to normal working condition after a system reboot. 102 | 103 | Do you want to continue? 104 | " 105 | then 106 | return 107 | fi 108 | 109 | menu_init 110 | menu_add __hardware_gpio_set_high "Set all GPIO to High" 111 | menu_add __hardware_gpio_set_low "Set all GPIO to Low" 112 | menu_add __hardware_gpio_get "Get all GPIO state" 113 | menu_show "Please select the test case:" 114 | } 115 | -------------------------------------------------------------------------------- /src/usr/lib/rsetup/tui/hardware/hardware.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | # shellcheck source=src/usr/lib/rsetup/mod/config.sh 4 | source "/usr/lib/rsetup/mod/config.sh" 5 | 6 | # shellcheck source=src/usr/lib/rsetup/tui/hardware/gpio.sh 7 | source "/usr/lib/rsetup/tui/hardware/gpio.sh" 8 | 9 | __hardware_gstreamer_test_picture() { 10 | local temp 11 | temp="$(mktemp "${TEMPDIR:-/tmp}/tmp.XXXXXXXXXX.jpg")" 12 | 13 | if gst-launch-1.0 v4l2src "device=/dev/$RSETUP_GSTREAMER_DEVICE" io-mode=4 num-buffers=30 ! \ 14 | autovideoconvert ! \ 15 | video/x-raw,format=UYVY,width=1920,height=1080 ! \ 16 | jpegenc ! \ 17 | multifilesink "location=$temp" 18 | then 19 | msgbox "Test image is saved at $temp." 20 | else 21 | rm -f "$temp" 22 | msgbox "Unable to capture an image with $RSETUP_GSTREAMER_DEVICE device. 23 | Please check if you have the required libraries installed." "$RTUI_PALETTE_ERROR" 24 | fi 25 | } 26 | 27 | __hardware_gstreamer_test_live() { 28 | gst-launch-1.0 v4l2src "device=/dev/$RSETUP_GSTREAMER_DEVICE" io-mode=4 ! \ 29 | autovideoconvert ! \ 30 | video/x-raw,format=NV12,width=1920,height=1080,framerate=30/1 ! \ 31 | xvimagesink 32 | } 33 | 34 | __hardware_gstreamer_test() { 35 | RSETUP_GSTREAMER_DEVICE="$RTUI_MENU_SELECTED" 36 | menu_init 37 | if [[ -n "${DISPLAY:-}" ]] 38 | then 39 | menu_add __hardware_gstreamer_test_live "Show live video" 40 | fi 41 | menu_add __hardware_gstreamer_test_picture "Capture a picture" 42 | menu_show "Please select the test case:" 43 | } 44 | 45 | __hardware_video() { 46 | menu_init 47 | for i in /dev/video* 48 | do 49 | menu_add __hardware_gstreamer_test "$(basename "$i")" 50 | done 51 | menu_emptymsg "No supported devices is detected. 52 | 53 | Please make sure they are enabled first." 54 | menu_show "Select video capture device:" 55 | } 56 | 57 | __hardware_gpio_leds() { 58 | checklist_init 59 | 60 | for i in "$RBUILD_DRIVER_ROOT_PATH/$RBUILD_LED_GPIO_DRIVER"/*/leds/* 61 | do 62 | if [[ -f "$i/trigger" ]] 63 | then 64 | checklist_add "$(basename "$i") [$(sed -E "s/.*\[(.*)\].*/\1/" "$i/trigger")]" "OFF" 65 | fi 66 | done 67 | checklist_emptymsg "No supported devices is detected. 68 | 69 | Please make sure they are enabled first." 70 | if ! checklist_show "Below are the available LEDs and their triggers. 71 | Select any to update their trigger." || checklist_is_selection_empty 72 | then 73 | return 74 | fi 75 | 76 | local triggers 77 | read -r -a triggers <<< "$(sed "s/\[//;s/\]//" "$(find -L "$RBUILD_DRIVER_ROOT_PATH/$RBUILD_LED_GPIO_DRIVER" -mindepth 4 -maxdepth 4 -name 'trigger' 2> /dev/null | head -1)")" 78 | 79 | radiolist_init 80 | for i in "${triggers[@]}" 81 | do 82 | radiolist_add "$i" "OFF" 83 | done 84 | if ! radiolist_show "Please select the new trigger:" || radiolist_is_selection_empty 85 | then 86 | return 87 | fi 88 | 89 | config_transaction_start 90 | for i in "${RTUI_CHECKLIST_STATE_NEW[@]}" 91 | do 92 | read -r -a i <<< "$(checklist_getitem "$i")" 93 | remove_config set_led_trigger "${i[0]}" 94 | enable_config set_led_trigger "${i[0]}" "$(radiolist_getitem "${RTUI_RADIOLIST_STATE_NEW[0]}")" 95 | done 96 | config_transaction_commit 97 | 98 | msgbox "LED trigger has been updated." 99 | } 100 | 101 | __pattern_breath() { 102 | local rgb=() sections="2" time 103 | mapfile -t rgb < <(color_picker) 104 | if (( ${#rgb[@]} == 0 )) 105 | then 106 | return 107 | fi 108 | 109 | if ! time=$(uint_inputbox "Please enter the total cycle time in milliseconds (ms):" "5000") 110 | then 111 | return 112 | fi 113 | 114 | local sections="2" 115 | local time_per_section=$((time / sections)) 116 | 117 | cat << EOF 118 | 0 $time_per_section ${rgb[0]} $time_per_section 119 | 0 $time_per_section ${rgb[1]} $time_per_section 120 | 0 $time_per_section ${rgb[2]} $time_per_section 121 | EOF 122 | } 123 | 124 | __pattern_rainbow() { 125 | local brightness time 126 | 127 | if ! brightness=$(uint_inputbox "Please enter the maximum brightness:" "255") 128 | then 129 | return 130 | elif (( 10#$brightness < 0 )) || (( 10#$brightness > 255)) 131 | then 132 | msgbox "The maximum brightness must be between 0 and 255. User entered '$brightness'." 133 | return 134 | fi 135 | 136 | if ! time=$(uint_inputbox "Please enter the total cycle time in milliseconds (ms):" "5000") 137 | then 138 | return 139 | fi 140 | 141 | # 2 rainbow patterns are available: 142 | # https://www.instructables.com/How-to-Make-Proper-Rainbow-and-Random-Colors-With-/ 143 | if (( "${RSETUP_RAINBOW_CONSTANT_BRIGHTNESS:-1}" == 1 )) 144 | then 145 | local sections="3" 146 | local time_per_section=$((time / sections)) 147 | cat << EOF 148 | $brightness $time_per_section 0 $time_per_section 0 $time_per_section 149 | 0 $time_per_section $brightness $time_per_section 0 $time_per_section 150 | 0 $time_per_section 0 $time_per_section $brightness $time_per_section 151 | EOF 152 | else 153 | local sections="6" 154 | local time_per_section=$((time / sections)) 155 | cat << EOF 156 | $brightness $time_per_section $brightness $time_per_section 0 $time_per_section 0 $time_per_section 0 $time_per_section $brightness $time_per_section 157 | 0 $time_per_section $brightness $time_per_section $brightness $time_per_section $brightness $time_per_section 0 $time_per_section 0 $time_per_section 158 | 0 $time_per_section 0 $time_per_section 0 $time_per_section $brightness $time_per_section $brightness $time_per_section $brightness $time_per_section 159 | EOF 160 | fi 161 | } 162 | 163 | __pattern_solid() { 164 | local rgb=() 165 | mapfile -t rgb < <(color_picker) 166 | if (( ${#rgb[@]} == 0 )) 167 | then 168 | return 169 | fi 170 | 171 | cat << EOF 172 | ${rgb[0]} 0 ${rgb[0]} 0 173 | ${rgb[1]} 0 ${rgb[1]} 0 174 | ${rgb[2]} 0 ${rgb[2]} 0 175 | EOF 176 | } 177 | 178 | __hardware_rgb_leds() { 179 | checklist_init 180 | 181 | local i rgb_led_name 182 | for i in "$RBUILD_DRIVER_ROOT_PATH/$RBUILD_LED_PWM_DRIVER"/*/leds 183 | do 184 | rgb_led_name="$(basename "$(realpath "$i/..")")" 185 | if [[ -d "$i/$rgb_led_name-red" ]] && [[ -d "$i/$rgb_led_name-green" ]] && [[ -d "$i/$rgb_led_name-blue" ]] 186 | then 187 | checklist_add "$rgb_led_name" "OFF" 188 | fi 189 | done 190 | checklist_emptymsg "No supported devices is detected. 191 | 192 | Please make sure they are enabled first." 193 | if ! checklist_show "Below are the available LEDs. 194 | Select any to update their pattern." || checklist_is_selection_empty 195 | then 196 | return 197 | fi 198 | 199 | radiolist_init 200 | for i in breath rainbow solid 201 | do 202 | radiolist_add "$i" "OFF" 203 | done 204 | if ! radiolist_show "Please select the new pattern:" || radiolist_is_selection_empty 205 | then 206 | return 207 | fi 208 | 209 | local patterns=() 210 | mapfile -t patterns < <(__pattern_"$(radiolist_getitem "${RTUI_RADIOLIST_STATE_NEW[0]}")") 211 | if (( ${#patterns[@]} == 0 )) 212 | then 213 | return 214 | fi 215 | 216 | config_transaction_start 217 | for i in "${RTUI_CHECKLIST_STATE_NEW[@]}" 218 | do 219 | read -r -a i <<< "$(checklist_getitem "$i")" 220 | remove_config set_led_trigger "${i[0]}-red" 221 | enable_config set_led_trigger "${i[0]}-red" "pattern" 222 | remove_config set_led_pattern "${i[0]}-red" 223 | enable_config set_led_pattern "${i[0]}-red" "${patterns[0]}" 224 | 225 | remove_config set_led_trigger "${i[0]}-green" 226 | enable_config set_led_trigger "${i[0]}-green" "pattern" 227 | remove_config set_led_pattern "${i[0]}-green" 228 | enable_config set_led_pattern "${i[0]}-green" "${patterns[1]}" 229 | 230 | remove_config set_led_trigger "${i[0]}-blue" 231 | enable_config set_led_trigger "${i[0]}-blue" "pattern" 232 | remove_config set_led_pattern "${i[0]}-blue" 233 | enable_config set_led_pattern "${i[0]}-blue" "${patterns[2]}" 234 | done 235 | config_transaction_commit 236 | 237 | msgbox "LED pattern has been updated." 238 | } 239 | 240 | __hardware_thermal() { 241 | radiolist_init 242 | 243 | local current_policy available_policies selected_governor 244 | current_policy="$(cat /sys/class/thermal/thermal_zone0/policy)" 245 | mapfile -t available_policies < <(sed -E -e "s/ $//g" -e "s/ /\n/g" /sys/class/thermal/thermal_zone0/available_policies) 246 | for i in "${available_policies[@]}" 247 | do 248 | if [[ "$i" == "$current_policy" ]] 249 | then 250 | radiolist_add "$i" "ON" 251 | else 252 | radiolist_add "$i" "OFF" 253 | fi 254 | done 255 | radiolist_emptymsg "No thermal governor is available." 256 | 257 | if ! radiolist_show "Please select the thermal governor. 258 | Recommendation: fanless or DC fan => power_allocator | PWM fan => step_wise" || radiolist_is_selection_empty 259 | then 260 | return 261 | fi 262 | 263 | selected_governor="$(radiolist_getitem "${RTUI_RADIOLIST_STATE_NEW[0]}")" 264 | 265 | if [[ $selected_governor == "power_allocator" ]] && grep -q "pwm-fan" "/sys/class/thermal/cooling_device"*/type <<< "" 266 | then 267 | msgbox "power_allocator governor is incompatible with pwm_fan driver. 268 | Please disable it and try again." "$RTUI_PALETTE_ERROR" 269 | elif enable_unique_config set_thermal_governor "$selected_governor" 270 | then 271 | msgbox "Thermal governor has been updated." 272 | else 273 | msgbox "Thermal governor could not be updated." "$RTUI_PALETTE_ERROR" 274 | fi 275 | } 276 | 277 | __hardware_dsi_mirror() { 278 | local active_monitors=() dsi_monitors=() external_monitors=() 279 | mapfile -t active_monitors < <(xrandr --listactivemonitors | tail -n +2 | awk '{print $4}') 280 | for i in "${active_monitors[@]}" 281 | do 282 | if [[ "$i" =~ "DSI" ]] 283 | then 284 | dsi_monitors+=( "$i" ) 285 | else 286 | external_monitors+=( "$i" ) 287 | fi 288 | done 289 | 290 | if ! (( ${#dsi_monitors[@]} )) 291 | then 292 | msgbox "No active DSI display is found. 293 | Please check if the overlay is enabled, and the screen is connected and powered on." 294 | return 295 | fi 296 | 297 | if ! (( ${#external_monitors[@]} )) 298 | then 299 | msgbox "No external display is found. 300 | Please check if the screen is connected and powered on." 301 | return 302 | fi 303 | 304 | local selected_dsi="${dsi_monitors[0]}" selected_external=( "${external_monitors[0]}" ) 305 | 306 | if (( ${#dsi_monitors[@]} > 1 )) 307 | then 308 | radiolist_init 309 | for i in "${dsi_monitors[@]}" 310 | do 311 | radiolist_add "$i" "OFF" 312 | done 313 | if ! radiolist_show "Please select the DSI monitor to be mirrored:" || radiolist_is_selection_empty 314 | then 315 | return 316 | fi 317 | selected_dsi="$(radiolist_getitem "${RTUI_RADIOLIST_STATE_NEW[0]}")" 318 | fi 319 | 320 | if (( ${#external_monitors[@]} > 1 )) 321 | then 322 | checklist_init 323 | for i in "${external_monitors[@]}" 324 | do 325 | checklist_add "$i" "OFF" 326 | done 327 | if ! checklist_show "Please select external monitors to mirror the DSI monitor:" || checklist_is_selection_empty 328 | then 329 | return 330 | fi 331 | selected_external=() 332 | for i in "${RTUI_CHECKLIST_STATE_NEW[@]}" 333 | do 334 | selected_external+=( "$(checklist_getitem "$i")" ) 335 | done 336 | fi 337 | 338 | local dsi_res dsi_native_res_x dsi_native_res_y 339 | dsi_res="$(tail -n 1 "/sys/devices/platform/display-subsystem/drm/card*/card*-$selected_dsi/modes")" 340 | dsi_native_res_x="$(cut -d "x" -f 1 <<< "$dsi_res")" 341 | dsi_native_res_y="$(cut -d "x" -f 2 <<< "$dsi_res")" 342 | 343 | local xrandr_cmd=( 344 | xrandr 345 | --output "$selected_dsi" 346 | --auto 347 | --primary 348 | ) 349 | 350 | if (( dsi_native_res_x < dsi_native_res_y )) 351 | then 352 | xrandr_cmd+=( --rotate left ) 353 | dsi_res="${dsi_native_res_y}x${dsi_native_res_x}" 354 | fi 355 | 356 | for i in "${selected_external[@]}" 357 | do 358 | xrandr_cmd+=( --output "$i" ) 359 | done 360 | xrandr_cmd+=( 361 | --auto 362 | --scale-from "$dsi_res" 363 | --same-as "$selected_dsi" 364 | ) 365 | 366 | "${xrandr_cmd[@]}" 367 | 368 | radxa-map-tp 369 | 370 | msgbox "DSI display mirroring has been enabled. 371 | To return to normal mode, please use your desktop environment's display setup tool." 372 | } 373 | 374 | __check_service_status() { 375 | local service="$1$2" 376 | case "$(systemctl is-enabled "$service")" in 377 | enabled) 378 | checklist_add "$service" "ON" 379 | ;; 380 | disabled) 381 | checklist_add "$service" "OFF" 382 | ;; 383 | esac 384 | } 385 | 386 | __hardware_otg() { 387 | checklist_init 388 | 389 | local udc udc_function udc_function_list services status i 390 | readarray -t services < <(jq -er '.[]' /usr/share/radxa-otgutils/services.json || echo -en "radxa-adbd@\nradxa-usbnet@") 391 | for udc in /sys/class/udc/* 392 | do 393 | udc="$(basename "$udc")" 394 | for i in "${services[@]}" 395 | do 396 | __check_service_status "$i" "$udc" 397 | done 398 | done 399 | 400 | checklist_emptymsg "No UDC exists. 401 | You can turn on the OTG port Peripheral mode device tree overlay at rsetup and look in the /sys/class/udc directory for." 402 | 403 | if ! checklist_show "Below are the available UDC functions. 404 | Select any to update their status." 405 | then 406 | return 407 | fi 408 | 409 | for i in "${RTUI_CHECKLIST_STATE_NEW[@]}" 410 | do 411 | udc_function_list+=("$(checklist_getitem "$i")") 412 | done 413 | if (( $(printf "%s\n" "${udc_function_list[@]}" | grep -o "radxa-adbd@" | wc -l) > 1 )) 414 | then 415 | msgbox "radxa-adbd can be enabled at most on one given port. Please reduce your selection and try again." 416 | return 417 | fi 418 | 419 | length=${#RTUI_CHECKLIST[@]} 420 | for ((i = 0; i < length; i+=3)) 421 | do 422 | udc_function="${RTUI_CHECKLIST[i+1]}" 423 | status="${RTUI_CHECKLIST[i+2]}" 424 | if [[ "$status" == "OFF" ]] 425 | then 426 | systemctl disable --now "$udc_function" 427 | fi 428 | done 429 | for ((i = 0; i < length; i+=3)) 430 | do 431 | udc_function="${RTUI_CHECKLIST[i+1]}" 432 | status="${RTUI_CHECKLIST[i+2]}" 433 | if [[ "$status" == "ON" ]] 434 | then 435 | systemctl enable --now "$udc_function" 436 | fi 437 | done 438 | } 439 | 440 | __hardware() { 441 | menu_init 442 | menu_add __hardware_video "Video capture devices" 443 | menu_add __hardware_gpio_leds "GPIO LEDs" 444 | menu_add __hardware_rgb_leds "RGB LEDs" 445 | menu_add __hardware_thermal "Thermal governor" 446 | menu_add __hardware_dsi_mirror "Configure DSI display mirroring" 447 | menu_add __hardware_gpio "40-pin GPIO" 448 | menu_add __hardware_otg "USB OTG services" 449 | menu_show "Manage on-board hardware" 450 | } 451 | -------------------------------------------------------------------------------- /src/usr/lib/rsetup/tui/local/local.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | __local_change_timezone() { 4 | dpkg-reconfigure tzdata 5 | } 6 | 7 | __local_change_locale() { 8 | dpkg-reconfigure locales 9 | } 10 | 11 | __local_keyboard_layout() { 12 | dpkg-reconfigure keyboard-configuration 13 | } 14 | 15 | __local_wifi_country() { 16 | local iface 17 | iface=$(iw dev | grep Interface | awk '{print $2}') 18 | if [[ -z "$iface" ]] 19 | then 20 | msgbox "No wireless interface found." "$RTUI_PALETTE_ERROR" 21 | return 1 22 | fi 23 | 24 | if ! wpa_cli -i "$iface" status &>/dev/null 25 | then 26 | msgbox "Could not communicate with wpa_supplicant." "$RTUI_PALETTE_ERROR" 27 | return 1 28 | fi 29 | 30 | radiolist_init 31 | 32 | while read -r 33 | do 34 | if grep -q "^#.*" <<< "$REPLY" 35 | then 36 | continue 37 | fi 38 | radiolist_add "$REPLY" "OFF" 39 | done < /usr/share/zoneinfo/iso3166.tab 40 | 41 | if radiolist_show "Select your Wi-Fi country:" && ! radiolist_is_selection_empty 42 | then 43 | local only_shrinked_index=${RTUI_RADIOLIST_STATE_NEW} 44 | trimmed_index=${only_shrinked_index//\"} 45 | index=$(( 3 * trimmed_index + 1 )) 46 | local country=${RTUI_RADIOLIST[$index]} 47 | 48 | if yesno "Are you sure to change Wi-Fi country to: '$country'?" 49 | then 50 | country=$(echo "$country" | cut -c 1-2) 51 | 52 | wpa_cli -i "$iface" set country "$country" 53 | wpa_cli -i "$iface" save_config > /dev/null 2>&1 54 | iw reg set "$country" 55 | 56 | local file="/etc/default/crda" 57 | if [[ ! -f $file ]] 58 | then 59 | touch $file 60 | fi 61 | 62 | if grep -q "^REGDOMAIN=" "$file" 63 | then 64 | sed -i "/REGDOMAIN=.*/cREGDOMAIN=$country" $file 65 | else 66 | sed -i "\$aREGDOMAIN=$country" $file 67 | fi 68 | 69 | if command -v rfkill &> /dev/null 70 | then 71 | rfkill unblock wifi 72 | fi 73 | msgbox "Wireless LAN country set to $country." 74 | fi 75 | fi 76 | } 77 | 78 | __local() { 79 | menu_init 80 | menu_add __local_change_timezone "Change Timezone" 81 | menu_add __local_change_locale "Change Locale" 82 | if $DEBUG 83 | then 84 | menu_add __local_keyboard_layout "Change Keyboard Layout" 85 | fi 86 | menu_add __local_wifi_country "Change Wi-Fi Country" 87 | menu_show "Please select an option below:" 88 | } 89 | -------------------------------------------------------------------------------- /src/usr/lib/rsetup/tui/main.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | # shellcheck source=externals/librtui/src/lib/librtui/tui.sh 4 | source "/usr/lib/librtui/tui.sh" 5 | 6 | # shellcheck source=src/usr/lib/rsetup/tui/overlay/overlay.sh 7 | source "/usr/lib/rsetup/tui/overlay/overlay.sh" 8 | # shellcheck source=src/usr/lib/rsetup/tui/comm/comm.sh 9 | source "/usr/lib/rsetup/tui/comm/comm.sh" 10 | # shellcheck source=src/usr/lib/rsetup/tui/hardware/hardware.sh 11 | source "/usr/lib/rsetup/tui/hardware/hardware.sh" 12 | # shellcheck source=src/usr/lib/rsetup/tui/local/local.sh 13 | source "/usr/lib/rsetup/tui/local/local.sh" 14 | # shellcheck source=src/usr/lib/rsetup/tui/system/system.sh 15 | source "/usr/lib/rsetup/tui/system/system.sh" 16 | # shellcheck source=src/usr/lib/rsetup/tui/task/task.sh 17 | source "/usr/lib/rsetup/tui/task/task.sh" 18 | # shellcheck source=src/usr/lib/rsetup/tui/user/user.sh 19 | source "/usr/lib/rsetup/tui/user/user.sh" 20 | 21 | __tui_about() { 22 | msgbox "rsetup - Radxa system setup utility 23 | 24 | Copyright 2022-$(date +%Y) Radxa Computer Co., Ltd" 25 | } 26 | 27 | __tui_main() { 28 | menu_init 29 | menu_add __system "System" 30 | menu_add __hardware "Hardware" 31 | menu_add __overlay "Overlays" 32 | menu_add __comm "Connectivity" 33 | menu_add __user "User Settings" 34 | menu_add __local "Localization" 35 | menu_add __task "Common Tasks" 36 | menu_add __tui_about "About" 37 | menu_show "Please select an option below:" 38 | } 39 | -------------------------------------------------------------------------------- /src/usr/lib/rsetup/tui/overlay/overlay.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | # shellcheck source=src/usr/lib/rsetup/mod/hwid.sh 4 | source "/usr/lib/rsetup/mod/hwid.sh" 5 | source "/usr/lib/rsetup/mod/pkg.sh" 6 | source "/usr/lib/rsetup/mod/overlay.sh" 7 | 8 | __overlay_install() { 9 | if ! __depends_package "Install 3rd party overlay" "gcc" "linux-headers-$(uname -r)" 10 | then 11 | return 12 | fi 13 | 14 | if ! yesno "3rd party overlay could physically damage your system. 15 | In addition, they may miss important metadata for rsetup to recognize correctly. 16 | This means if you ever run 'Manage overlay' function again, your custom overlays 17 | might be disabled, and you will have to manually reenable them. 18 | 19 | Are you sure?" 20 | then 21 | return 22 | fi 23 | 24 | local item basename err=0 25 | if ! item="$(fselect "$PWD")" 26 | then 27 | return 28 | fi 29 | 30 | load_u-boot_setting 31 | 32 | basename="$(basename "$item")" 33 | 34 | case $basename in 35 | *.dtbo) 36 | cp "$item" "$U_BOOT_FDT_OVERLAYS_DIR/$basename" 37 | ;; 38 | *.dts|*.dtso) 39 | basename="${basename%.dts*}.dtbo" 40 | 41 | compile_dtb "$item" "$U_BOOT_FDT_OVERLAYS_DIR/$basename" || err=$? 42 | case $err in 43 | 0) : ;; 44 | 1) 45 | msgbox "Unable to preprocess the source code!" 46 | return 47 | ;; 48 | 2) 49 | msgbox "Unable to compile the source code!" 50 | return 51 | ;; 52 | *) 53 | msgbox "Unknown error $err occured during compilation." 54 | return 55 | ;; 56 | esac 57 | ;; 58 | *) 59 | msgbox "Unknown file format: $basename" 60 | return 61 | ;; 62 | esac 63 | 64 | if u-boot-update >/dev/null 65 | then 66 | msgbox "Selected overlays will be enabled at next boot." 67 | else 68 | msgbox "Unable to update the boot config." 69 | fi 70 | } 71 | 72 | __overlay_show() { 73 | local validation="${1:-true}" 74 | infobox "Searching available overlays may take a while, please wait..." 75 | load_u-boot_setting 76 | 77 | local dtbos=( "$U_BOOT_FDT_OVERLAYS_DIR"/*.dtbo* ) 78 | mapfile -t dtbos < <(dtbo_is_compatible "${dtbos[@]}") 79 | 80 | checklist_init 81 | # Bash doesn support IFS=$'\0' 82 | # Use array to emulate this 83 | local items=() 84 | mapfile -t items < <(parse_dtbo --show-overlays "title" "${dtbos[@]}") 85 | while (( ${#items[@]} >= 3 )) 86 | do 87 | checklist_add "${items[0]/$'\n'}" "${items[1]/$'\n'}" "${items[2]/$'\n'}" 88 | items=("${items[@]:3}") 89 | done 90 | 91 | checklist_emptymsg "Unable to find any compatible overlay under $U_BOOT_FDT_OVERLAYS_DIR." 92 | 93 | while true 94 | do 95 | if ! checklist_show "Please select overlays:" 96 | then 97 | return 1 98 | fi 99 | 100 | if $validation 101 | then 102 | return 103 | fi 104 | done 105 | } 106 | 107 | __overlay_validate() { 108 | local i item 109 | 110 | if is_overlay_unchanged; then 111 | if ! yesno "You did not make any changes to overlay selection. 112 | 113 | Are you sure to continue?"; then 114 | return 1 115 | fi 116 | fi 117 | 118 | check_overlay_conflict_init 119 | for i in "${RTUI_CHECKLIST_STATE_NEW[@]}" 120 | do 121 | item="$(checklist_getitem "$i")" 122 | if ! check_overlay_conflict "$U_BOOT_FDT_OVERLAYS_DIR/$item"* 123 | then 124 | return 1 125 | fi 126 | 127 | local title package 128 | mapfile -t title < <(parse_dtbo --default-value "file" "title" "$U_BOOT_FDT_OVERLAYS_DIR/$item"*) 129 | mapfile -t package < <(parse_dtbo "package" "$U_BOOT_FDT_OVERLAYS_DIR/$item"*) 130 | if [[ "${package[0]}" != "null" ]] 131 | then 132 | if ! __depends_package "${title[0]}" "${package[@]}" 133 | then 134 | msgbox "Failed to install required packages for '${title[0]}'." "$RTUI_PALETTE_ERROR" 135 | return 1 136 | fi 137 | fi 138 | done 139 | } 140 | 141 | __overlay_manage() { 142 | if ! __overlay_show __overlay_validate 143 | then 144 | return 145 | fi 146 | 147 | disable_overlays 148 | 149 | local items=() ret 150 | for i in "${RTUI_CHECKLIST_STATE_NEW[@]}" 151 | do 152 | items+=("$(checklist_getitem "$i")") 153 | done 154 | 155 | if (( ${#items[@]} == 0 )) 156 | then 157 | u-boot-update 158 | return 159 | fi 160 | 161 | if enable_overlays "${items[@]}" 162 | then 163 | msgbox "Selected overlays will be enabled at next boot." 164 | else 165 | ret="$?" 166 | case "$(( ret - 256 ))" in 167 | "$ERROR_ILLEGAL_PARAMETERS") 168 | msgbox "The selection contains non-existing overlays. Did you delete them?" 169 | ;; 170 | *) 171 | msgbox "Unable to update the boot config." 172 | ;; 173 | esac 174 | fi 175 | } 176 | 177 | __overlay_info() { 178 | if ! __overlay_show 179 | then 180 | return 181 | fi 182 | 183 | local item title category description exclusive package i 184 | for i in "${RTUI_CHECKLIST_STATE_NEW[@]}" 185 | do 186 | item="$(checklist_getitem "$i")" 187 | mapfile -t title < <(parse_dtbo "title" "$U_BOOT_FDT_OVERLAYS_DIR/$item"*) 188 | mapfile -t category < <(parse_dtbo "category" "$U_BOOT_FDT_OVERLAYS_DIR/$item"*) 189 | mapfile -t exclusive < <(parse_dtbo "exclusive" "$U_BOOT_FDT_OVERLAYS_DIR/$item"*) 190 | mapfile -t package < <(parse_dtbo "package" "$U_BOOT_FDT_OVERLAYS_DIR/$item"*) 191 | description="$(parse_dtbo "description" "$U_BOOT_FDT_OVERLAYS_DIR/$item"*)" 192 | if (( ${#title[@]} == 1 )) && [[ "${title[0]}" == "null" ]] 193 | then 194 | title=( "$item" ) 195 | description="This is a 3rd party overlay. No metadata is available." 196 | fi 197 | if ! yesno "Title: ${title[0]} 198 | Category: ${category[0]} 199 | Exclusive: ${exclusive[*]} 200 | Package: ${package[*]} 201 | Description: 202 | 203 | $description" 204 | then 205 | break 206 | fi 207 | done 208 | } 209 | 210 | __overlay_rebuild() { 211 | if ! yesno "WARNING 212 | 213 | This feature will rebuild the overlay folder. 214 | Overlays *provided by Radxa* will be replaced by the one from the current running kernel, 215 | and incompatible ones will be removed. 216 | 217 | This operation will preserve the overlay enable status as much as possible, 218 | and user supplied overlays will be untouched. 219 | 220 | Are you sure? 221 | " 222 | then 223 | return 224 | fi 225 | 226 | if rebuild_overlays "$(uname -r)" "$(get_soc_vendor)" 227 | then 228 | msgbox "Overlays have been reset." 229 | else 230 | msgbox "Unable to reset overlays." 231 | fi 232 | } 233 | 234 | __overlay() { 235 | if ! show_once "RSETUP_OVERLAY_WARNING" yesno "WARNING 236 | 237 | Overlays, by its nature, require \"hidden\" knowledge about the running device tree. 238 | While major breakage is unlikely, this does mean that after kernel update, the overlay may cease to work. 239 | 240 | If you accept the risk, select Yes to continue. 241 | Otherwise, select No to go back to previous menu." 242 | then 243 | return 244 | fi 245 | 246 | load_u-boot_setting 247 | 248 | if [[ -n "${U_BOOT_FDT_OVERLAYS:-}" ]] 249 | then 250 | msgbox \ 251 | "Detected 'U_BOOT_FDT_OVERLAYS' in '/etc/default/u-boot'. 252 | This usually happens when you want to customize your boot process. 253 | To avoid potential conflicts, overlay feature is temporarily disabled until such customization is reverted." 254 | return 255 | fi 256 | 257 | menu_init 258 | menu_add __overlay_manage "Manage overlays" 259 | menu_add __overlay_info "View overlay info" 260 | menu_add __overlay_install "Install 3rd party overlay" 261 | menu_add __overlay_rebuild "Rebuild overlays" 262 | menu_show "Configure Device Tree Overlay" 263 | } 264 | -------------------------------------------------------------------------------- /src/usr/lib/rsetup/tui/system/system.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | __system_system_update() { 4 | if yesno "System will be updated, continue?" 5 | then 6 | if ! system_update; then 7 | cat < 4 | 5 | _rsetup_completions() { 6 | 7 | case "$COMP_CWORD" in 8 | 0) : ;; 9 | 1) 10 | local subcommands=( 11 | mpp_chromium 12 | mpp_enable_kernel_logging 13 | mpp_gst_play 14 | mpp_play 15 | ) 16 | 17 | mapfile -t COMPREPLY < <(compgen -W "${subcommands[*]}" -- "${COMP_WORDS[COMP_CWORD]}") 18 | ;; 19 | *) 20 | if [[ "$(type -t "_rsdk_${COMP_WORDS[1]}_completions")" == "function" ]]; then 21 | "_rsetup_${COMP_WORDS[1]}_completions" 22 | fi 23 | ;; 24 | esac 25 | } 26 | 27 | complete -o default -F _rsetup_completions rsetup 28 | -------------------------------------------------------------------------------- /src/usr/share/librtui/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /src/usr/share/man/man8/.gitignore: -------------------------------------------------------------------------------- 1 | /*.8 2 | -------------------------------------------------------------------------------- /src/usr/share/man/man8/rsetup.8.md: -------------------------------------------------------------------------------- 1 | # RESTUP(8) 2 | 3 | ## NAME 4 | 5 | rsetup - Radxa system setup utility 6 | 7 | ## SYNOPSIS 8 | 9 | **rsetup** 10 | 11 | ## DESCRIPTION 12 | 13 | Radxa system setup utility (rsetup) provides an user friendly menu to 14 | perform many essential system configuration tasks. It also provides 15 | an on-boot configuration service to allow customization. 16 | -------------------------------------------------------------------------------- /theme/css/chrome.css: -------------------------------------------------------------------------------- 1 | /* CSS for UI elements (a.k.a. chrome) */ 2 | 3 | @import "variables.css"; 4 | 5 | html { 6 | scrollbar-color: var(--scrollbar) var(--bg); 7 | } 8 | #searchresults a, 9 | .content a:link, 10 | a:visited, 11 | a > .hljs { 12 | color: var(--links); 13 | } 14 | 15 | /* 16 | body-container is necessary because mobile browsers don't seem to like 17 | overflow-x on the body tag when there is a tag. 18 | */ 19 | #body-container { 20 | /* 21 | This is used when the sidebar pushes the body content off the side of 22 | the screen on small screens. Without it, dragging on mobile Safari 23 | will want to reposition the viewport in a weird way. 24 | */ 25 | overflow-x: clip; 26 | } 27 | 28 | /* Menu Bar */ 29 | 30 | #menu-bar, 31 | #menu-bar-hover-placeholder { 32 | z-index: 101; 33 | margin: auto calc(0px - var(--page-padding)); 34 | } 35 | #menu-bar { 36 | position: relative; 37 | display: flex; 38 | flex-wrap: wrap; 39 | background-color: var(--bg); 40 | border-bottom-color: var(--bg); 41 | border-bottom-width: 1px; 42 | border-bottom-style: solid; 43 | } 44 | #menu-bar.sticky, 45 | .js #menu-bar-hover-placeholder:hover + #menu-bar, 46 | .js #menu-bar:hover, 47 | .js.sidebar-visible #menu-bar { 48 | position: -webkit-sticky; 49 | position: sticky; 50 | top: 0 !important; 51 | } 52 | #menu-bar-hover-placeholder { 53 | position: sticky; 54 | position: -webkit-sticky; 55 | top: 0; 56 | height: var(--menu-bar-height); 57 | } 58 | #menu-bar.bordered { 59 | border-bottom-color: var(--table-border-color); 60 | } 61 | #menu-bar i, 62 | #menu-bar .icon-button { 63 | position: relative; 64 | padding: 0 8px; 65 | z-index: 10; 66 | line-height: var(--menu-bar-height); 67 | cursor: pointer; 68 | transition: color 0.5s; 69 | } 70 | @media only screen and (max-width: 420px) { 71 | #menu-bar i, 72 | #menu-bar .icon-button { 73 | padding: 0 5px; 74 | } 75 | } 76 | 77 | .icon-button { 78 | border: none; 79 | background: none; 80 | padding: 0; 81 | color: inherit; 82 | } 83 | .icon-button i { 84 | margin: 0; 85 | } 86 | 87 | .right-buttons { 88 | margin: 0 15px; 89 | } 90 | .right-buttons a { 91 | text-decoration: none; 92 | } 93 | 94 | .left-buttons { 95 | display: flex; 96 | margin: 0 5px; 97 | } 98 | .no-js .left-buttons { 99 | display: none; 100 | } 101 | 102 | .menu-title { 103 | display: inline-block; 104 | font-weight: 200; 105 | font-size: 2.4rem; 106 | line-height: var(--menu-bar-height); 107 | text-align: center; 108 | margin: 0; 109 | flex: 1; 110 | white-space: nowrap; 111 | overflow: hidden; 112 | text-overflow: ellipsis; 113 | } 114 | .js .menu-title { 115 | cursor: pointer; 116 | } 117 | 118 | .menu-bar, 119 | .menu-bar:visited, 120 | .nav-chapters, 121 | .nav-chapters:visited, 122 | .mobile-nav-chapters, 123 | .mobile-nav-chapters:visited, 124 | .menu-bar .icon-button, 125 | .menu-bar a i { 126 | color: var(--icons); 127 | } 128 | 129 | .menu-bar i:hover, 130 | .menu-bar .icon-button:hover, 131 | .nav-chapters:hover, 132 | .mobile-nav-chapters i:hover { 133 | color: var(--icons-hover); 134 | } 135 | 136 | /* Nav Icons */ 137 | 138 | .nav-chapters { 139 | font-size: 2.5em; 140 | text-align: center; 141 | text-decoration: none; 142 | 143 | position: fixed; 144 | top: 0; 145 | bottom: 0; 146 | margin: 0; 147 | max-width: 150px; 148 | min-width: 90px; 149 | 150 | display: flex; 151 | justify-content: center; 152 | align-content: center; 153 | flex-direction: column; 154 | 155 | transition: 156 | color 0.5s, 157 | background-color 0.5s; 158 | } 159 | 160 | .nav-chapters:hover { 161 | text-decoration: none; 162 | background-color: var(--theme-hover); 163 | transition: 164 | background-color 0.15s, 165 | color 0.15s; 166 | } 167 | 168 | .nav-wrapper { 169 | margin-top: 50px; 170 | display: none; 171 | } 172 | 173 | .mobile-nav-chapters { 174 | font-size: 2.5em; 175 | text-align: center; 176 | text-decoration: none; 177 | width: 90px; 178 | border-radius: 5px; 179 | background-color: var(--sidebar-bg); 180 | } 181 | 182 | .previous { 183 | float: left; 184 | } 185 | 186 | .next { 187 | float: right; 188 | right: var(--page-padding); 189 | } 190 | 191 | @media only screen and (max-width: 1080px) { 192 | .nav-wide-wrapper { 193 | display: none; 194 | } 195 | .nav-wrapper { 196 | display: block; 197 | } 198 | } 199 | 200 | @media only screen and (max-width: 1380px) { 201 | .sidebar-visible .nav-wide-wrapper { 202 | display: none; 203 | } 204 | .sidebar-visible .nav-wrapper { 205 | display: block; 206 | } 207 | } 208 | 209 | /* Inline code */ 210 | 211 | :not(pre) > .hljs { 212 | display: inline; 213 | padding: 0.1em 0.3em; 214 | border-radius: 3px; 215 | } 216 | 217 | :not(pre):not(a) > .hljs { 218 | color: var(--inline-code-color); 219 | overflow-x: initial; 220 | } 221 | 222 | a:hover > .hljs { 223 | text-decoration: underline; 224 | } 225 | 226 | pre { 227 | position: relative; 228 | } 229 | pre > .buttons { 230 | position: absolute; 231 | z-index: 100; 232 | right: 0px; 233 | top: 2px; 234 | margin: 0px; 235 | padding: 2px 0px; 236 | 237 | color: var(--sidebar-fg); 238 | cursor: pointer; 239 | visibility: hidden; 240 | opacity: 0; 241 | transition: 242 | visibility 0.1s linear, 243 | opacity 0.1s linear; 244 | } 245 | pre:hover > .buttons { 246 | visibility: visible; 247 | opacity: 1; 248 | } 249 | pre > .buttons :hover { 250 | color: var(--sidebar-active); 251 | border-color: var(--icons-hover); 252 | background-color: var(--theme-hover); 253 | } 254 | pre > .buttons i { 255 | margin-left: 8px; 256 | } 257 | pre > .buttons button { 258 | cursor: inherit; 259 | margin: 0px 5px; 260 | padding: 3px 5px; 261 | font-size: 14px; 262 | 263 | border-style: solid; 264 | border-width: 1px; 265 | border-radius: 4px; 266 | border-color: var(--icons); 267 | background-color: var(--theme-popup-bg); 268 | transition: 100ms; 269 | transition-property: color, border-color, background-color; 270 | color: var(--icons); 271 | } 272 | @media (pointer: coarse) { 273 | pre > .buttons button { 274 | /* On mobile, make it easier to tap buttons. */ 275 | padding: 0.3rem 1rem; 276 | } 277 | } 278 | pre > code { 279 | padding: 1rem; 280 | } 281 | 282 | /* FIXME: ACE editors overlap their buttons because ACE does absolute 283 | positioning within the code block which breaks padding. The only solution I 284 | can think of is to move the padding to the outer pre tag (or insert a div 285 | wrapper), but that would require fixing a whole bunch of CSS rules. 286 | */ 287 | .hljs.ace_editor { 288 | padding: 0rem 0rem; 289 | } 290 | 291 | pre > .result { 292 | margin-top: 10px; 293 | } 294 | 295 | /* Search */ 296 | 297 | #searchresults a { 298 | text-decoration: none; 299 | } 300 | 301 | mark { 302 | border-radius: 2px; 303 | padding: 0 3px 1px 3px; 304 | margin: 0 -3px -1px -3px; 305 | background-color: var(--search-mark-bg); 306 | transition: background-color 300ms linear; 307 | cursor: pointer; 308 | } 309 | 310 | mark.fade-out { 311 | background-color: rgba(0, 0, 0, 0) !important; 312 | cursor: auto; 313 | } 314 | 315 | .searchbar-outer { 316 | margin-left: auto; 317 | margin-right: auto; 318 | max-width: var(--content-max-width); 319 | } 320 | 321 | #searchbar { 322 | width: 100%; 323 | margin: 5px auto 0px auto; 324 | padding: 10px 16px; 325 | transition: box-shadow 300ms ease-in-out; 326 | border: 1px solid var(--searchbar-border-color); 327 | border-radius: 3px; 328 | background-color: var(--searchbar-bg); 329 | color: var(--searchbar-fg); 330 | } 331 | #searchbar:focus, 332 | #searchbar.active { 333 | box-shadow: 0 0 3px var(--searchbar-shadow-color); 334 | } 335 | 336 | .searchresults-header { 337 | font-weight: bold; 338 | font-size: 1em; 339 | padding: 18px 0 0 5px; 340 | color: var(--searchresults-header-fg); 341 | } 342 | 343 | .searchresults-outer { 344 | margin-left: auto; 345 | margin-right: auto; 346 | max-width: var(--content-max-width); 347 | border-bottom: 1px dashed var(--searchresults-border-color); 348 | } 349 | 350 | ul#searchresults { 351 | list-style: none; 352 | padding-left: 20px; 353 | } 354 | ul#searchresults li { 355 | margin: 10px 0px; 356 | padding: 2px; 357 | border-radius: 2px; 358 | } 359 | ul#searchresults li.focus { 360 | background-color: var(--searchresults-li-bg); 361 | } 362 | ul#searchresults span.teaser { 363 | display: block; 364 | clear: both; 365 | margin: 5px 0 0 20px; 366 | font-size: 0.8em; 367 | } 368 | ul#searchresults span.teaser em { 369 | font-weight: bold; 370 | font-style: normal; 371 | } 372 | 373 | /* Sidebar */ 374 | 375 | .sidebar { 376 | position: fixed; 377 | left: 0; 378 | top: 0; 379 | bottom: 0; 380 | width: var(--sidebar-width); 381 | font-size: 0.875em; 382 | box-sizing: border-box; 383 | -webkit-overflow-scrolling: touch; 384 | overscroll-behavior-y: contain; 385 | background-color: var(--sidebar-bg); 386 | color: var(--sidebar-fg); 387 | } 388 | .sidebar-resizing { 389 | -moz-user-select: none; 390 | -webkit-user-select: none; 391 | -ms-user-select: none; 392 | user-select: none; 393 | } 394 | .js:not(.sidebar-resizing) .sidebar { 395 | transition: transform 0.3s; /* Animation: slide away */ 396 | } 397 | .sidebar code { 398 | line-height: 2em; 399 | } 400 | .sidebar .sidebar-scrollbox { 401 | overflow-y: auto; 402 | position: absolute; 403 | top: 0; 404 | bottom: 0; 405 | left: 0; 406 | right: 0; 407 | padding: 10px 10px; 408 | } 409 | .sidebar .sidebar-resize-handle { 410 | position: absolute; 411 | cursor: col-resize; 412 | width: 0; 413 | right: 0; 414 | top: 0; 415 | bottom: 0; 416 | } 417 | .js .sidebar .sidebar-resize-handle { 418 | cursor: col-resize; 419 | width: 5px; 420 | } 421 | .sidebar-hidden .sidebar { 422 | transform: translateX(calc(0px - var(--sidebar-width))); 423 | } 424 | .sidebar::-webkit-scrollbar { 425 | background: var(--sidebar-bg); 426 | } 427 | .sidebar::-webkit-scrollbar-thumb { 428 | background: var(--scrollbar); 429 | } 430 | 431 | .sidebar-visible .page-wrapper { 432 | transform: translateX(var(--sidebar-width)); 433 | } 434 | @media only screen and (min-width: 620px) { 435 | .sidebar-visible .page-wrapper { 436 | transform: none; 437 | margin-left: var(--sidebar-width); 438 | } 439 | } 440 | 441 | .chapter { 442 | list-style: none outside none; 443 | padding-left: 0; 444 | line-height: 2.2em; 445 | } 446 | 447 | .chapter ol { 448 | width: 100%; 449 | } 450 | 451 | .chapter li { 452 | display: flex; 453 | color: var(--sidebar-non-existant); 454 | } 455 | .chapter li a { 456 | display: block; 457 | padding: 0; 458 | text-decoration: none; 459 | color: var(--sidebar-fg); 460 | } 461 | 462 | .chapter li a:hover { 463 | color: var(--sidebar-active); 464 | } 465 | 466 | .chapter li a.active { 467 | color: var(--sidebar-active); 468 | } 469 | 470 | .chapter li > a.toggle { 471 | cursor: pointer; 472 | display: block; 473 | margin-left: auto; 474 | padding: 0 10px; 475 | user-select: none; 476 | opacity: 0.68; 477 | } 478 | 479 | .chapter li > a.toggle div { 480 | transition: transform 0.5s; 481 | } 482 | 483 | /* collapse the section */ 484 | .chapter li:not(.expanded) + li > ol { 485 | display: none; 486 | } 487 | 488 | .chapter li.chapter-item { 489 | line-height: 1.5em; 490 | margin-top: 0.6em; 491 | } 492 | 493 | .chapter li.expanded > a.toggle div { 494 | transform: rotate(90deg); 495 | } 496 | 497 | .spacer { 498 | width: 100%; 499 | height: 3px; 500 | margin: 5px 0px; 501 | } 502 | .chapter .spacer { 503 | background-color: var(--sidebar-spacer); 504 | } 505 | 506 | @media (-moz-touch-enabled: 1), (pointer: coarse) { 507 | .chapter li a { 508 | padding: 5px 0; 509 | } 510 | .spacer { 511 | margin: 10px 0; 512 | } 513 | } 514 | 515 | .section { 516 | list-style: none outside none; 517 | padding-left: 20px; 518 | line-height: 1.9em; 519 | } 520 | 521 | /* Theme Menu Popup */ 522 | 523 | .theme-popup { 524 | position: absolute; 525 | left: 10px; 526 | top: var(--menu-bar-height); 527 | z-index: 1000; 528 | border-radius: 4px; 529 | font-size: 0.7em; 530 | color: var(--fg); 531 | background: var(--theme-popup-bg); 532 | border: 1px solid var(--theme-popup-border); 533 | margin: 0; 534 | padding: 0; 535 | list-style: none; 536 | display: none; 537 | /* Don't let the children's background extend past the rounded corners. */ 538 | overflow: hidden; 539 | } 540 | .theme-popup .default { 541 | color: var(--icons); 542 | } 543 | .theme-popup .theme { 544 | width: 100%; 545 | border: 0; 546 | margin: 0; 547 | padding: 2px 20px; 548 | line-height: 25px; 549 | white-space: nowrap; 550 | text-align: left; 551 | cursor: pointer; 552 | color: inherit; 553 | background: inherit; 554 | font-size: inherit; 555 | } 556 | .theme-popup .theme:hover { 557 | background-color: var(--theme-hover); 558 | } 559 | 560 | .theme-selected::before { 561 | display: inline-block; 562 | content: "✓"; 563 | margin-left: -14px; 564 | width: 14px; 565 | } 566 | -------------------------------------------------------------------------------- /theme/css/general.css: -------------------------------------------------------------------------------- 1 | /* Base styles and content styles */ 2 | 3 | @import "variables.css"; 4 | 5 | :root { 6 | /* Browser default font-size is 16px, this way 1 rem = 10px */ 7 | font-size: 62.5%; 8 | } 9 | 10 | html { 11 | font-family: "Open Sans", sans-serif; 12 | color: var(--fg); 13 | background-color: var(--bg); 14 | text-size-adjust: none; 15 | -webkit-text-size-adjust: none; 16 | } 17 | 18 | body { 19 | margin: 0; 20 | font-size: 1.6rem; 21 | overflow-x: hidden; 22 | } 23 | 24 | code { 25 | font-family: var(--mono-font) !important; 26 | font-size: var(--code-font-size); 27 | } 28 | 29 | /* make long words/inline code not x overflow */ 30 | main { 31 | overflow-wrap: break-word; 32 | } 33 | 34 | /* make wide tables scroll if they overflow */ 35 | .table-wrapper { 36 | overflow-x: auto; 37 | } 38 | 39 | /* Don't change font size in headers. */ 40 | h1 code, 41 | h2 code, 42 | h3 code, 43 | h4 code, 44 | h5 code, 45 | h6 code { 46 | font-size: unset; 47 | } 48 | 49 | .left { 50 | float: left; 51 | } 52 | .right { 53 | float: right; 54 | } 55 | .boring { 56 | opacity: 0.6; 57 | } 58 | .hide-boring .boring { 59 | display: none; 60 | } 61 | .hidden { 62 | display: none !important; 63 | } 64 | 65 | h2, 66 | h3 { 67 | margin-top: 2.5em; 68 | } 69 | h4, 70 | h5 { 71 | margin-top: 2em; 72 | } 73 | 74 | .header + .header h3, 75 | .header + .header h4, 76 | .header + .header h5 { 77 | margin-top: 1em; 78 | } 79 | 80 | h1:target::before, 81 | h2:target::before, 82 | h3:target::before, 83 | h4:target::before, 84 | h5:target::before, 85 | h6:target::before { 86 | display: inline-block; 87 | content: "»"; 88 | margin-left: -30px; 89 | width: 30px; 90 | } 91 | 92 | /* This is broken on Safari as of version 14, but is fixed 93 | in Safari Technology Preview 117 which I think will be Safari 14.2. 94 | https://bugs.webkit.org/show_bug.cgi?id=218076 95 | */ 96 | :target { 97 | scroll-margin-top: calc(var(--menu-bar-height) + 0.5em); 98 | } 99 | 100 | .page { 101 | outline: 0; 102 | padding: 0 var(--page-padding); 103 | margin-top: calc( 104 | 0px - var(--menu-bar-height) 105 | ); /* Compensate for the #menu-bar-hover-placeholder */ 106 | } 107 | .page-wrapper { 108 | box-sizing: border-box; 109 | } 110 | .js:not(.sidebar-resizing) .page-wrapper { 111 | transition: 112 | margin-left 0.3s ease, 113 | transform 0.3s ease; /* Animation: slide away */ 114 | } 115 | 116 | .content { 117 | overflow-y: auto; 118 | padding: 0 5px 50px 5px; 119 | } 120 | .content main { 121 | margin-left: auto; 122 | margin-right: auto; 123 | max-width: var(--content-max-width); 124 | } 125 | .content p { 126 | line-height: 1.45em; 127 | } 128 | .content ol { 129 | line-height: 1.45em; 130 | } 131 | .content ul { 132 | line-height: 1.45em; 133 | } 134 | .content a { 135 | text-decoration: none; 136 | } 137 | .content a:hover { 138 | text-decoration: underline; 139 | } 140 | .content img, 141 | .content video { 142 | max-width: 100%; 143 | } 144 | .content .header:link, 145 | .content .header:visited { 146 | color: var(--fg); 147 | } 148 | .content .header:link, 149 | .content .header:visited:hover { 150 | text-decoration: none; 151 | } 152 | 153 | table { 154 | margin: 0 auto; 155 | border-collapse: collapse; 156 | } 157 | table td { 158 | padding: 3px 20px; 159 | border: 1px var(--table-border-color) solid; 160 | } 161 | table thead { 162 | background: var(--table-header-bg); 163 | } 164 | table thead td { 165 | font-weight: 700; 166 | border: none; 167 | } 168 | table thead th { 169 | padding: 3px 20px; 170 | } 171 | table thead tr { 172 | border: 1px var(--table-header-bg) solid; 173 | } 174 | /* Alternate background colors for rows */ 175 | table tbody tr:nth-child(2n) { 176 | background: var(--table-alternate-bg); 177 | } 178 | 179 | blockquote { 180 | margin: 20px 0; 181 | padding: 0 20px; 182 | color: var(--fg); 183 | background-color: var(--quote-bg); 184 | border-top: 0.1em solid var(--quote-border); 185 | border-bottom: 0.1em solid var(--quote-border); 186 | } 187 | 188 | kbd { 189 | background-color: var(--table-border-color); 190 | border-radius: 4px; 191 | border: solid 1px var(--theme-popup-border); 192 | box-shadow: inset 0 -1px 0 var(--theme-hover); 193 | display: inline-block; 194 | font-size: var(--code-font-size); 195 | font-family: var(--mono-font); 196 | line-height: 10px; 197 | padding: 4px 5px; 198 | vertical-align: middle; 199 | } 200 | 201 | :not(.footnote-definition) + .footnote-definition, 202 | .footnote-definition + :not(.footnote-definition) { 203 | margin-top: 2em; 204 | } 205 | .footnote-definition { 206 | font-size: 0.9em; 207 | margin: 0.5em 0; 208 | } 209 | .footnote-definition p { 210 | display: inline; 211 | } 212 | 213 | .tooltiptext { 214 | position: absolute; 215 | visibility: hidden; 216 | color: #fff; 217 | background-color: #333; 218 | transform: translateX( 219 | -50% 220 | ); /* Center by moving tooltip 50% of its width left */ 221 | left: -8px; /* Half of the width of the icon */ 222 | top: -35px; 223 | font-size: 0.8em; 224 | text-align: center; 225 | border-radius: 6px; 226 | padding: 5px 8px; 227 | margin: 5px; 228 | z-index: 1000; 229 | } 230 | .tooltipped .tooltiptext { 231 | visibility: visible; 232 | } 233 | 234 | .chapter li.part-title { 235 | color: var(--sidebar-fg); 236 | margin: 5px 0px; 237 | font-weight: bold; 238 | } 239 | 240 | .result-no-output { 241 | font-style: italic; 242 | } 243 | -------------------------------------------------------------------------------- /theme/css/language-picker.css: -------------------------------------------------------------------------------- 1 | #language-list { 2 | left: auto; 3 | right: 10px; 4 | } 5 | 6 | [dir="rtl"] #language-list { 7 | left: 10px; 8 | right: auto; 9 | } 10 | 11 | #language-list a { 12 | color: inherit; 13 | } 14 | -------------------------------------------------------------------------------- /theme/css/mdbook-admonish.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | :root { 3 | --md-admonition-icon--admonish-note: url("data:image/svg+xml;charset=utf-8,"); 4 | --md-admonition-icon--admonish-abstract: url("data:image/svg+xml;charset=utf-8,"); 5 | --md-admonition-icon--admonish-info: url("data:image/svg+xml;charset=utf-8,"); 6 | --md-admonition-icon--admonish-tip: url("data:image/svg+xml;charset=utf-8,"); 7 | --md-admonition-icon--admonish-success: url("data:image/svg+xml;charset=utf-8,"); 8 | --md-admonition-icon--admonish-question: url("data:image/svg+xml;charset=utf-8,"); 9 | --md-admonition-icon--admonish-warning: url("data:image/svg+xml;charset=utf-8,"); 10 | --md-admonition-icon--admonish-failure: url("data:image/svg+xml;charset=utf-8,"); 11 | --md-admonition-icon--admonish-danger: url("data:image/svg+xml;charset=utf-8,"); 12 | --md-admonition-icon--admonish-bug: url("data:image/svg+xml;charset=utf-8,"); 13 | --md-admonition-icon--admonish-example: url("data:image/svg+xml;charset=utf-8,"); 14 | --md-admonition-icon--admonish-quote: url("data:image/svg+xml;charset=utf-8,"); 15 | --md-details-icon: url("data:image/svg+xml;charset=utf-8,"); 16 | } 17 | 18 | :is(.admonition) { 19 | display: flow-root; 20 | margin: 1.5625em 0; 21 | padding: 0 1.2rem; 22 | color: var(--fg); 23 | page-break-inside: avoid; 24 | background-color: var(--bg); 25 | border: 0 solid black; 26 | border-inline-start-width: 0.4rem; 27 | border-radius: 0.2rem; 28 | box-shadow: 29 | 0 0.2rem 1rem rgba(0, 0, 0, 0.05), 30 | 0 0 0.1rem rgba(0, 0, 0, 0.1); 31 | } 32 | @media print { 33 | :is(.admonition) { 34 | box-shadow: none; 35 | } 36 | } 37 | :is(.admonition) > * { 38 | box-sizing: border-box; 39 | } 40 | :is(.admonition) :is(.admonition) { 41 | margin-top: 1em; 42 | margin-bottom: 1em; 43 | } 44 | :is(.admonition) > .tabbed-set:only-child { 45 | margin-top: 0; 46 | } 47 | html :is(.admonition) > :last-child { 48 | margin-bottom: 1.2rem; 49 | } 50 | 51 | a.admonition-anchor-link { 52 | display: none; 53 | position: absolute; 54 | left: -1.2rem; 55 | padding-right: 1rem; 56 | } 57 | a.admonition-anchor-link:link, 58 | a.admonition-anchor-link:visited { 59 | color: var(--fg); 60 | } 61 | a.admonition-anchor-link:link:hover, 62 | a.admonition-anchor-link:visited:hover { 63 | text-decoration: none; 64 | } 65 | a.admonition-anchor-link::before { 66 | content: "§"; 67 | } 68 | 69 | :is(.admonition-title, summary.admonition-title) { 70 | position: relative; 71 | min-height: 4rem; 72 | margin-block: 0; 73 | margin-inline: -1.6rem -1.2rem; 74 | padding-block: 0.8rem; 75 | padding-inline: 4.4rem 1.2rem; 76 | font-weight: 700; 77 | background-color: rgba(68, 138, 255, 0.1); 78 | display: flex; 79 | } 80 | :is(.admonition-title, summary.admonition-title) p { 81 | margin: 0; 82 | } 83 | html :is(.admonition-title, summary.admonition-title):last-child { 84 | margin-bottom: 0; 85 | } 86 | :is(.admonition-title, summary.admonition-title)::before { 87 | position: absolute; 88 | top: 0.625em; 89 | inset-inline-start: 1.6rem; 90 | width: 2rem; 91 | height: 2rem; 92 | background-color: #448aff; 93 | mask-image: url('data:image/svg+xml;charset=utf-8,'); 94 | -webkit-mask-image: url('data:image/svg+xml;charset=utf-8,'); 95 | mask-repeat: no-repeat; 96 | -webkit-mask-repeat: no-repeat; 97 | mask-size: contain; 98 | -webkit-mask-size: contain; 99 | content: ""; 100 | } 101 | :is(.admonition-title, summary.admonition-title):hover 102 | a.admonition-anchor-link { 103 | display: initial; 104 | } 105 | 106 | details.admonition > summary.admonition-title::after { 107 | position: absolute; 108 | top: 0.625em; 109 | inset-inline-end: 1.6rem; 110 | height: 2rem; 111 | width: 2rem; 112 | background-color: currentcolor; 113 | mask-image: var(--md-details-icon); 114 | -webkit-mask-image: var(--md-details-icon); 115 | mask-repeat: no-repeat; 116 | -webkit-mask-repeat: no-repeat; 117 | mask-size: contain; 118 | -webkit-mask-size: contain; 119 | content: ""; 120 | transform: rotate(0deg); 121 | transition: transform 0.25s; 122 | } 123 | details[open].admonition > summary.admonition-title::after { 124 | transform: rotate(90deg); 125 | } 126 | 127 | :is(.admonition):is(.admonish-note) { 128 | border-color: #448aff; 129 | } 130 | 131 | :is(.admonish-note) > :is(.admonition-title, summary.admonition-title) { 132 | background-color: rgba(68, 138, 255, 0.1); 133 | } 134 | :is(.admonish-note) > :is(.admonition-title, summary.admonition-title)::before { 135 | background-color: #448aff; 136 | mask-image: var(--md-admonition-icon--admonish-note); 137 | -webkit-mask-image: var(--md-admonition-icon--admonish-note); 138 | mask-repeat: no-repeat; 139 | -webkit-mask-repeat: no-repeat; 140 | mask-size: contain; 141 | -webkit-mask-repeat: no-repeat; 142 | } 143 | 144 | :is(.admonition):is(.admonish-abstract, .admonish-summary, .admonish-tldr) { 145 | border-color: #00b0ff; 146 | } 147 | 148 | :is(.admonish-abstract, .admonish-summary, .admonish-tldr) 149 | > :is(.admonition-title, summary.admonition-title) { 150 | background-color: rgba(0, 176, 255, 0.1); 151 | } 152 | :is(.admonish-abstract, .admonish-summary, .admonish-tldr) 153 | > :is(.admonition-title, summary.admonition-title)::before { 154 | background-color: #00b0ff; 155 | mask-image: var(--md-admonition-icon--admonish-abstract); 156 | -webkit-mask-image: var(--md-admonition-icon--admonish-abstract); 157 | mask-repeat: no-repeat; 158 | -webkit-mask-repeat: no-repeat; 159 | mask-size: contain; 160 | -webkit-mask-repeat: no-repeat; 161 | } 162 | 163 | :is(.admonition):is(.admonish-info, .admonish-todo) { 164 | border-color: #00b8d4; 165 | } 166 | 167 | :is(.admonish-info, .admonish-todo) 168 | > :is(.admonition-title, summary.admonition-title) { 169 | background-color: rgba(0, 184, 212, 0.1); 170 | } 171 | :is(.admonish-info, .admonish-todo) 172 | > :is(.admonition-title, summary.admonition-title)::before { 173 | background-color: #00b8d4; 174 | mask-image: var(--md-admonition-icon--admonish-info); 175 | -webkit-mask-image: var(--md-admonition-icon--admonish-info); 176 | mask-repeat: no-repeat; 177 | -webkit-mask-repeat: no-repeat; 178 | mask-size: contain; 179 | -webkit-mask-repeat: no-repeat; 180 | } 181 | 182 | :is(.admonition):is(.admonish-tip, .admonish-hint, .admonish-important) { 183 | border-color: #00bfa5; 184 | } 185 | 186 | :is(.admonish-tip, .admonish-hint, .admonish-important) 187 | > :is(.admonition-title, summary.admonition-title) { 188 | background-color: rgba(0, 191, 165, 0.1); 189 | } 190 | :is(.admonish-tip, .admonish-hint, .admonish-important) 191 | > :is(.admonition-title, summary.admonition-title)::before { 192 | background-color: #00bfa5; 193 | mask-image: var(--md-admonition-icon--admonish-tip); 194 | -webkit-mask-image: var(--md-admonition-icon--admonish-tip); 195 | mask-repeat: no-repeat; 196 | -webkit-mask-repeat: no-repeat; 197 | mask-size: contain; 198 | -webkit-mask-repeat: no-repeat; 199 | } 200 | 201 | :is(.admonition):is(.admonish-success, .admonish-check, .admonish-done) { 202 | border-color: #00c853; 203 | } 204 | 205 | :is(.admonish-success, .admonish-check, .admonish-done) 206 | > :is(.admonition-title, summary.admonition-title) { 207 | background-color: rgba(0, 200, 83, 0.1); 208 | } 209 | :is(.admonish-success, .admonish-check, .admonish-done) 210 | > :is(.admonition-title, summary.admonition-title)::before { 211 | background-color: #00c853; 212 | mask-image: var(--md-admonition-icon--admonish-success); 213 | -webkit-mask-image: var(--md-admonition-icon--admonish-success); 214 | mask-repeat: no-repeat; 215 | -webkit-mask-repeat: no-repeat; 216 | mask-size: contain; 217 | -webkit-mask-repeat: no-repeat; 218 | } 219 | 220 | :is(.admonition):is(.admonish-question, .admonish-help, .admonish-faq) { 221 | border-color: #64dd17; 222 | } 223 | 224 | :is(.admonish-question, .admonish-help, .admonish-faq) 225 | > :is(.admonition-title, summary.admonition-title) { 226 | background-color: rgba(100, 221, 23, 0.1); 227 | } 228 | :is(.admonish-question, .admonish-help, .admonish-faq) 229 | > :is(.admonition-title, summary.admonition-title)::before { 230 | background-color: #64dd17; 231 | mask-image: var(--md-admonition-icon--admonish-question); 232 | -webkit-mask-image: var(--md-admonition-icon--admonish-question); 233 | mask-repeat: no-repeat; 234 | -webkit-mask-repeat: no-repeat; 235 | mask-size: contain; 236 | -webkit-mask-repeat: no-repeat; 237 | } 238 | 239 | :is(.admonition):is(.admonish-warning, .admonish-caution, .admonish-attention) { 240 | border-color: #ff9100; 241 | } 242 | 243 | :is(.admonish-warning, .admonish-caution, .admonish-attention) 244 | > :is(.admonition-title, summary.admonition-title) { 245 | background-color: rgba(255, 145, 0, 0.1); 246 | } 247 | :is(.admonish-warning, .admonish-caution, .admonish-attention) 248 | > :is(.admonition-title, summary.admonition-title)::before { 249 | background-color: #ff9100; 250 | mask-image: var(--md-admonition-icon--admonish-warning); 251 | -webkit-mask-image: var(--md-admonition-icon--admonish-warning); 252 | mask-repeat: no-repeat; 253 | -webkit-mask-repeat: no-repeat; 254 | mask-size: contain; 255 | -webkit-mask-repeat: no-repeat; 256 | } 257 | 258 | :is(.admonition):is(.admonish-failure, .admonish-fail, .admonish-missing) { 259 | border-color: #ff5252; 260 | } 261 | 262 | :is(.admonish-failure, .admonish-fail, .admonish-missing) 263 | > :is(.admonition-title, summary.admonition-title) { 264 | background-color: rgba(255, 82, 82, 0.1); 265 | } 266 | :is(.admonish-failure, .admonish-fail, .admonish-missing) 267 | > :is(.admonition-title, summary.admonition-title)::before { 268 | background-color: #ff5252; 269 | mask-image: var(--md-admonition-icon--admonish-failure); 270 | -webkit-mask-image: var(--md-admonition-icon--admonish-failure); 271 | mask-repeat: no-repeat; 272 | -webkit-mask-repeat: no-repeat; 273 | mask-size: contain; 274 | -webkit-mask-repeat: no-repeat; 275 | } 276 | 277 | :is(.admonition):is(.admonish-danger, .admonish-error) { 278 | border-color: #ff1744; 279 | } 280 | 281 | :is(.admonish-danger, .admonish-error) 282 | > :is(.admonition-title, summary.admonition-title) { 283 | background-color: rgba(255, 23, 68, 0.1); 284 | } 285 | :is(.admonish-danger, .admonish-error) 286 | > :is(.admonition-title, summary.admonition-title)::before { 287 | background-color: #ff1744; 288 | mask-image: var(--md-admonition-icon--admonish-danger); 289 | -webkit-mask-image: var(--md-admonition-icon--admonish-danger); 290 | mask-repeat: no-repeat; 291 | -webkit-mask-repeat: no-repeat; 292 | mask-size: contain; 293 | -webkit-mask-repeat: no-repeat; 294 | } 295 | 296 | :is(.admonition):is(.admonish-bug) { 297 | border-color: #f50057; 298 | } 299 | 300 | :is(.admonish-bug) > :is(.admonition-title, summary.admonition-title) { 301 | background-color: rgba(245, 0, 87, 0.1); 302 | } 303 | :is(.admonish-bug) > :is(.admonition-title, summary.admonition-title)::before { 304 | background-color: #f50057; 305 | mask-image: var(--md-admonition-icon--admonish-bug); 306 | -webkit-mask-image: var(--md-admonition-icon--admonish-bug); 307 | mask-repeat: no-repeat; 308 | -webkit-mask-repeat: no-repeat; 309 | mask-size: contain; 310 | -webkit-mask-repeat: no-repeat; 311 | } 312 | 313 | :is(.admonition):is(.admonish-example) { 314 | border-color: #7c4dff; 315 | } 316 | 317 | :is(.admonish-example) > :is(.admonition-title, summary.admonition-title) { 318 | background-color: rgba(124, 77, 255, 0.1); 319 | } 320 | :is(.admonish-example) 321 | > :is(.admonition-title, summary.admonition-title)::before { 322 | background-color: #7c4dff; 323 | mask-image: var(--md-admonition-icon--admonish-example); 324 | -webkit-mask-image: var(--md-admonition-icon--admonish-example); 325 | mask-repeat: no-repeat; 326 | -webkit-mask-repeat: no-repeat; 327 | mask-size: contain; 328 | -webkit-mask-repeat: no-repeat; 329 | } 330 | 331 | :is(.admonition):is(.admonish-quote, .admonish-cite) { 332 | border-color: #9e9e9e; 333 | } 334 | 335 | :is(.admonish-quote, .admonish-cite) 336 | > :is(.admonition-title, summary.admonition-title) { 337 | background-color: rgba(158, 158, 158, 0.1); 338 | } 339 | :is(.admonish-quote, .admonish-cite) 340 | > :is(.admonition-title, summary.admonition-title)::before { 341 | background-color: #9e9e9e; 342 | mask-image: var(--md-admonition-icon--admonish-quote); 343 | -webkit-mask-image: var(--md-admonition-icon--admonish-quote); 344 | mask-repeat: no-repeat; 345 | -webkit-mask-repeat: no-repeat; 346 | mask-size: contain; 347 | -webkit-mask-repeat: no-repeat; 348 | } 349 | 350 | .navy :is(.admonition) { 351 | background-color: var(--sidebar-bg); 352 | } 353 | 354 | .ayu :is(.admonition), 355 | .coal :is(.admonition) { 356 | background-color: var(--theme-hover); 357 | } 358 | 359 | .rust :is(.admonition) { 360 | background-color: var(--sidebar-bg); 361 | color: var(--sidebar-fg); 362 | } 363 | .rust .admonition-anchor-link:link, 364 | .rust .admonition-anchor-link:visited { 365 | color: var(--sidebar-fg); 366 | } 367 | -------------------------------------------------------------------------------- /theme/css/print.css: -------------------------------------------------------------------------------- 1 | #sidebar, 2 | #menu-bar, 3 | .nav-chapters, 4 | .mobile-nav-chapters { 5 | display: none; 6 | } 7 | 8 | #page-wrapper.page-wrapper { 9 | transform: none; 10 | margin-left: 0px; 11 | overflow-y: initial; 12 | } 13 | 14 | #content { 15 | max-width: none; 16 | margin: 0; 17 | padding: 0; 18 | } 19 | 20 | .page { 21 | overflow-y: initial; 22 | } 23 | 24 | code { 25 | background-color: #666666; 26 | border-radius: 5px; 27 | 28 | /* Force background to be printed in Chrome */ 29 | -webkit-print-color-adjust: exact; 30 | } 31 | 32 | pre > .buttons { 33 | z-index: 2; 34 | } 35 | 36 | a, 37 | a:visited, 38 | a:active, 39 | a:hover { 40 | color: #4183c4; 41 | text-decoration: none; 42 | } 43 | 44 | h1, 45 | h2, 46 | h3, 47 | h4, 48 | h5, 49 | h6 { 50 | page-break-inside: avoid; 51 | page-break-after: avoid; 52 | } 53 | 54 | pre, 55 | code { 56 | page-break-inside: avoid; 57 | white-space: pre-wrap; 58 | } 59 | 60 | .fa { 61 | display: none !important; 62 | } 63 | -------------------------------------------------------------------------------- /theme/css/variables.css: -------------------------------------------------------------------------------- 1 | /* Globals */ 2 | 3 | :root { 4 | --sidebar-width: 300px; 5 | --page-padding: 15px; 6 | --content-max-width: 750px; 7 | --menu-bar-height: 50px; 8 | --mono-font: "Source Code Pro", Consolas, "Ubuntu Mono", Menlo, 9 | "DejaVu Sans Mono", monospace, monospace; 10 | --code-font-size: 0.875em 11 | /* please adjust the ace font size accordingly in editor.js */; 12 | } 13 | 14 | /* Themes */ 15 | 16 | .ayu { 17 | --bg: hsl(210, 25%, 8%); 18 | --fg: #c5c5c5; 19 | 20 | --sidebar-bg: #14191f; 21 | --sidebar-fg: #c8c9db; 22 | --sidebar-non-existant: #5c6773; 23 | --sidebar-active: #ffb454; 24 | --sidebar-spacer: #2d334f; 25 | 26 | --scrollbar: var(--sidebar-fg); 27 | 28 | --icons: #737480; 29 | --icons-hover: #b7b9cc; 30 | 31 | --links: #0096cf; 32 | 33 | --inline-code-color: #ffb454; 34 | 35 | --theme-popup-bg: #14191f; 36 | --theme-popup-border: #5c6773; 37 | --theme-hover: #191f26; 38 | 39 | --quote-bg: hsl(226, 15%, 17%); 40 | --quote-border: hsl(226, 15%, 22%); 41 | 42 | --table-border-color: hsl(210, 25%, 13%); 43 | --table-header-bg: hsl(210, 25%, 28%); 44 | --table-alternate-bg: hsl(210, 25%, 11%); 45 | 46 | --searchbar-border-color: #848484; 47 | --searchbar-bg: #424242; 48 | --searchbar-fg: #fff; 49 | --searchbar-shadow-color: #d4c89f; 50 | --searchresults-header-fg: #666; 51 | --searchresults-border-color: #888; 52 | --searchresults-li-bg: #252932; 53 | --search-mark-bg: #e3b171; 54 | } 55 | 56 | .coal { 57 | --bg: hsl(200, 7%, 8%); 58 | --fg: #98a3ad; 59 | 60 | --sidebar-bg: #292c2f; 61 | --sidebar-fg: #a1adb8; 62 | --sidebar-non-existant: #505254; 63 | --sidebar-active: #3473ad; 64 | --sidebar-spacer: #393939; 65 | 66 | --scrollbar: var(--sidebar-fg); 67 | 68 | --icons: #43484d; 69 | --icons-hover: #b3c0cc; 70 | 71 | --links: #2b79a2; 72 | 73 | --inline-code-color: #c5c8c6; 74 | 75 | --theme-popup-bg: #141617; 76 | --theme-popup-border: #43484d; 77 | --theme-hover: #1f2124; 78 | 79 | --quote-bg: hsl(234, 21%, 18%); 80 | --quote-border: hsl(234, 21%, 23%); 81 | 82 | --table-border-color: hsl(200, 7%, 13%); 83 | --table-header-bg: hsl(200, 7%, 28%); 84 | --table-alternate-bg: hsl(200, 7%, 11%); 85 | 86 | --searchbar-border-color: #aaa; 87 | --searchbar-bg: #b7b7b7; 88 | --searchbar-fg: #000; 89 | --searchbar-shadow-color: #aaa; 90 | --searchresults-header-fg: #666; 91 | --searchresults-border-color: #98a3ad; 92 | --searchresults-li-bg: #2b2b2f; 93 | --search-mark-bg: #355c7d; 94 | } 95 | 96 | .light { 97 | --bg: hsl(0, 0%, 100%); 98 | --fg: hsl(0, 0%, 0%); 99 | 100 | --sidebar-bg: #fafafa; 101 | --sidebar-fg: hsl(0, 0%, 0%); 102 | --sidebar-non-existant: #aaaaaa; 103 | --sidebar-active: #1f1fff; 104 | --sidebar-spacer: #f4f4f4; 105 | 106 | --scrollbar: #8f8f8f; 107 | 108 | --icons: #747474; 109 | --icons-hover: #000000; 110 | 111 | --links: #20609f; 112 | 113 | --inline-code-color: #301900; 114 | 115 | --theme-popup-bg: #fafafa; 116 | --theme-popup-border: #cccccc; 117 | --theme-hover: #e6e6e6; 118 | 119 | --quote-bg: hsl(197, 37%, 96%); 120 | --quote-border: hsl(197, 37%, 91%); 121 | 122 | --table-border-color: hsl(0, 0%, 95%); 123 | --table-header-bg: hsl(0, 0%, 80%); 124 | --table-alternate-bg: hsl(0, 0%, 97%); 125 | 126 | --searchbar-border-color: #aaa; 127 | --searchbar-bg: #fafafa; 128 | --searchbar-fg: #000; 129 | --searchbar-shadow-color: #aaa; 130 | --searchresults-header-fg: #666; 131 | --searchresults-border-color: #888; 132 | --searchresults-li-bg: #e4f2fe; 133 | --search-mark-bg: #a2cff5; 134 | } 135 | 136 | .navy { 137 | --bg: hsl(226, 23%, 11%); 138 | --fg: #bcbdd0; 139 | 140 | --sidebar-bg: #282d3f; 141 | --sidebar-fg: #c8c9db; 142 | --sidebar-non-existant: #505274; 143 | --sidebar-active: #2b79a2; 144 | --sidebar-spacer: #2d334f; 145 | 146 | --scrollbar: var(--sidebar-fg); 147 | 148 | --icons: #737480; 149 | --icons-hover: #b7b9cc; 150 | 151 | --links: #2b79a2; 152 | 153 | --inline-code-color: #c5c8c6; 154 | 155 | --theme-popup-bg: #161923; 156 | --theme-popup-border: #737480; 157 | --theme-hover: #282e40; 158 | 159 | --quote-bg: hsl(226, 15%, 17%); 160 | --quote-border: hsl(226, 15%, 22%); 161 | 162 | --table-border-color: hsl(226, 23%, 16%); 163 | --table-header-bg: hsl(226, 23%, 31%); 164 | --table-alternate-bg: hsl(226, 23%, 14%); 165 | 166 | --searchbar-border-color: #aaa; 167 | --searchbar-bg: #aeaec6; 168 | --searchbar-fg: #000; 169 | --searchbar-shadow-color: #aaa; 170 | --searchresults-header-fg: #5f5f71; 171 | --searchresults-border-color: #5c5c68; 172 | --searchresults-li-bg: #242430; 173 | --search-mark-bg: #a2cff5; 174 | } 175 | 176 | .rust { 177 | --bg: hsl(60, 9%, 87%); 178 | --fg: #262625; 179 | 180 | --sidebar-bg: #3b2e2a; 181 | --sidebar-fg: #c8c9db; 182 | --sidebar-non-existant: #505254; 183 | --sidebar-active: #e69f67; 184 | --sidebar-spacer: #45373a; 185 | 186 | --scrollbar: var(--sidebar-fg); 187 | 188 | --icons: #737480; 189 | --icons-hover: #262625; 190 | 191 | --links: #2b79a2; 192 | 193 | --inline-code-color: #6e6b5e; 194 | 195 | --theme-popup-bg: #e1e1db; 196 | --theme-popup-border: #b38f6b; 197 | --theme-hover: #99908a; 198 | 199 | --quote-bg: hsl(60, 5%, 75%); 200 | --quote-border: hsl(60, 5%, 70%); 201 | 202 | --table-border-color: hsl(60, 9%, 82%); 203 | --table-header-bg: #b3a497; 204 | --table-alternate-bg: hsl(60, 9%, 84%); 205 | 206 | --searchbar-border-color: #aaa; 207 | --searchbar-bg: #fafafa; 208 | --searchbar-fg: #000; 209 | --searchbar-shadow-color: #aaa; 210 | --searchresults-header-fg: #666; 211 | --searchresults-border-color: #888; 212 | --searchresults-li-bg: #dec2a2; 213 | --search-mark-bg: #e69f67; 214 | } 215 | 216 | @media (prefers-color-scheme: dark) { 217 | .light.no-js { 218 | --bg: hsl(200, 7%, 8%); 219 | --fg: #98a3ad; 220 | 221 | --sidebar-bg: #292c2f; 222 | --sidebar-fg: #a1adb8; 223 | --sidebar-non-existant: #505254; 224 | --sidebar-active: #3473ad; 225 | --sidebar-spacer: #393939; 226 | 227 | --scrollbar: var(--sidebar-fg); 228 | 229 | --icons: #43484d; 230 | --icons-hover: #b3c0cc; 231 | 232 | --links: #2b79a2; 233 | 234 | --inline-code-color: #c5c8c6; 235 | 236 | --theme-popup-bg: #141617; 237 | --theme-popup-border: #43484d; 238 | --theme-hover: #1f2124; 239 | 240 | --quote-bg: hsl(234, 21%, 18%); 241 | --quote-border: hsl(234, 21%, 23%); 242 | 243 | --table-border-color: hsl(200, 7%, 13%); 244 | --table-header-bg: hsl(200, 7%, 28%); 245 | --table-alternate-bg: hsl(200, 7%, 11%); 246 | 247 | --searchbar-border-color: #aaa; 248 | --searchbar-bg: #b7b7b7; 249 | --searchbar-fg: #000; 250 | --searchbar-shadow-color: #aaa; 251 | --searchresults-header-fg: #666; 252 | --searchresults-border-color: #98a3ad; 253 | --searchresults-li-bg: #2b2b2f; 254 | --search-mark-bg: #355c7d; 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /theme/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radxa-pkg/rsetup/ddc60c82399b088826ef15d9d57b562ab8806d25/theme/favicon.png -------------------------------------------------------------------------------- /theme/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /theme/fonts/OPEN-SANS-LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /theme/fonts/SOURCE-CODE-PRO-LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries. 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /theme/fonts/fonts.css: -------------------------------------------------------------------------------- 1 | /* Open Sans is licensed under the Apache License, Version 2.0. See http://www.apache.org/licenses/LICENSE-2.0 */ 2 | /* Source Code Pro is under the Open Font License. See https://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL */ 3 | 4 | /* open-sans-300 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ 5 | @font-face { 6 | font-family: "Open Sans"; 7 | font-style: normal; 8 | font-weight: 300; 9 | src: 10 | local("Open Sans Light"), 11 | local("OpenSans-Light"), 12 | url("open-sans-v17-all-charsets-300.woff2") format("woff2"); 13 | } 14 | 15 | /* open-sans-300italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ 16 | @font-face { 17 | font-family: "Open Sans"; 18 | font-style: italic; 19 | font-weight: 300; 20 | src: 21 | local("Open Sans Light Italic"), 22 | local("OpenSans-LightItalic"), 23 | url("open-sans-v17-all-charsets-300italic.woff2") format("woff2"); 24 | } 25 | 26 | /* open-sans-regular - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ 27 | @font-face { 28 | font-family: "Open Sans"; 29 | font-style: normal; 30 | font-weight: 400; 31 | src: 32 | local("Open Sans Regular"), 33 | local("OpenSans-Regular"), 34 | url("open-sans-v17-all-charsets-regular.woff2") format("woff2"); 35 | } 36 | 37 | /* open-sans-italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ 38 | @font-face { 39 | font-family: "Open Sans"; 40 | font-style: italic; 41 | font-weight: 400; 42 | src: 43 | local("Open Sans Italic"), 44 | local("OpenSans-Italic"), 45 | url("open-sans-v17-all-charsets-italic.woff2") format("woff2"); 46 | } 47 | 48 | /* open-sans-600 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ 49 | @font-face { 50 | font-family: "Open Sans"; 51 | font-style: normal; 52 | font-weight: 600; 53 | src: 54 | local("Open Sans SemiBold"), 55 | local("OpenSans-SemiBold"), 56 | url("open-sans-v17-all-charsets-600.woff2") format("woff2"); 57 | } 58 | 59 | /* open-sans-600italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ 60 | @font-face { 61 | font-family: "Open Sans"; 62 | font-style: italic; 63 | font-weight: 600; 64 | src: 65 | local("Open Sans SemiBold Italic"), 66 | local("OpenSans-SemiBoldItalic"), 67 | url("open-sans-v17-all-charsets-600italic.woff2") format("woff2"); 68 | } 69 | 70 | /* open-sans-700 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ 71 | @font-face { 72 | font-family: "Open Sans"; 73 | font-style: normal; 74 | font-weight: 700; 75 | src: 76 | local("Open Sans Bold"), 77 | local("OpenSans-Bold"), 78 | url("open-sans-v17-all-charsets-700.woff2") format("woff2"); 79 | } 80 | 81 | /* open-sans-700italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ 82 | @font-face { 83 | font-family: "Open Sans"; 84 | font-style: italic; 85 | font-weight: 700; 86 | src: 87 | local("Open Sans Bold Italic"), 88 | local("OpenSans-BoldItalic"), 89 | url("open-sans-v17-all-charsets-700italic.woff2") format("woff2"); 90 | } 91 | 92 | /* open-sans-800 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ 93 | @font-face { 94 | font-family: "Open Sans"; 95 | font-style: normal; 96 | font-weight: 800; 97 | src: 98 | local("Open Sans ExtraBold"), 99 | local("OpenSans-ExtraBold"), 100 | url("open-sans-v17-all-charsets-800.woff2") format("woff2"); 101 | } 102 | 103 | /* open-sans-800italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ 104 | @font-face { 105 | font-family: "Open Sans"; 106 | font-style: italic; 107 | font-weight: 800; 108 | src: 109 | local("Open Sans ExtraBold Italic"), 110 | local("OpenSans-ExtraBoldItalic"), 111 | url("open-sans-v17-all-charsets-800italic.woff2") format("woff2"); 112 | } 113 | 114 | /* source-code-pro-500 - latin_vietnamese_latin-ext_greek_cyrillic-ext_cyrillic */ 115 | @font-face { 116 | font-family: "Source Code Pro"; 117 | font-style: normal; 118 | font-weight: 500; 119 | src: url("source-code-pro-v11-all-charsets-500.woff2") format("woff2"); 120 | } 121 | -------------------------------------------------------------------------------- /theme/fonts/open-sans-v17-all-charsets-300.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radxa-pkg/rsetup/ddc60c82399b088826ef15d9d57b562ab8806d25/theme/fonts/open-sans-v17-all-charsets-300.woff2 -------------------------------------------------------------------------------- /theme/fonts/open-sans-v17-all-charsets-300italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radxa-pkg/rsetup/ddc60c82399b088826ef15d9d57b562ab8806d25/theme/fonts/open-sans-v17-all-charsets-300italic.woff2 -------------------------------------------------------------------------------- /theme/fonts/open-sans-v17-all-charsets-600.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radxa-pkg/rsetup/ddc60c82399b088826ef15d9d57b562ab8806d25/theme/fonts/open-sans-v17-all-charsets-600.woff2 -------------------------------------------------------------------------------- /theme/fonts/open-sans-v17-all-charsets-600italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radxa-pkg/rsetup/ddc60c82399b088826ef15d9d57b562ab8806d25/theme/fonts/open-sans-v17-all-charsets-600italic.woff2 -------------------------------------------------------------------------------- /theme/fonts/open-sans-v17-all-charsets-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radxa-pkg/rsetup/ddc60c82399b088826ef15d9d57b562ab8806d25/theme/fonts/open-sans-v17-all-charsets-700.woff2 -------------------------------------------------------------------------------- /theme/fonts/open-sans-v17-all-charsets-700italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radxa-pkg/rsetup/ddc60c82399b088826ef15d9d57b562ab8806d25/theme/fonts/open-sans-v17-all-charsets-700italic.woff2 -------------------------------------------------------------------------------- /theme/fonts/open-sans-v17-all-charsets-800.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radxa-pkg/rsetup/ddc60c82399b088826ef15d9d57b562ab8806d25/theme/fonts/open-sans-v17-all-charsets-800.woff2 -------------------------------------------------------------------------------- /theme/fonts/open-sans-v17-all-charsets-800italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radxa-pkg/rsetup/ddc60c82399b088826ef15d9d57b562ab8806d25/theme/fonts/open-sans-v17-all-charsets-800italic.woff2 -------------------------------------------------------------------------------- /theme/fonts/open-sans-v17-all-charsets-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radxa-pkg/rsetup/ddc60c82399b088826ef15d9d57b562ab8806d25/theme/fonts/open-sans-v17-all-charsets-italic.woff2 -------------------------------------------------------------------------------- /theme/fonts/open-sans-v17-all-charsets-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radxa-pkg/rsetup/ddc60c82399b088826ef15d9d57b562ab8806d25/theme/fonts/open-sans-v17-all-charsets-regular.woff2 -------------------------------------------------------------------------------- /theme/fonts/source-code-pro-v11-all-charsets-500.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radxa-pkg/rsetup/ddc60c82399b088826ef15d9d57b562ab8806d25/theme/fonts/source-code-pro-v11-all-charsets-500.woff2 -------------------------------------------------------------------------------- /theme/highlight.css: -------------------------------------------------------------------------------- 1 | /* 2 | * An increased contrast highlighting scheme loosely based on the 3 | * "Base16 Atelier Dune Light" theme by Bram de Haan 4 | * (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/dune) 5 | * Original Base16 color scheme by Chris Kempson 6 | * (https://github.com/chriskempson/base16) 7 | */ 8 | 9 | /* Comment */ 10 | .hljs-comment, 11 | .hljs-quote { 12 | color: #575757; 13 | } 14 | 15 | /* Red */ 16 | .hljs-variable, 17 | .hljs-template-variable, 18 | .hljs-attribute, 19 | .hljs-tag, 20 | .hljs-name, 21 | .hljs-regexp, 22 | .hljs-link, 23 | .hljs-name, 24 | .hljs-selector-id, 25 | .hljs-selector-class { 26 | color: #d70025; 27 | } 28 | 29 | /* Orange */ 30 | .hljs-number, 31 | .hljs-meta, 32 | .hljs-built_in, 33 | .hljs-builtin-name, 34 | .hljs-literal, 35 | .hljs-type, 36 | .hljs-params { 37 | color: #b21e00; 38 | } 39 | 40 | /* Green */ 41 | .hljs-string, 42 | .hljs-symbol, 43 | .hljs-bullet { 44 | color: #008200; 45 | } 46 | 47 | /* Blue */ 48 | .hljs-title, 49 | .hljs-section { 50 | color: #0030f2; 51 | } 52 | 53 | /* Purple */ 54 | .hljs-keyword, 55 | .hljs-selector-tag { 56 | color: #9d00ec; 57 | } 58 | 59 | .hljs { 60 | display: block; 61 | overflow-x: auto; 62 | background: #f6f7f6; 63 | color: #000; 64 | } 65 | 66 | .hljs-emphasis { 67 | font-style: italic; 68 | } 69 | 70 | .hljs-strong { 71 | font-weight: bold; 72 | } 73 | 74 | .hljs-addition { 75 | color: #22863a; 76 | background-color: #f0fff4; 77 | } 78 | 79 | .hljs-deletion { 80 | color: #b31d28; 81 | background-color: #ffeef0; 82 | } 83 | --------------------------------------------------------------------------------