├── test ├── emoji │ ├── zwj.txt │ ├── copyright.txt │ ├── warning.txt │ ├── eye-in-bubble.txt │ ├── random.txt │ ├── text-presentation.txt │ ├── zwj-family.txt │ ├── test-emoji.txt │ ├── in-wintitle.sh │ ├── with-bgcolor.sh │ ├── worldmap.sh │ ├── and-tabstops.sh │ ├── facepalm.sh │ └── wchar-test.sh ├── font-fallback.txt ├── font-fallback.sh ├── KAM.sh ├── copy-to-clipboard.sh ├── lf-test.sh ├── fill-screen.sh ├── DECSTBM.sh ├── colors.sh ├── dimmed-bold-bright-text.sh ├── origin-mode-and-margins.sh ├── box-drawings.sh ├── synchronized-output.sh ├── scroll-up-with-margins.sh ├── DECIC.sh ├── batched-rendering.sh ├── DECDC.sh ├── DECSLRM.sh ├── colors-256.sh └── decoration.sh ├── src ├── vtbackend │ ├── InputBinding.cpp │ ├── MatchModes.cpp │ ├── MockTerm.cpp │ ├── cell │ │ ├── CellConfig.h │ │ └── CompactCell.cpp │ ├── test_main.cpp │ ├── InputHandler.h │ ├── Cursor.h │ ├── logging.h │ ├── Color_test.cpp │ ├── Capabilities_test.cpp │ ├── primitives.cpp │ ├── RenderBuffer.cpp │ ├── JumpHistory.h │ ├── Metrics.h │ ├── GraphicsAttributes.h │ ├── JumpHistory.cpp │ ├── Functions.cpp │ ├── InputBinding.h │ └── Sequence.cpp ├── contour │ ├── contour.rc │ ├── res │ │ ├── bell.oga │ │ ├── bell.wav │ │ └── images │ │ │ ├── contour-logo.ico │ │ │ ├── contour-logo.png │ │ │ ├── contour-logo-16.png │ │ │ ├── contour-logo-32.png │ │ │ ├── contour-logo-64.png │ │ │ ├── contour-logo.icns │ │ │ ├── contour-logo-128.png │ │ │ ├── contour-logo-256.png │ │ │ └── contour-logo-512.png │ ├── shell-integration │ │ ├── shell-integration.tcsh │ │ ├── shell-integration.zsh │ │ └── shell-integration.fish │ ├── display │ │ ├── shaders │ │ │ ├── simple.vert │ │ │ ├── background.frag │ │ │ ├── background.vert │ │ │ ├── text.vert │ │ │ ├── background_image.vert │ │ │ ├── dual_kawase_down.frag │ │ │ ├── dual_kawase_up.frag │ │ │ ├── background_image.frag │ │ │ └── blur_gaussian.frag │ │ ├── DisplayResources.qrc │ │ ├── Vertex.h │ │ ├── ShaderConfig.h │ │ └── CMakeLists.txt │ ├── BlurBehind.h │ ├── org.contourterminal.Contour.OpenHere.desktop │ ├── CaptureScreen.h │ ├── ui.template │ │ └── RequestPermission.qml.in │ ├── resources.qrc │ ├── ContourApp.h │ └── Audio.h ├── crispy │ ├── BufferObject_test.cpp │ ├── BufferObject.cpp │ ├── overloaded.h │ ├── test_main.cpp │ ├── .clang-tidy │ ├── Comparison.h │ ├── base64_test.cpp │ ├── Size_test.cpp │ ├── logstore.cpp │ ├── reference.h │ ├── compose_test.cpp │ ├── deferred.h │ ├── times_test.cpp │ ├── interpolated_string.h │ ├── StackTrace.h │ ├── sort_test.cpp │ ├── defines.h │ ├── utils.cpp │ ├── range.h │ ├── TrieMap_test.cpp │ ├── compose.h │ ├── interpolated_string_test.cpp │ ├── Owned.h │ ├── App.h │ ├── sort.h │ ├── point.h │ └── algorithm.h ├── text_shaper │ ├── .clang-tidy │ ├── README.md │ ├── font_locator_provider.h │ ├── coretext_locator.h │ ├── directwrite_locator.h │ ├── mock_font_locator.h │ ├── fontconfig_locator.h │ ├── font.cpp │ ├── font_locator_provider.cpp │ ├── directwrite_shaper.h │ ├── open_shaper.h │ └── mock_font_locator.cpp ├── CMakeLists.txt ├── vtpty │ ├── Pty.cpp │ ├── UnixUtils.cpp │ ├── ConPty.h │ ├── MockViewPty.h │ ├── PageSize.h │ ├── MockPty.cpp │ ├── MockViewPty.cpp │ └── MockPty.h ├── vtrasterizer │ ├── shared_defines.h │ ├── utils.h │ ├── BackgroundRenderer.h │ ├── CursorRenderer.h │ ├── CMakeLists.txt │ └── BoxDrawingRenderer.h └── vtparser │ ├── CMakeLists.txt │ ├── ParserExtension.h │ └── Parser_test.cpp ├── docs ├── CNAME ├── demo │ ├── ime │ │ ├── demo_ime.mp4 │ │ └── pinyin_ime.mp4 │ ├── size_indicator.md │ ├── font-ligatures.md │ ├── images.md │ ├── input-modes.md │ ├── ime.md │ ├── statusline.md │ └── line-marks.md ├── assets │ └── contour-logo.png ├── requirements.txt ├── videos │ ├── size-indicator.mp4 │ └── contour-normal-mode-select-and-yank.webm ├── screenshots │ ├── contour-lsix.png │ ├── size-indicator.png │ ├── contour-sixel-plot.png │ ├── contour-sixel-images.png │ ├── contour-font-ligatures.png │ ├── contour-notcurses-ncneofetch.png │ ├── contour-julia-repl-sixel-image.png │ ├── contour-screenshots-0.1.0-pre2.png │ └── contour-win32-acrylic-background.png ├── vt-extensions │ ├── index.md │ ├── font-settings.md │ ├── clickable-links.md │ ├── unicode-core.md │ ├── line-reflow-mode.md │ ├── save-and-restore-sgr-attributes.md │ ├── buffer-capture.md │ └── vertical-line-marks.md ├── stylesheets │ └── extra.css ├── configuration │ └── advanced │ │ ├── images.md │ │ ├── mouse.md │ │ ├── renderer.md │ │ └── misc.md ├── drafts │ ├── daemon-mode.md │ └── terminal-emulators.md └── internals │ └── CODING_STYLE.md ├── cmake ├── cincludeme │ └── CMakeLists.txt ├── ClangTidy.cmake ├── CIncludeMe.cmake ├── EnableCcache.cmake ├── presets │ ├── common.json │ └── os-macos.json ├── Modules │ └── MacOSXBundleInfo.plist.in └── FindPackageMessage.cmake ├── scripts ├── test.gdb ├── install-git-hooks.sh ├── check-includes.sh ├── mkdebchangelog.sh ├── check-pr-todos.sh ├── mkchangelogmd.sh ├── git-hooks │ └── pre-commit.git-clang-format.sh ├── check-common.sh ├── ci-prepare-contour.sh ├── ci-set-vars.ps1 ├── ci │ └── Xvfb-contour-run.sh ├── ci-install-run-deps.sh ├── check-release.sh └── ci-set-vars.sh ├── .github ├── ISSUE_TEMPLATE │ ├── general.md │ └── feature_request.md ├── actions │ └── spelling │ │ └── allow │ │ ├── allow.txt │ │ ├── names.txt │ │ ├── api-terms.txt │ │ ├── patterns.txt │ │ ├── qt-terms.txt │ │ └── terminal-terms.txt ├── FUNDING.yml ├── workflows │ ├── labeler.yml │ ├── changelog.yml │ ├── codeql-analysis.yml.disabled │ └── docs.yml ├── ci-ppa │ └── dput.cf ├── codechecker │ ├── skipfile.txt │ └── config.json ├── fontconfig-memleak-fix.patch ├── CODEOWNERS ├── mock-font-locator.yml ├── labeler.yml ├── static │ ├── DockerUbuntu │ └── readme ├── fedora │ └── Dockerfile ├── pull_request_template.md └── appimage │ └── Dockerfile ├── examples ├── CMakeLists.txt └── vt-set-profile ├── .editorconfig ├── CMakePresets.json ├── vcpkg-configuration.json ├── .ecrc ├── vcpkg.json ├── .spellcheck.yml ├── .codecov.yml ├── SECURITY.md ├── .gitignore └── autogen.sh /test/emoji/zwj.txt: -------------------------------------------------------------------------------- 1 | 👨‍🦰 2 | -------------------------------------------------------------------------------- /src/vtbackend/InputBinding.cpp: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/emoji/copyright.txt: -------------------------------------------------------------------------------- 1 | © 2 | -------------------------------------------------------------------------------- /test/emoji/warning.txt: -------------------------------------------------------------------------------- 1 | ⚠️X 2 | -------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | www.contour-terminal.org 2 | -------------------------------------------------------------------------------- /test/emoji/eye-in-bubble.txt: -------------------------------------------------------------------------------- 1 | 👁️‍🗨️ABC 2 | -------------------------------------------------------------------------------- /test/emoji/random.txt: -------------------------------------------------------------------------------- 1 | 🌈 💝 😛 2 | 👪 3 | -------------------------------------------------------------------------------- /test/emoji/text-presentation.txt: -------------------------------------------------------------------------------- 1 | ✌︎ 2 | -------------------------------------------------------------------------------- /test/emoji/zwj-family.txt: -------------------------------------------------------------------------------- 1 | 👨‍👩‍👧‍👦 2 | -------------------------------------------------------------------------------- /test/emoji/test-emoji.txt: -------------------------------------------------------------------------------- 1 | hello 🐧 world 👨‍🦰! 2 | -------------------------------------------------------------------------------- /test/font-fallback.txt: -------------------------------------------------------------------------------- 1 |  2 | 🅝 3 |  4 | ⌨ 5 | ئ 6 | Х 7 | ㎦ 8 | ㆇ 9 | -------------------------------------------------------------------------------- /cmake/cincludeme/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(cincludeme cincludeme.cpp) 2 | -------------------------------------------------------------------------------- /src/contour/contour.rc: -------------------------------------------------------------------------------- 1 | IDI_ICON1 ICON "res/images/contour-logo.ico" 2 | -------------------------------------------------------------------------------- /test/font-fallback.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | cat $(dirname $0)/font-fallback.txt 4 | read 5 | -------------------------------------------------------------------------------- /src/contour/res/bell.oga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/contour/master/src/contour/res/bell.oga -------------------------------------------------------------------------------- /src/contour/res/bell.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/contour/master/src/contour/res/bell.wav -------------------------------------------------------------------------------- /docs/demo/ime/demo_ime.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/contour/master/docs/demo/ime/demo_ime.mp4 -------------------------------------------------------------------------------- /docs/assets/contour-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/contour/master/docs/assets/contour-logo.png -------------------------------------------------------------------------------- /docs/demo/ime/pinyin_ime.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/contour/master/docs/demo/ime/pinyin_ime.mp4 -------------------------------------------------------------------------------- /test/emoji/in-wintitle.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | echo -ne "\033]2;emoji title 😀 here\x07" 4 | sleep 10 5 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | mkdocs-material 2 | mkdocs-exclude 3 | mkdocs-glightbox 4 | mkdocs-video 5 | pygments 6 | -------------------------------------------------------------------------------- /docs/videos/size-indicator.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/contour/master/docs/videos/size-indicator.mp4 -------------------------------------------------------------------------------- /docs/screenshots/contour-lsix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/contour/master/docs/screenshots/contour-lsix.png -------------------------------------------------------------------------------- /test/emoji/with-bgcolor.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | echo -ne "-\e[44;37m⚠️AB\e[m\n" 3 | echo -ne "-\e[44;37m❗️AB\e[m\n" 4 | -------------------------------------------------------------------------------- /docs/screenshots/size-indicator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/contour/master/docs/screenshots/size-indicator.png -------------------------------------------------------------------------------- /scripts/test.gdb: -------------------------------------------------------------------------------- 1 | set width 0 2 | set height 0 3 | set verbose off 4 | catch throw 5 | run 6 | bt 7 | bt full 8 | exit 9 | -------------------------------------------------------------------------------- /docs/screenshots/contour-sixel-plot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/contour/master/docs/screenshots/contour-sixel-plot.png -------------------------------------------------------------------------------- /src/contour/res/images/contour-logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/contour/master/src/contour/res/images/contour-logo.ico -------------------------------------------------------------------------------- /src/contour/res/images/contour-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/contour/master/src/contour/res/images/contour-logo.png -------------------------------------------------------------------------------- /docs/screenshots/contour-sixel-images.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/contour/master/docs/screenshots/contour-sixel-images.png -------------------------------------------------------------------------------- /src/contour/res/images/contour-logo-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/contour/master/src/contour/res/images/contour-logo-16.png -------------------------------------------------------------------------------- /src/contour/res/images/contour-logo-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/contour/master/src/contour/res/images/contour-logo-32.png -------------------------------------------------------------------------------- /src/contour/res/images/contour-logo-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/contour/master/src/contour/res/images/contour-logo-64.png -------------------------------------------------------------------------------- /src/contour/res/images/contour-logo.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/contour/master/src/contour/res/images/contour-logo.icns -------------------------------------------------------------------------------- /test/KAM.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | echo -ne "\033[2h" 4 | 5 | echo "Write something ..." 6 | sleep 5 7 | 8 | echo -ne "\033[2l" 9 | -------------------------------------------------------------------------------- /test/emoji/worldmap.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | printf "\U0001F5FAx\n" 3 | printf "\U0001F5FA\uFE0Ex\n" 4 | printf "\U0001F5FA\uFE0Fx\n" 5 | -------------------------------------------------------------------------------- /docs/screenshots/contour-font-ligatures.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/contour/master/docs/screenshots/contour-font-ligatures.png -------------------------------------------------------------------------------- /src/contour/res/images/contour-logo-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/contour/master/src/contour/res/images/contour-logo-128.png -------------------------------------------------------------------------------- /src/contour/res/images/contour-logo-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/contour/master/src/contour/res/images/contour-logo-256.png -------------------------------------------------------------------------------- /src/contour/res/images/contour-logo-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/contour/master/src/contour/res/images/contour-logo-512.png -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/general.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: General feedback 3 | about: General feedback about the Contour Terminal Emulator 4 | --- 5 | -------------------------------------------------------------------------------- /.github/actions/spelling/allow/allow.txt: -------------------------------------------------------------------------------- 1 | FreeBSD 2 | OpenBSD 3 | UpperCamelCase 4 | copy'n'paste 5 | lowerCamelCase 6 | macOS 7 | zypper 8 | -------------------------------------------------------------------------------- /docs/screenshots/contour-notcurses-ncneofetch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/contour/master/docs/screenshots/contour-notcurses-ncneofetch.png -------------------------------------------------------------------------------- /test/emoji/and-tabstops.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | echo -ne "1234567890\n" 3 | echo -ne "-\e[44;37m⚠️A\tB\e[m\n" 4 | echo -ne "-\e[44;37m❗️A\tB\e[m\n" 5 | -------------------------------------------------------------------------------- /docs/screenshots/contour-julia-repl-sixel-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/contour/master/docs/screenshots/contour-julia-repl-sixel-image.png -------------------------------------------------------------------------------- /docs/screenshots/contour-screenshots-0.1.0-pre2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/contour/master/docs/screenshots/contour-screenshots-0.1.0-pre2.png -------------------------------------------------------------------------------- /docs/videos/contour-normal-mode-select-and-yank.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/contour/master/docs/videos/contour-normal-mode-select-and-yank.webm -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: ['christianparpart'] 4 | custom: ['https://paypal.me/ChristianParpart'] 5 | -------------------------------------------------------------------------------- /docs/screenshots/contour-win32-acrylic-background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/contour/master/docs/screenshots/contour-win32-acrylic-background.png -------------------------------------------------------------------------------- /src/contour/shell-integration/shell-integration.tcsh: -------------------------------------------------------------------------------- 1 | alias precmd 'echo -n "\\e[?2028l\\e[>M\\e]7;$PWD\\e\\\\";' 2 | alias postcmd 'echo -n "\\e[?2028h";' 3 | -------------------------------------------------------------------------------- /docs/demo/size_indicator.md: -------------------------------------------------------------------------------- 1 | # Size indicator 2 | 3 | On resize window with current terminal size will appear 4 | 5 | ![type:video](../videos/size-indicator.mp4) 6 | -------------------------------------------------------------------------------- /docs/vt-extensions/index.md: -------------------------------------------------------------------------------- 1 | # VT extensions 2 | 3 | Contour supports some new or adopted non-standard VT extensions, which will be listed in the sub-sections here. 4 | -------------------------------------------------------------------------------- /src/contour/display/shaders/simple.vert: -------------------------------------------------------------------------------- 1 | layout(location = 0) in highp vec3 position; 2 | 3 | void main() 4 | { 5 | gl_Position = vec4(position, 1.0); 6 | } 7 | -------------------------------------------------------------------------------- /src/vtbackend/MatchModes.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace vtbackend 5 | { 6 | 7 | } // namespace vtbackend 8 | -------------------------------------------------------------------------------- /test/copy-to-clipboard.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # Copies stdin to operating-system clipboard. 3 | 4 | contents=`cat | base64` 5 | exec echo -ne "\033]52;c;$contents\033\\" 6 | -------------------------------------------------------------------------------- /.github/actions/spelling/allow/names.txt: -------------------------------------------------------------------------------- 1 | AYNSTAYN 2 | Caloras 3 | Dicharry 4 | Vladim 5 | Vladimir 6 | christianparpart 7 | dankamongmen 8 | parpart 9 | whisperity 10 | yaraslaut 11 | -------------------------------------------------------------------------------- /scripts/install-git-hooks.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | set -e 4 | 5 | PROJECT_ROOT="$(dirname $0)/.." 6 | 7 | cp -vrip "${PROJECT_ROOT}/scripts/git-hooks" \ 8 | "${PROJECT_ROOT}/.git/hooks" 9 | -------------------------------------------------------------------------------- /test/lf-test.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | clear 4 | 5 | echo -ne "\033[20l" 6 | 7 | echo -ne "\033[5;5H" 8 | for i in `seq 1 5`; do 9 | echo -ne "${i}\n" 10 | done 11 | 12 | echo -ne "\033[20h" 13 | -------------------------------------------------------------------------------- /src/contour/display/shaders/background.frag: -------------------------------------------------------------------------------- 1 | in highp vec4 fs_textColor; 2 | out highp vec4 outColor; 3 | 4 | uniform highp float u_time; 5 | 6 | void main() 7 | { 8 | outColor = fs_textColor; 9 | } 10 | -------------------------------------------------------------------------------- /docs/demo/font-ligatures.md: -------------------------------------------------------------------------------- 1 | # Font Ligatures 2 | 3 | Contour renders your font ligatures out of the box. 4 | If you font supports them, Contour will render them. 5 | 6 | ![demo](/screenshots/contour-font-ligatures.png){ align=left } 7 | -------------------------------------------------------------------------------- /test/fill-screen.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | COUNT=30 4 | for i in `seq 1 ${COUNT}`; do 5 | echo -ne "$i ..." 6 | if [[ $i -ne ${COUNT} ]]; then 7 | echo 8 | else 9 | echo -n " > " 10 | fi 11 | done 12 | -------------------------------------------------------------------------------- /docs/stylesheets/extra.css: -------------------------------------------------------------------------------- 1 | .check-mark { 2 | color: #116111; 3 | } 4 | .arrow-right-bold { 5 | color: #1FFFA2; 6 | } 7 | 8 | .md-typeset table:not([class]) th, 9 | .md-typeset table:not([class]) td { 10 | padding: 5px 5px; 11 | } 12 | -------------------------------------------------------------------------------- /src/crispy/BufferObject_test.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #include 3 | 4 | #include 5 | 6 | TEST_CASE("buffer_object", "[buffer_object]") 7 | { 8 | // TODO 9 | } 10 | -------------------------------------------------------------------------------- /test/DECSTBM.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | clear 4 | echo -ne "\033[5;10r" 5 | for i in `seq 1 15`; do 6 | echo "${i}: Hello, World" 7 | sleep 1 8 | done 9 | sleep 5 10 | echo -ne "\033[r" 11 | echo "ENDING..." 12 | echo -ne "\033[10;1H" 13 | -------------------------------------------------------------------------------- /src/contour/BlurBehind.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #pragma once 3 | 4 | #include 5 | 6 | namespace BlurBehind 7 | { 8 | 9 | void setEnabled(QWindow* window, bool enabled, QRegion const& region = QRegion()); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /test/colors.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | for i in `seq 0 7`; do 4 | for ground in 3 4; do 5 | for b in "" "1;"; do # bold, non-bold 6 | echo -ne "\033[${b}${ground}${i}m HELLO \033[m " 7 | done 8 | done 9 | echo -ne "\n" 10 | done 11 | -------------------------------------------------------------------------------- /.github/workflows/labeler.yml: -------------------------------------------------------------------------------- 1 | name: "PR Labeler" 2 | 3 | on: 4 | - pull_request_target 5 | 6 | jobs: 7 | triage: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/labeler@v4 11 | with: 12 | repo-token: "${{ secrets.GITHUB_TOKEN }}" 13 | -------------------------------------------------------------------------------- /test/dimmed-bold-bright-text.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | DIMMED="DIMMED █" 4 | NORMAL="█ NORMAL █" 5 | BOLD_OR_BRIGHT="█ BOLD/BRIGHT" 6 | 7 | for i in {0..8} ; do 8 | printf "${i}: \e[0;2;3${i}m${DIMMED}\e[0;3${i}m${NORMAL}\e[0;1;3${i}m${BOLD_OR_BRIGHT}\e[0m\n" 9 | done 10 | -------------------------------------------------------------------------------- /src/vtbackend/MockTerm.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | 9 | namespace vtbackend 10 | { 11 | 12 | } // namespace vtbackend 13 | -------------------------------------------------------------------------------- /.github/ci-ppa/dput.cf: -------------------------------------------------------------------------------- 1 | 2 | [contour] 3 | fqdn = ppa.launchpad.net 4 | method = ftp 5 | incoming = ~christianparpart/contour 6 | login = anonymous 7 | 8 | [contour-dev] 9 | fqdn = ppa.launchpad.net 10 | method = ftp 11 | incoming = ~christianparpart/contour-dev 12 | login = anonymous 13 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if(LIBTERMINAL_TESTING) 2 | if(UNIX) 3 | add_executable(watch-mouse-events watch-mouse-events.cpp) 4 | target_link_libraries(watch-mouse-events vtbackend) 5 | 6 | add_executable(detect-dark-light-mode detect-dark-light-mode.cpp) 7 | endif() 8 | endif() 9 | -------------------------------------------------------------------------------- /examples/vt-set-profile: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # [EXPERIMENTAL FEATURE] 4 | # Sets the config profile via VT DCS sequence. 5 | # 6 | # Usage: vt-set-profile NAME 7 | 8 | vt_set_profile() 9 | { 10 | local name="${1}" 11 | echo -ne "\033P\$p${name}\033\\" 12 | } 13 | 14 | vt_set_profile "${1}" 15 | -------------------------------------------------------------------------------- /src/crispy/BufferObject.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #include 3 | 4 | namespace crispy 5 | { 6 | 7 | template class buffer_object; 8 | template class buffer_fragment; 9 | template class buffer_object_pool; 10 | 11 | } // namespace crispy 12 | -------------------------------------------------------------------------------- /src/crispy/overloaded.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #pragma once 3 | 4 | template 5 | struct overloaded: Ts... // NOLINT(readability-identifier-naming) 6 | { 7 | using Ts::operator()...; 8 | }; 9 | 10 | template 11 | overloaded(Ts...) -> overloaded; 12 | -------------------------------------------------------------------------------- /.github/codechecker/skipfile.txt: -------------------------------------------------------------------------------- 1 | -/usr/include/* 2 | -/usr/lib/llvm*/lib/clang/*/include/* 3 | -*/Build/* 4 | -*/Build/src/contour/contour_autogen/* 5 | -*/Build/src/contour/opengl/contour_frontend_opengl_autogen/* 6 | +_deps/sources/libunicode-*/* 7 | +_deps/sources/termbench-pro-*/* 8 | -_deps/sources/* 9 | -_deps/* 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | insert_final_newline = true 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | indent_size = 2 13 | 14 | [*.xml] 15 | indent_size = 2 16 | 17 | [.github/**/*.yml] 18 | indent_size = 2 19 | -------------------------------------------------------------------------------- /CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 6, 3 | "cmakeMinimumRequired": { 4 | "major": 3, 5 | "minor": 27, 6 | "patch": 0 7 | }, 8 | "include": [ 9 | "cmake/presets/os-linux.json", 10 | "cmake/presets/os-windows.json", 11 | "cmake/presets/os-macos.json" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /test/origin-mode-and-margins.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | echo -ne "\033[65;1\"p" # VT level 4, 7-bit controls (needed for L/R margin) 4 | echo -ne "\033[?69h" # allow L/R margin mode 5 | echo -ne "\033[5;15s" # left/right margin 6 | 7 | echo -ne "\033[5;15r" # top/bottom margin 8 | 9 | echo -ne "\033[?6h" # enable Origin mode 10 | -------------------------------------------------------------------------------- /test/emoji/facepalm.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | printf "\U0001F926\n" # FACEPALM 4 | printf "\U0001F926\U0001F3FC\n" # EMOJI MODIFIER FITZPATRICK TYPE-3 5 | printf "\U0001F926\U0001F3FC\u200D\u2642\n" # ZWJ, MALE SIGN 6 | printf "\U0001F926\U0001F3FC\u200D\u2642\uFE0F\n" # VS16 (VARIATION SELECTOR-16) 7 | -------------------------------------------------------------------------------- /scripts/check-includes.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | grep -R --include '*.cpp' -E '^#.*include ".*"$' src/ 4 | rv1=$? 5 | 6 | grep -R --include '*.h' -E '^#.*include ".*"$' src/ 7 | rv2=$? 8 | 9 | if [[ $rv1 -eq 0 || $rv2 -eq 0 ]]; then 10 | echo 1>&2 "Error: found #include \"...\" in C++ files." 11 | exit 1 12 | else 13 | echo "All good. ;-)" 14 | fi 15 | -------------------------------------------------------------------------------- /src/crispy/test_main.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #define CATCH_CONFIG_RUNNER 3 | #include 4 | 5 | int main(int argc, char const* argv[]) 6 | { 7 | int const result = Catch::Session().run(argc, argv); 8 | 9 | // avoid closing extern console to close on VScode/windows 10 | // system("pause"); 11 | 12 | return result; 13 | } 14 | -------------------------------------------------------------------------------- /.github/fontconfig-memleak-fix.patch: -------------------------------------------------------------------------------- 1 | diff --git a/src/fccfg.c b/src/fccfg.c 2 | index 328dd9c..c59879c 100644 3 | --- a/src/fccfg.c 4 | +++ b/src/fccfg.c 5 | @@ -2213,6 +2213,8 @@ FcConfigFilename (const FcChar8 *url) 6 | return NULL; 7 | for (p = path; *p; p++) 8 | { 9 | + if (file) 10 | + FcStrFree (file); 11 | file = FcConfigFileExists (*p, url); 12 | if (file) 13 | break; 14 | -------------------------------------------------------------------------------- /src/contour/display/shaders/background.vert: -------------------------------------------------------------------------------- 1 | uniform highp mat4 u_projection; 2 | layout (location = 0) in highp vec3 vs_vertex; // target vertex coordinates 3 | layout (location = 1) in highp vec4 vs_colors; // custom foreground colors 4 | 5 | out mediump vec4 fs_textColor; 6 | 7 | void main() 8 | { 9 | gl_Position = u_projection * vec4(vs_vertex.xyz, 1.0); 10 | fs_textColor = vs_colors; 11 | } 12 | -------------------------------------------------------------------------------- /vcpkg-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "default-registry": { 3 | "kind": "git", 4 | "baseline": "000d1bda1ffa95a73e0b40334fa4103d6f4d3d48", 5 | "repository": "https://github.com/microsoft/vcpkg" 6 | }, 7 | "registries": [ 8 | { 9 | "kind": "artifact", 10 | "location": "https://github.com/microsoft/vcpkg-ce-catalog/archive/refs/heads/main.zip", 11 | "name": "microsoft" 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /src/contour/org.contourterminal.Contour.OpenHere.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Type=Service 3 | X-KDE-ServiceTypes=KonqPopupMenu/Plugin 4 | MimeType=inode/directory; 5 | Actions=runContourHere; 6 | X-KDE-AuthorizeAction=shell_access 7 | 8 | [Desktop Action runContourHere] 9 | Exec=contour terminal working-directory %f 10 | Icon=org.contourterminal.Contour 11 | Name=Open Contour Terminal here 12 | Comment=Run Contour from the given working directory. 13 | -------------------------------------------------------------------------------- /.ecrc: -------------------------------------------------------------------------------- 1 | { 2 | "Verbose": false, 3 | "Debug": false, 4 | "IgnoreDefaults": false, 5 | "SpacesAftertabs": false, 6 | "NoColor": false, 7 | "Exclude": ["docs.*"], 8 | "AllowedContentTypes": [], 9 | "PassedFiles": [], 10 | "Disable": { 11 | "EndOfLine": false, 12 | "Indentation": false, 13 | "IndentSize": true, 14 | "InsertFinalNewline": false, 15 | "TrimTrailingWhitespace": false, 16 | "MaxLineLength": false 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/text_shaper/.clang-tidy: -------------------------------------------------------------------------------- 1 | --- 2 | InheritParentConfig: true 3 | Checks: >- 4 | readability-identifier-naming 5 | CheckOptions: 6 | - key: readability-identifier-naming.EnumCase 7 | value: lower_case 8 | - key: readability-identifier-naming.EnumConstantCase 9 | value: lower_case 10 | - key: readability-identifier-naming.ClassCase 11 | value: lower_case 12 | - key: readability-identifier-naming.TypeAliasCase 13 | value: lower_case 14 | -------------------------------------------------------------------------------- /.github/workflows/changelog.yml: -------------------------------------------------------------------------------- 1 | name: Check Changelog 2 | 3 | on: 4 | pull_request: 5 | types: [assigned, opened, synchronize, reopened, labeled, unlabeled] 6 | branches: 7 | - master 8 | 9 | jobs: 10 | Check-Changelog: 11 | name: Check Changelog Action 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: tarides/changelog-check-action@v2 15 | name: Check changelog 16 | with: 17 | changelog: metainfo.xml 18 | -------------------------------------------------------------------------------- /.github/codechecker/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "analyze": [ 3 | "--enable=portability", 4 | "--enable=security", 5 | "--enable=sensitive", 6 | "--disable=google", 7 | "--enable=google-build-namespaces", 8 | "--enable=bugprone-easily-swappable-parameters", 9 | "--enable=readability-suspicious-call-argument", 10 | "--skip=.github/codechecker/skipfile.txt" 11 | ], 12 | "parse": [ 13 | "--skip=.github/codechecker/skipfile.txt" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /docs/vt-extensions/font-settings.md: -------------------------------------------------------------------------------- 1 | # Query or Change Font Settings 2 | 3 | This VT extension can be used to query the current font settings of the connected terminal 4 | or to change them. 5 | 6 | Mind, there is a similar VT extension (OSC 50) introduced by xterm, which is inferior. 7 | 8 | ## Syntax: Query Font 9 | 10 | ``` 11 | OSC 60 ST 12 | ``` 13 | 14 | ## Syntax: Query Font 15 | 16 | ``` 17 | OSC 60 ; size ; regular ; bold ; italic ; bold italic ST 18 | ``` 19 | 20 | -------------------------------------------------------------------------------- /test/box-drawings.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | FRAMED="\e[51m" 4 | YELLOW="\e[1;33m" 5 | GREEN="\e[1;34m" 6 | RESET="\e[m" 7 | 8 | for i in `seq 1 5`; do echo -ne "║│╎┆┊┃╏┇┋\n"; done 9 | for i in `seq 1 5`; do echo -ne "|──|╌╌|┄┄|┈┈|\n"; done 10 | 11 | echo -ne "${FRAMED}${YELLOW}" 12 | 13 | # echo -ne "\t\u256D\u2500\n" 14 | # echo -ne "\t\u2570\u2500\n" 15 | echo -ne " \u256D\u2500\u256E\n" 16 | echo -ne " \u2570\u2500\u256F\n" 17 | 18 | echo -ne "${RESET}" 19 | 20 | read 21 | -------------------------------------------------------------------------------- /test/synchronized-output.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | echo -ne "The following text should be printed after a short sleep \e[1;31minstantly\e[m.\n" 4 | echo -ne "On non-conforming terminals, the text will be written in two chunks.\n\n" 5 | sleep 0.01 # make sure that's actually flushed out 6 | 7 | echo -ne "\033[?2026h" 8 | 9 | echo -ne "Hello " 10 | for i in `seq 1 10`; do 11 | echo -ne "." 12 | sleep 0.1 13 | done 14 | echo -ne " World\n" 15 | 16 | echo -ne "\033[?2026l" 17 | -------------------------------------------------------------------------------- /scripts/mkdebchangelog.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | set -ex 4 | 5 | ROOTDIR="$(readlink -f $(dirname $0)/..)" 6 | VERSION="${1:-0.0.0}" 7 | INCR="${2:-$(date +%s)}" 8 | 9 | sed -i -e "s/0.0.0-1/${VERSION}-${INCR}/g" "${ROOTDIR}/debian/changelog" 10 | 11 | # cat "${ROOTDIR}/Changelog.md" \ 12 | # | sed 's/^### \([^ ]\+\).*$/contour (\1-1) focal; urgency=low/' \ 13 | # | while read LINE; do 14 | # if [[ "${LINE}" != "" ]] && [[ "${LINE}" != "^contour" ]]; then 15 | # fi 16 | # done 17 | -------------------------------------------------------------------------------- /docs/vt-extensions/clickable-links.md: -------------------------------------------------------------------------------- 1 | # Clickable Links 2 | 3 | This is also known as the `OSC 8` feature, 4 | as it is implemented by quite some other terminal emulators and client applications already. 5 | 6 | ## Syntax 7 | 8 | `OSC 8 ;; URL ST TEXT OSC 8 ;; ST` 9 | 10 | `OSC 8 ; id=ID ; URL ST TEXT OSC 8 ;; ST` 11 | 12 | ## Client Tooling 13 | 14 | - `ls` on Linux supports parameter `--hyperlinks=auto`. 15 | - GCC 10+ supports hyperlinks for diagnostic output, see `-fdiagnostics-hyperlink=auto` 16 | 17 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # See https://docs.github.com/en/enterprise-server@2.21/github/creating-cloning-and-archiving-repositories/creating-a-repository-on-github/about-code-owners#example-of-a-codeowners-file 2 | 3 | * @christianparpart 4 | /.github/ @data-man 5 | /.github/CODEOWNERS @christianparpart 6 | /.github/codechecker/ @whisperity 7 | /.github/workflows/*codechecker* @whisperity 8 | cmake/ @data-man 9 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if(("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")) 2 | #(TODO: first get rid of these warnings) add_compile_options(-Werror) 3 | endif() 4 | 5 | include(PedanticCompiler) 6 | 7 | add_subdirectory(crispy) 8 | add_subdirectory(text_shaper) 9 | add_subdirectory(vtpty) 10 | add_subdirectory(vtparser) 11 | add_subdirectory(vtbackend) 12 | add_subdirectory(vtrasterizer) 13 | 14 | if(CONTOUR_FRONTEND_GUI) 15 | add_subdirectory(contour) 16 | endif() 17 | -------------------------------------------------------------------------------- /vcpkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json", 3 | "builtin-baseline": "80403036a665cb8fcc1a1b3e17593d20b03b2489", 4 | "dependencies": [ 5 | { "name": "catch2", "version>=": "3.4.0" }, 6 | { "name": "libssh2", "version>=": "1.11.0" }, 7 | { "name": "ms-gsl" }, 8 | { "name": "range-v3", "version>=": "0.12.0" }, 9 | { "name": "yaml-cpp" }, 10 | "freetype", 11 | "harfbuzz" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /.spellcheck.yml: -------------------------------------------------------------------------------- 1 | matrix: 2 | - name: Markdown 3 | aspell: 4 | lang: en 5 | dictionary: 6 | encoding: utf-8 7 | pipeline: 8 | - pyspelling.filters.markdown: 9 | - pyspelling.filters.html: 10 | comments: false 11 | ignores: 12 | - code 13 | - pre 14 | sources: 15 | - '**/*.md' 16 | wordlists: 17 | - .wordlist.txt 18 | default_encoding: utf-8 19 | - name: cpp 20 | sources: 21 | - '**/*.{cpp,hpp,c,h}' 22 | pipeline: 23 | - pyspelling.filters.cpp: 24 | line_comments: false 25 | -------------------------------------------------------------------------------- /cmake/ClangTidy.cmake: -------------------------------------------------------------------------------- 1 | 2 | option(ENABLE_TIDY "Enable clang-tidy [default: OFF]" OFF) 3 | if(ENABLE_TIDY) 4 | find_program(CLANG_TIDY_EXE 5 | NAMES clang-tidy 6 | DOC "Path to clang-tidy executable") 7 | if(NOT CLANG_TIDY_EXE) 8 | message(STATUS "[clang-tidy] Not found.") 9 | else() 10 | message(STATUS "[clang-tidy] found: ${CLANG_TIDY_EXE}") 11 | set(CMAKE_CXX_CLANG_TIDY "${CLANG_TIDY_EXE}") 12 | endif() 13 | else() 14 | message(STATUS "[clang-tidy] Disabled.") 15 | endif() 16 | -------------------------------------------------------------------------------- /.github/actions/spelling/allow/api-terms.txt: -------------------------------------------------------------------------------- 1 | EPOLLIN 2 | FSM 3 | FTERRORS 4 | FUNCDNAME 5 | GLES 6 | GLFW 7 | GLIBCXX 8 | GLchar 9 | GLenum 10 | GLfloat 11 | GLsizei 12 | GLsizeiptr 13 | GLuint 14 | HPCON 15 | HRESULT 16 | IUnknown 17 | PIDVNODEPATHINFO 18 | PIPESTATUS 19 | SSHDSS 20 | SSHRSA 21 | STARTUPINFO 22 | STARTUPINFOEX 23 | STREQUAL 24 | STRONGHASH 25 | UCRT 26 | VAO 27 | VBO 28 | WCA 29 | WIF 30 | WINAPI 31 | WINCOMPATTRDATA 32 | WORKDIR 33 | addrinfo 34 | addrlen 35 | catchorg 36 | cerrno 37 | consolas 38 | coretext 39 | fbo 40 | -------------------------------------------------------------------------------- /src/text_shaper/README.md: -------------------------------------------------------------------------------- 1 | # `text_shaper` 2 | 3 | A maybe platform independent text shaping and rendering API 4 | that can maybe used by other projects too. 5 | 6 | ### Font loading 7 | 8 | Either via `shaper::load_font(font_description, font_size) -> font_key`, 9 | or as part of font fallback, an alternate font at the same given size. 10 | 11 | Loaded fonts are stored in a map associated with a `font_key`. 12 | 13 | ### Requirements 14 | 15 | - libunicode 16 | - for Linux: 17 | - freetype 18 | - harfbuzz 19 | - fontconfig 20 | -------------------------------------------------------------------------------- /src/text_shaper/font_locator_provider.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #pragma once 3 | 4 | #include 5 | 6 | #include 7 | 8 | namespace text 9 | { 10 | 11 | class font_locator_provider 12 | { 13 | public: 14 | static font_locator_provider& get(); 15 | 16 | font_locator& native(); 17 | 18 | font_locator& mock(); 19 | 20 | private: 21 | std::unique_ptr _native {}; 22 | std::unique_ptr _mock {}; 23 | }; 24 | 25 | } // namespace text 26 | -------------------------------------------------------------------------------- /scripts/check-pr-todos.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | count=0 3 | FOUND=$(git grep "TODO(pr)" | grep -v "scripts/check-pr-todos.sh") 4 | if [[ "${FOUND}" != "" ]]; then 5 | count=$[count + 1] 6 | fi 7 | 8 | FOUND2=$(git grep "crispy::todo(" | grep -v "scripts/check-pr-todos.sh") 9 | if [[ "${FOUND2}" != "" ]]; then 10 | count=$[count + 1] 11 | fi 12 | 13 | if [[ "${count}" == "0" ]]; then 14 | exit 0 15 | fi 16 | echo "This PR still contains PR-related TODO itmes that must be resolved." 17 | echo 18 | echo "${FOUND} ${FOUND2}" 19 | exit 1 20 | -------------------------------------------------------------------------------- /src/crispy/.clang-tidy: -------------------------------------------------------------------------------- 1 | --- 2 | InheritParentConfig: true 3 | Checks: >- 4 | readability-identifier-naming 5 | CheckOptions: 6 | - key: readability-identifier-naming.EnumCase 7 | value: lower_case 8 | - key: readability-identifier-naming.EnumConstantCase 9 | value: lower_case 10 | - key: readability-identifier-naming.ClassCase 11 | value: lower_case 12 | - key: readability-identifier-naming.StructCase 13 | value: lower_case 14 | - key: readability-identifier-naming.TypeAliasCase 15 | value: lower_case 16 | -------------------------------------------------------------------------------- /src/crispy/Comparison.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #pragma once 3 | #include 4 | 5 | namespace crispy 6 | { 7 | 8 | enum class comparison : uint8_t 9 | { 10 | Less, 11 | Equal, 12 | Greater 13 | }; 14 | 15 | template 16 | constexpr comparison strongCompare(T const& a, T const& b) 17 | { 18 | if (a < b) 19 | return comparison::Less; 20 | else if (a == b) 21 | return comparison::Equal; 22 | else 23 | return comparison::Greater; 24 | } 25 | 26 | } // namespace crispy 27 | -------------------------------------------------------------------------------- /.codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | notify: 3 | require_ci_to_pass: yes 4 | 5 | coverage: 6 | precision: 2 7 | round: down 8 | range: "70...100" 9 | 10 | status: 11 | project: yes 12 | patch: yes 13 | changes: no 14 | 15 | ignore: 16 | - src/vtbackend/*_test.cpp 17 | - 3rdparty/* 18 | 19 | parsers: 20 | gcov: 21 | branch_detection: 22 | conditional: yes 23 | loop: yes 24 | method: no 25 | macro: no 26 | 27 | comment: 28 | layout: "header, diff" 29 | behavior: default 30 | require_changes: no 31 | -------------------------------------------------------------------------------- /docs/demo/images.md: -------------------------------------------------------------------------------- 1 | # Image Support 2 | 3 | Contour implements first-class [Sixel](https://en.wikipedia.org/wiki/Sixel) image support. 4 | 5 | Most notably supporting libraries, 6 | are [notcurses](https://github.com/dankamongmen/notcurses/) 7 | and [libsixel](https://github.com/libsixel/libsixel/). 8 | 9 | Also, libsixel does provide a CLI to convert well known image formats (like PNG) 10 | to Sixel format and have them printed to the terminal. 11 | 12 | ![sixel-demo](../screenshots/contour-lsix.png) 13 | ![julia-repl-sixel-demo](../screenshots/contour-julia-repl-sixel-image.png) 14 | -------------------------------------------------------------------------------- /src/vtbackend/cell/CellConfig.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | namespace vtbackend 8 | { 9 | 10 | /// Type of cell to be used with the primary screen. 11 | using PrimaryScreenCell = CompactCell; 12 | 13 | /// Type of cell to be used with the alternate screen. 14 | using AlternateScreenCell = CompactCell; 15 | 16 | /// The Cell to be used with the indicator (and host writable) status line. 17 | using StatusDisplayCell = SimpleCell; 18 | 19 | } // namespace vtbackend 20 | -------------------------------------------------------------------------------- /cmake/CIncludeMe.cmake: -------------------------------------------------------------------------------- 1 | add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/cincludeme) 2 | 3 | function(CIncludeMe INPUT_FILE OUTPUT_FILE SYMBOL_NAME NAMESPACE) 4 | add_custom_command( 5 | OUTPUT "${OUTPUT_FILE}" 6 | WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" 7 | COMMAND cincludeme "${OUTPUT_FILE}" "${NAMESPACE}" "${INPUT_FILE}" "${SYMBOL_NAME}" 8 | DEPENDS cincludeme "${INPUT_FILE}" 9 | COMMENT "Generating ${OUTPUT_FILE} from ${INPUT_FILE}" 10 | VERBATIM 11 | ) 12 | set_source_files_properties("${OUTPUT_FILE}" PROPERTIES GENERATED TRUE) 13 | endfunction() 14 | -------------------------------------------------------------------------------- /.github/actions/spelling/allow/patterns.txt: -------------------------------------------------------------------------------- 1 | AFAF 2 | AGHIE 3 | DEFG 4 | EEEEE 5 | EFAB 6 | FAFD 7 | FBAA 8 | FBAB 9 | FBAC 10 | FBAD 11 | FBAE 12 | FBAF 13 | FBCDJ 14 | FBF 15 | FFAF 16 | FFFA 17 | FFFC 18 | FFFD 19 | FGI 20 | FGI 21 | FHI 22 | FHI 23 | Fabcdef 24 | GHHIJL 25 | GHJ 26 | Gijkl 27 | IJKL 28 | KLMNO 29 | KLMNOPQRST 30 | MNOP 31 | MNOPQR 32 | XXXXX 33 | XXXXXXXXX 34 | XYIU 35 | XYIU 36 | YWI 37 | YWJj 38 | aaaaa 39 | aaaaaa 40 | aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz 41 | aanimate 42 | abbcdf 43 | abdeff 44 | abz 45 | aca 46 | acaf 47 | acdf 48 | bbb 49 | bce 50 | bda 51 | cdcd 52 | cde 53 | cdef 54 | cdefg 55 | -------------------------------------------------------------------------------- /src/vtpty/Pty.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #include 3 | 4 | #if defined(_MSC_VER) 5 | #include 6 | #else 7 | #include 8 | #endif 9 | 10 | using std::make_unique; 11 | using std::optional; 12 | using std::unique_ptr; 13 | 14 | namespace vtpty 15 | { 16 | 17 | unique_ptr createPty(PageSize pageSize, optional viewSize) 18 | { 19 | #if defined(_MSC_VER) 20 | return make_unique(pageSize /*TODO: , viewSize*/); 21 | #else 22 | return make_unique(pageSize, viewSize); 23 | #endif 24 | } 25 | 26 | } // namespace vtpty 27 | -------------------------------------------------------------------------------- /test/emoji/wchar-test.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | wchar="$(printf '\u26a1')" 4 | 5 | screen_goto() { 6 | local row=$1 7 | local col=$2 8 | echo -ne "\033[${row};${col}H" 9 | } 10 | 11 | screen_goto_column() { 12 | local col=$1 13 | echo -ne "\033[${col}G" 14 | } 15 | 16 | screen_clr() { 17 | clear 18 | } 19 | 20 | screen_write() { 21 | echo -ne "${@}" 22 | } 23 | 24 | clear 25 | screen_write "ABCDEFG\n" 26 | screen_write "${wchar}XYZ\n" 27 | 28 | screen_write "${wchar}XYZ" 29 | screen_goto_column 2 30 | screen_write "AB" 31 | #screen_goto_column 1 32 | #screen_write "X" 33 | screen_write "\n" 34 | -------------------------------------------------------------------------------- /.github/mock-font-locator.yml: -------------------------------------------------------------------------------- 1 | 2 | mock_font_locator: 3 | # Ubuntu 20.04 4 | - { family: "monospace", slant: normal, weight: normal, path: "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf" } 5 | - { family: "emoji", slant: normal, weight: normal, path: "/usr/share/fonts/truetype/noto/NotoColorEmoji.ttf" } 6 | 7 | # That would be it for macOS 8 | # - { family: "monospace", slant: normal, weight: normal, path: "/Users/trapni/Library/Fonts/JetBrains Mono Regular Nerd Font Complete Mono.ttf" } 9 | # - { family: "emoji", slant: normal, weight: normal, path: "/System/Library/Fonts/Apple Color Emoji.ttc" } 10 | -------------------------------------------------------------------------------- /docs/vt-extensions/unicode-core.md: -------------------------------------------------------------------------------- 1 | # Unicode Core 2 | 3 | We implement the [Terminal Unicode Core](https://github.com/contour-terminal/terminal-unicode-core) specification, 4 | which addresses some of the problems terminals usually have when it comes to Unicode. 5 | 6 | This mainly addresses complex grapheme cluster handling, variation selectors (VS15, VS16) handling, and its cursor placement. 7 | 8 | Right-to-left (RTL) text is explicitly not handled in this extension. 9 | 10 | Please follow up on the specification here: [https://github.com/contour-terminal/terminal-unicode-core](https://github.com/contour-terminal/terminal-unicode-core) 11 | -------------------------------------------------------------------------------- /src/contour/display/shaders/text.vert: -------------------------------------------------------------------------------- 1 | uniform highp mat4 vs_projection; // projection matrix (flips around the coordinate system) 2 | 3 | layout (location = 0) in highp vec3 vs_vertex; // target vertex coordinates 4 | layout (location = 1) in highp vec4 vs_texCoords; // 2D-atlas texture coordinates 5 | layout (location = 2) in highp vec4 vs_colors; // custom foreground colors 6 | 7 | out highp vec4 fs_TexCoord; 8 | out highp vec4 fs_textColor; 9 | 10 | void main() 11 | { 12 | gl_Position = vs_projection * vec4(vs_vertex.xyz, 1.0); 13 | 14 | fs_TexCoord = vs_texCoords; 15 | fs_textColor = vs_colors; 16 | } 17 | -------------------------------------------------------------------------------- /docs/vt-extensions/line-reflow-mode.md: -------------------------------------------------------------------------------- 1 | # Line Reflow Reconfiguration 2 | 3 | On resize, overly long lines, that would otherwise be cut off, are usually reflowed to the next line on modern terminals. 4 | 5 | This extension allows toggling reflow for the current line and subsequent lines using a DEC mode (`2028`). 6 | 7 | ## Feature detection 8 | 9 | Use `DECRQM` (`CSI ? 2028 $ p`) to detect support for line reflow reconfiguration. 10 | 11 | ## Using the feature 12 | 13 | Use `CSI ? 2028 h` to enable text reflow on the current line and the following lines. 14 | 15 | Use `CSI ? 2028 l` to disable text reflow on the current line and the following lines. 16 | 17 | -------------------------------------------------------------------------------- /src/vtbackend/test_main.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #include 3 | #include 4 | 5 | #define CATCH_CONFIG_RUNNER 6 | #include 7 | 8 | int main(int argc, char const* argv[]) 9 | { 10 | char const* logFilterString = getenv("LOG"); 11 | if (logFilterString) 12 | { 13 | logstore::configure(logFilterString); 14 | crispy::app::customizeLogStoreOutput(); 15 | } 16 | int const result = Catch::Session().run(argc, argv); 17 | 18 | // avoid closing extern console to close on VScode/windows 19 | // system("pause"); 20 | 21 | return result; 22 | } 23 | -------------------------------------------------------------------------------- /test/scroll-up-with-margins.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | clear 3 | 4 | # draw left line 5 | for i in `seq 1 30`; do 6 | echo -ne "\033[${i};1H" 7 | echo -n "| $i ..." 8 | done 9 | 10 | # draw top line 11 | echo -ne "\033[4;10H" 12 | echo -ne "=========================================================" 13 | 14 | # draw bottom line 15 | echo -ne "\033[16;10H" 16 | echo -ne "=========================================================" 17 | 18 | echo -ne "\033[5;15r" 19 | echo -ne "\033[15;1H" 20 | 21 | for i in a b c d; do 22 | sleep 1 23 | echo -ne "\t${i}\n" 24 | done 25 | 26 | # restore default 27 | sleep 1 28 | echo -ne "\033[r" 29 | echo -ne "\033[28H" 30 | -------------------------------------------------------------------------------- /.github/labeler.yml: -------------------------------------------------------------------------------- 1 | CI: 2 | - .github/** 3 | CMake: 4 | - "**CMakeLists.txt" 5 | - cmake/** 6 | OpenGL: 7 | - src/contour/opengl/*.cpp 8 | - src/contour/opengl/*.h 9 | "VT: backend": 10 | - src/vtbackend/*.cpp 11 | - src/vtbackend/*.h 12 | - src/vtpty/*.cpp 13 | - src/vtpty/*.h 14 | - src/vtparser/*.cpp 15 | - src/vtparser/*.h 16 | "VT: rasterizer": 17 | - src/vtrasterizer/*.cpp 18 | - src/vtrasterizer/*.h 19 | documentation: 20 | - "**/*.md" 21 | - docs/** 22 | fonts: 23 | - src/text_shaper/*.cpp 24 | - src/text_shaper/*.h 25 | frontend: 26 | - src/contour/** 27 | test: 28 | - src/**/*_test.cpp 29 | -------------------------------------------------------------------------------- /docs/configuration/advanced/images.md: -------------------------------------------------------------------------------- 1 | # Advanced Image configuration 2 | 3 | ### Sixel scrolling mode 4 | 5 | Enable or disable sixel scrolling (SM/RM ?80 default) 6 | 7 | sixel_scrolling: true 8 | 9 | ### Sixel register capacity 10 | 11 | Configures the maximum number of color registers available 12 | for rendering Sixel graphics. 13 | 14 | sixel_register_count: 4096 15 | 16 | ### Maximum image size 17 | 18 | Sets the maximum width and height in pixels of an image to be accepted. 19 | 20 | A value of 0 defaults to system screen pixel width/height. 21 | 22 | Default: `0` (that is: current screen size). 23 | 24 | max_width: 0 25 | max_height: 0 26 | -------------------------------------------------------------------------------- /src/text_shaper/coretext_locator.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | namespace text 8 | { 9 | class coretext_locator: public font_locator 10 | { 11 | public: 12 | coretext_locator(); 13 | 14 | [[nodiscard]] font_source_list locate(font_description const& description) override; 15 | [[nodiscard]] font_source_list all() override; 16 | [[nodiscard]] font_source_list resolve(gsl::span codepoints) override; 17 | 18 | private: 19 | struct Private; 20 | std::unique_ptr _d; 21 | }; 22 | } // namespace text 23 | -------------------------------------------------------------------------------- /scripts/mkchangelogmd.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | project_root="`dirname $0`/.." 4 | metainfo_xml="$project_root/metainfo.xml" 5 | 6 | version_string=`xmllint --xpath 'string(/component/releases/release[1]/@version)' $metainfo_xml` 7 | release_type=`xmllint --xpath 'string(/component/releases/release[1]/@type)' $metainfo_xml` 8 | 9 | if test x$release_type = xstable; then 10 | release_type=`date +%Y-%m-%d` 11 | else 12 | release_type="unreleased" 13 | fi 14 | 15 | echo "### $version_string ($release_type)" 16 | echo "" 17 | 18 | # Changelog items 19 | xmllint --xpath '/component/releases/release[1]/description/ul/li' $metainfo_xml | 20 | sed 's/
  • / - /g' | 21 | sed 's,
  • ,\n,g' 22 | -------------------------------------------------------------------------------- /src/contour/display/shaders/background_image.vert: -------------------------------------------------------------------------------- 1 | uniform highp mat4 u_projection; 2 | uniform lowp sampler2D u_backgroundImage; 3 | uniform highp vec2 u_resolution; 4 | uniform highp float u_blur; 5 | uniform highp float u_opacity; 6 | uniform highp float u_time; 7 | 8 | layout (location = 0) in highp vec3 vs_vertex; // target vertex coordinates 9 | layout (location = 1) in highp vec2 vs_texCoords; // normalized texture coordinates 10 | 11 | out highp vec2 fs_TexCoord; 12 | out highp vec2 fs_FragCoord; 13 | 14 | void main() 15 | { 16 | gl_Position = u_projection * vec4(vs_vertex.xyz, 1.0); 17 | fs_TexCoord = vs_texCoords; 18 | fs_FragCoord = gl_Position.xy; 19 | } 20 | -------------------------------------------------------------------------------- /.github/actions/spelling/allow/qt-terms.txt: -------------------------------------------------------------------------------- 1 | QAbstract 2 | QApplication 3 | QAudio 4 | QByte 5 | QClipboard 6 | QCore 7 | QDebug 8 | QDesktop 9 | QElapsed 10 | QEvent 11 | QFile 12 | QFocus 13 | QFont 14 | QGui 15 | QHost 16 | QHover 17 | QIO 18 | QIcon 19 | QImage 20 | QInput 21 | QJS 22 | QKey 23 | QMLJSDEBUGGER 24 | QMatrix 25 | QMedia 26 | QMessage 27 | QMeta 28 | QMime 29 | QModel 30 | QMouse 31 | QNative 32 | QObject 33 | QOffscreen 34 | QOpen 35 | QPoint 36 | QProcess 37 | QQml 38 | QQuick 39 | QRect 40 | QRegion 41 | QRunnable 42 | QSG 43 | QScreen 44 | QStandard 45 | QString 46 | QStyle 47 | QSurface 48 | QSystem 49 | QTimer 50 | QType 51 | QUrl 52 | QVariant 53 | QVector 54 | QWheel 55 | QWindow 56 | Qunused 57 | -------------------------------------------------------------------------------- /test/DECIC.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | clear 4 | 5 | echo -ne "1234567890\n" 6 | echo -ne "ABCDEFGHIJ\n" 7 | echo -ne "ABCDEFGHIJ\n" 8 | echo -ne "ABCDEFGHIJ\n" 9 | echo -ne "ABCDEFGHIJ" 10 | 11 | echo -ne "\033[?69h" # DECLRMM enable 12 | echo -ne "\033[2;8s" # DECSLRM - set left/right margin 13 | echo -ne "\033[2;4r" # DECSTBM - set top/bottom margin 14 | sleep 1 15 | 16 | echo -ne "\033[2;2H" 17 | sleep 1 18 | 19 | for i in `seq 1 5`; do 20 | echo -ne "\033[1'}" # DECIC - insert column 1 21 | sleep 1 22 | done 23 | 24 | echo -ne "\033[?69l" # DECLRMM disable 25 | echo -ne "\033[r" # DECSTBM - set top/bottom margin 26 | sleep 1 27 | 28 | echo -ne "\033[6;1H" 29 | echo "" 30 | read 31 | -------------------------------------------------------------------------------- /src/contour/CaptureScreen.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #pragma once 3 | 4 | #include 5 | 6 | #include 7 | 8 | namespace contour 9 | { 10 | 11 | struct CaptureSettings 12 | { 13 | bool logicalLines = false; // -l 14 | bool words = false; // split output into one word per line 15 | double timeout = 1.0f; // -t 16 | std::string outputFile; // -o 17 | int verbosityLevel = 0; // -v, -q (XXX intentionally not parsed currently!) 18 | vtbackend::LineCount lineCount = vtbackend::LineCount { 0 }; // (use terminal default) 19 | }; 20 | 21 | bool captureScreen(CaptureSettings const& settings); 22 | 23 | } // namespace contour 24 | -------------------------------------------------------------------------------- /src/contour/ui.template/RequestPermission.qml.in: -------------------------------------------------------------------------------- 1 | // vim:syntax=qml 2 | import @qml_import_QtQuicklabs@ 3 | 4 | MessageDialog { 5 | id: messageDialog 6 | // icon: StandardIcon.Question 7 | // TODO: which permissions exactly? Fill me in! 8 | title: "Host Application is Requesting Permissions." 9 | text: "The host application is requesting special permissions. %1".arg("TODO: What perms?") 10 | 11 | buttons: MessageDialog.Yes | MessageDialog.YesToAll | MessageDialog.No | MessageDialog.NoToAll | MessageDialog.Abort 12 | 13 | function clickedRememberChoice() { 14 | return messageDialog.clickedButton == MessageDialog.YesToAll 15 | || messageDialog.clickedButton == MessageDialog.NoToAll; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /docs/vt-extensions/save-and-restore-sgr-attributes.md: -------------------------------------------------------------------------------- 1 | # Save and Restore SGR attributes. 2 | 3 | ## Feature detection 4 | 5 | Use `SGRSAVE` (`CSI # {`) save the currently set SGR attributes, 6 | and `SGRRESTORE` (`CSI # }`) to restore the previously saved SGR attributes. 7 | 8 | ## Relation to xterm's XTPUSHSGR / XTPOPSGR 9 | 10 | Both, `XTPUSHSGR` and `XTPOPSGR` are in its most basic form equivalent to `SGRSAVE` and `SGRRESTORE`, 11 | but the xterm extensions push and pop using a stack rather than save and restore using a simple 12 | storage location, and the xterm equivalent does allow pushing/popping only certain SGR attributes. 13 | 14 | This is needless functionality that should not be implemented by a terminal but rather in the applications itself. 15 | -------------------------------------------------------------------------------- /src/contour/display/shaders/dual_kawase_down.frag: -------------------------------------------------------------------------------- 1 | in highp vec4 vColor; 2 | out highp vec4 fColor; 3 | 4 | uniform highp sampler2D u_texture; 5 | uniform highp vec2 u_viewportResolution; 6 | uniform highp vec2 u_offset; 7 | uniform highp vec2 u_halfpixel; 8 | 9 | void main() 10 | { 11 | highp vec2 uv = vec2(gl_FragCoord.xy / u_viewportResolution); 12 | 13 | highp vec4 sum = texture(u_texture, uv) * 4.0; 14 | sum += texture(u_texture, uv - u_halfpixel.xy * u_offset); 15 | sum += texture(u_texture, uv + u_halfpixel.xy * u_offset); 16 | sum += texture(u_texture, uv + vec2(u_halfpixel.x, -u_halfpixel.y) * u_offset); 17 | sum += texture(u_texture, uv - vec2(u_halfpixel.x, -u_halfpixel.y) * u_offset); 18 | 19 | fColor = sum / 8.0; 20 | } 21 | -------------------------------------------------------------------------------- /test/batched-rendering.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | if [[ "$1" == "sync" ]]; then 4 | synchronous=1 5 | else 6 | synchronous=0 7 | fi 8 | 9 | batch_start() { 10 | echo -ne "\033[?2001h" 11 | } 12 | 13 | batch_end() { 14 | echo -ne "\033[?2001l" 15 | } 16 | 17 | trapped() { 18 | echo -ne "\nTrapped.\n" 19 | batch_end 20 | exit 0 21 | } 22 | 23 | trap trapped INT 24 | 25 | echo -ne "\033[1;1H" 26 | echo -ne "\033[2J" 27 | 28 | echo "prepare me" 29 | sleep 5 30 | 31 | i=0 32 | while [[ i -lt 5 ]]; do 33 | i=$[i + 1] 34 | 35 | test $synchronous -ne 0 && batch_start 36 | 37 | echo -ne "${i}: .... " 38 | sleep 1 39 | echo -ne "... done\n" 40 | sleep 1 41 | 42 | test $synchronous -ne 0 && batch_end 43 | done 44 | -------------------------------------------------------------------------------- /scripts/git-hooks/pre-commit.git-clang-format.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Runs "clang-format" on the commit before committing and prohibits commit if 4 | # changes happened. 5 | exit 0 6 | 7 | dir_list="$PWD" # Add the directories you want here 8 | cmd="git diff -U0 --no-color --staged HEAD -- $dir_list | clang-format-diff -p1" 9 | 10 | echo "" 11 | echo "Running clang-format on this commit" 12 | echo "" 13 | 14 | diff=$(eval "$cmd") 15 | if [[ $? -ne 0 ]] 16 | then 17 | echo "Command failed to execute." 18 | exit 1 19 | fi 20 | 21 | if [[ -z "$diff" ]] 22 | then 23 | echo "Everything is clean" 24 | exit 0 25 | else 26 | echo 1>&2 "$diff" 27 | echo 1>&2 "" 28 | echo 1>&2 "Commit aborted due to code format inconsistencies." 29 | exit 1 30 | fi 31 | -------------------------------------------------------------------------------- /src/crispy/base64_test.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #include 3 | 4 | #include 5 | 6 | using namespace crispy; 7 | 8 | TEST_CASE("base64.encode", "[base64]") 9 | { 10 | CHECK("YQ==" == base64::encode("a")); 11 | CHECK("YWI=" == base64::encode("ab")); 12 | CHECK("YWJj" == base64::encode("abc")); 13 | CHECK("YWJjZA==" == base64::encode("abcd")); 14 | CHECK("Zm9vOmJhcg==" == base64::encode("foo:bar")); 15 | } 16 | 17 | TEST_CASE("base64.decode", "[base64]") 18 | { 19 | CHECK("a" == base64::decode("YQ==")); 20 | CHECK("ab" == base64::decode("YWI=")); 21 | CHECK("abc" == base64::decode("YWJj")); 22 | CHECK("abcd" == base64::decode("YWJjZA==")); 23 | CHECK("foo:bar" == base64::decode("Zm9vOmJhcg==")); 24 | } 25 | -------------------------------------------------------------------------------- /src/contour/display/DisplayResources.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | shaders/background.frag 4 | shaders/background.vert 5 | shaders/background_image.frag 6 | shaders/background_image.vert 7 | shaders/blur_gaussian.frag 8 | shaders/dual_kawase_down.frag 9 | shaders/dual_kawase_up.frag 10 | shaders/simple.vert 11 | shaders/text.frag 12 | shaders/text.vert 13 | 14 | 15 | ../../vtrasterizer/shared_defines.h 16 | 17 | 18 | 19 | 21 | -------------------------------------------------------------------------------- /src/contour/resources.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | res/bell.oga 4 | res/bell.wav 5 | res/images/contour-logo-256.png 6 | res/images/contour-logo-256.png 7 | contour.yml 8 | shell-integration/shell-integration.zsh 9 | shell-integration/shell-integration.fish 10 | shell-integration/shell-integration.tcsh 11 | shell-integration/shell-integration.bash 12 | ui/RequestPermission.qml 13 | ui/Terminal.qml 14 | ui/main.qml 15 | 16 | 17 | 19 | -------------------------------------------------------------------------------- /src/text_shaper/directwrite_locator.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | struct IDWriteFontFace; 8 | 9 | namespace text 10 | { 11 | 12 | /** 13 | * Font locator API implementation using `DirectWrite` library. 14 | * 15 | * This is available only on Windows. 16 | */ 17 | class directwrite_locator: public font_locator 18 | { 19 | public: 20 | directwrite_locator(); 21 | 22 | font_source_list locate(font_description const& description) override; 23 | font_source_list all() override; 24 | font_source_list resolve(gsl::span codepoints) override; 25 | 26 | private: 27 | struct Private; 28 | std::unique_ptr _d; 29 | }; 30 | 31 | } // namespace text 32 | -------------------------------------------------------------------------------- /src/vtpty/UnixUtils.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | using namespace std::string_literals; 11 | 12 | namespace vtpty 13 | { 14 | 15 | UnixPipe::UnixPipe(unsigned flags): pfd { -1, -1 } 16 | { 17 | #if defined(__linux__) 18 | if (pipe2(pfd.data(), static_cast(flags)) < 0) 19 | throw std::runtime_error { "Failed to create PTY pipe. "s + strerror(errno) }; 20 | #else 21 | if (pipe(pfd.data()) < 0) 22 | throw std::runtime_error { "Failed to create PTY pipe. "s + strerror(errno) }; 23 | for (auto const fd: pfd) 24 | if (!util::setFileFlags(fd, static_cast(flags))) 25 | break; 26 | #endif 27 | } 28 | 29 | } // namespace vtpty 30 | -------------------------------------------------------------------------------- /src/vtrasterizer/shared_defines.h: -------------------------------------------------------------------------------- 1 | // NOLINTBEGIN(cppcoreguidelines-macro-to-enum) 2 | // NOLINTBEGIN(modernize-macro-to-enum) 3 | 4 | // Shared preprocessor definitions between C++ and GLSL. 5 | // 6 | // This file has no header guards because GLSL does not seem to understand that. 7 | 8 | // Render a alpha-antialiased glyph tile using the red channel. 9 | #define FRAGMENT_SELECTOR_GLYPH_ALPHA 0 10 | 11 | // Render a raw BGRA image (e.g. emoji or image data) 12 | #define FRAGMENT_SELECTOR_IMAGE_BGRA 1 13 | 14 | // Render an LCD-subpixel antialiased glyph (simple algorithm) 15 | #define FRAGMENT_SELECTOR_GLYPH_LCD_SIMPLE 2 16 | 17 | // Render an LCD-subpixel antialiased glyph (advanced algorithm) 18 | #define FRAGMENT_SELECTOR_GLYPH_LCD 3 19 | 20 | // NOLINTEND(modernize-macro-to-enum) 21 | // NOLINTEND(cppcoreguidelines-macro-to-enum) 22 | -------------------------------------------------------------------------------- /test/DECDC.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | 4 | clear 5 | 6 | echo -ne "\033[4;5Hx\033[4;15Hx" 7 | 8 | echo -ne "\033[65;1\"p" # VT level 4, 7-bit controls (needed for L/R margin) 9 | echo -ne "\033[?69h" # allow L/R margin mode 10 | echo -ne "\033[5;15s" # horizontal margin 11 | echo -ne "\033[5;15r" # vertical margin 12 | 13 | echo -ne "\033[44;37m" # set SGR 14 | 15 | echo -ne "\033[5;5H" # move cursor to row 5 col 5 16 | echo -ne "1234567890" # fill line 17 | 18 | echo -ne "\033[6;5H" # move cursor to row 6 col 5 19 | echo -ne "1234567890" # fill line 20 | echo -ne "\033[6;6H" # move cursor to 6;10 21 | echo -ne "\033[5'~" # DECDC: delete 5 columns 22 | 23 | # reset 24 | echo -ne "\033[m" # reset SGR 25 | echo -ne "\033[?69l" # disable horizontal margin 26 | #echo -ne "\033[r" 27 | #echo -ne "\033[s" 28 | 29 | echo -ne "\033[15;1H" 30 | -------------------------------------------------------------------------------- /src/crispy/Size_test.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | using terminal::Coordinate; 10 | using terminal::Size; 11 | 12 | TEST_CASE("Size.iterator", "") 13 | { 14 | Size const s { 3, 2 }; 15 | Size::iterator i = s.begin(); 16 | Size::iterator e = s.end(); 17 | REQUIRE(i != e); 18 | 19 | REQUIRE(*i == Coordinate { 0, 0 }); 20 | 21 | ++i; 22 | REQUIRE(*i == Coordinate { 0, 1 }); 23 | 24 | ++i; 25 | REQUIRE(*i == Coordinate { 0, 2 }); 26 | 27 | ++i; 28 | REQUIRE(*i == Coordinate { 1, 0 }); 29 | 30 | ++i; 31 | REQUIRE(*i == Coordinate { 1, 1 }); 32 | 33 | ++i; 34 | REQUIRE(*i == Coordinate { 1, 2 }); 35 | 36 | ++i; 37 | REQUIRE(i == e); 38 | } 39 | -------------------------------------------------------------------------------- /src/vtbackend/InputHandler.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #pragma once 3 | 4 | #include 5 | 6 | #include 7 | 8 | namespace vtbackend 9 | { 10 | 11 | struct HandledTag 12 | { 13 | }; 14 | using Handled = boxed::boxed; 15 | 16 | /** 17 | * Generic input handler interface. 18 | * 19 | * @see ViInputHandler 20 | * @see Terminal 21 | */ 22 | class InputHandler 23 | { 24 | public: 25 | virtual ~InputHandler() = default; 26 | virtual Handled sendKeyPressEvent(Key key, Modifiers modifiers, KeyboardEventType eventType) = 0; 27 | virtual Handled sendCharPressEvent(char32_t codepoint, 28 | Modifiers modifiers, 29 | KeyboardEventType eventType) = 0; 30 | }; 31 | 32 | } // namespace vtbackend 33 | -------------------------------------------------------------------------------- /scripts/check-common.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | set -e 4 | 5 | ErrorCount=0 6 | 7 | ## libunicode version match 8 | 9 | LIBUNICODE_SHA_PS=$(awk 'match ($0, /libunicode_git_sha="[0-9a-f]+/) { print(substr($0, RSTART+20, RLENGTH-20)); }' scripts/install-deps.ps1) 10 | LIBUNICODE_SHA_SH=$(awk 'match ($0, /libunicode_git_sha="[0-9a-f]+/) { print(substr($0, RSTART+20, RLENGTH-20)); }' scripts/install-deps.sh) 11 | 12 | if [[ "${LIBUNICODE_SHA_SH}" != "${LIBUNICODE_SHA_PS}" ]]; then 13 | echo 1>&2 "Error: libunicode version seems to mismatch between the two install-deps scripts." 14 | echo 1>&2 "libunicode sha (PowerShell) : ${LIBUNICODE_SHA_SH}" 15 | echo 1>&2 "libunicode sha (bash) : ${LIBUNICODE_SHA_PS}" 16 | ErrorCount=$[ErrorCount + 1] 17 | fi 18 | 19 | if [[ $ErrorCount -ne 0 ]]; then 20 | exit 1 21 | fi 22 | 23 | echo "Seems all OK" 24 | -------------------------------------------------------------------------------- /.github/static/DockerUbuntu: -------------------------------------------------------------------------------- 1 | ARG VERSION="24.04" 2 | FROM yaraslaut/static_qt:$VERSION 3 | #FROM qt_static:$VERSION 4 | ARG ARCH="x86_64" 5 | 6 | RUN apt-get update 7 | RUN apt-get install -y xutils-dev pkg-config binutils gettext autoconf libtool make git 8 | 9 | WORKDIR / 10 | COPY . /contour 11 | RUN LD_LIBRARY_PATH=/usr/local/lib CC=clang CXX=clang++ sh /contour/scripts/install-static.sh 12 | 13 | WORKDIR /contour 14 | RUN LD_LIBRARY_PATH=/usr/local/lib cmake -S . -B build -G Ninja \ 15 | -D CONTOUR_BUILD_STATIC=ON \ 16 | -D CONTOUR_USE_CPM=ON \ 17 | -D CONTOUR_WITH_UTEMPTER=OFF \ 18 | -D CMAKE_CXX_COMPILER=clang++-18 \ 19 | -D CMAKE_C_COMPILER=clang-18 \ 20 | -D CMAKE_CXX_FLAGS="-I/contour/build/_deps/yaml-cpp-src/include" \ 21 | -D CMAKE_PREFIX_PATH=/usr/local/Qt-6.7.1/lib/cmake 22 | RUN cmake --build build --verbose 23 | RUN ldd ./build/src/contour/contour 24 | -------------------------------------------------------------------------------- /src/vtbackend/Cursor.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #pragma once 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace vtbackend 10 | { 11 | 12 | /// Terminal cursor data structure. 13 | /// 14 | /// NB: Take care what to store here, as DECSC/DECRC will save/restore this struct. 15 | struct Cursor 16 | { 17 | CellLocation position { LineOffset(0), ColumnOffset(0) }; 18 | bool autoWrap = true; // false; 19 | bool originMode = false; 20 | bool wrapPending = false; 21 | GraphicsAttributes graphicsRendition {}; 22 | CharsetMapping charsets {}; 23 | HyperlinkId hyperlink {}; 24 | // TODO: selective erase attribute 25 | // TODO: SS2/SS3 states 26 | // TODO: CharacterSet for GL and GR 27 | }; 28 | 29 | } // namespace vtbackend 30 | -------------------------------------------------------------------------------- /src/contour/ContourApp.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #pragma once 3 | 4 | #include 5 | 6 | namespace contour 7 | { 8 | 9 | /// Contour TUI application. 10 | /// 11 | /// TODO: provide special installable targets in debian packageS (cmake and PPA) 12 | class ContourApp: public crispy::app 13 | { 14 | public: 15 | ContourApp(); 16 | 17 | [[nodiscard]] crispy::cli::command parameterDefinition() const override; 18 | 19 | private: 20 | int captureAction(); 21 | int listDebugTagsAction(); 22 | int parserTableAction(); 23 | int profileAction(); 24 | int terminfoAction(); 25 | int configAction(); 26 | int integrationAction(); 27 | int infoVT(); 28 | int documentationVT(); 29 | int documentationKeyMapping(); 30 | int documentationGlobalConfig(); 31 | int documentationProfileConfig(); 32 | }; 33 | 34 | } // namespace contour 35 | -------------------------------------------------------------------------------- /src/vtbackend/cell/CompactCell.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #include 3 | 4 | namespace vtbackend 5 | { 6 | 7 | std::u32string CompactCell::codepoints() const 8 | { 9 | std::u32string s; 10 | if (_codepoint) 11 | { 12 | s += _codepoint; 13 | if (_extra) 14 | { 15 | for (char32_t const cp: _extra->codepoints) 16 | { 17 | s += cp; 18 | } 19 | } 20 | } 21 | return s; 22 | } 23 | 24 | std::string CompactCell::toUtf8() const 25 | { 26 | if (!_codepoint) 27 | return {}; 28 | 29 | std::string text; 30 | text += unicode::convert_to(_codepoint); 31 | if (_extra) 32 | for (char32_t const cp: _extra->codepoints) 33 | text += unicode::convert_to(cp); 34 | return text; 35 | } 36 | 37 | } // namespace vtbackend 38 | -------------------------------------------------------------------------------- /cmake/EnableCcache.cmake: -------------------------------------------------------------------------------- 1 | # Setup ccache. 2 | # 3 | # The ccache is auto-enabled if the tool is found. 4 | # To disable set -DCCACHE=OFF option. 5 | option(ENABLE_CCACHE "Enable compiler cache (ccache) [default: ON]" ON) 6 | if(ENABLE_CCACHE) 7 | if(NOT DEFINED CMAKE_CXX_COMPILER_LAUNCHER) 8 | find_program(CCACHE ccache DOC "ccache tool path; set to OFF to disable") 9 | if(CCACHE) 10 | set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE}) 11 | if(COMMAND cotire) 12 | # Change ccache config to meet cotire requirements. 13 | set(ENV{CCACHE_SLOPPINESS} pch_defines,time_macros) 14 | endif() 15 | message(STATUS "[ccache] Enabled: ${CCACHE}") 16 | else() 17 | message(STATUS "[ccache] Disabled: Not found.") 18 | endif() 19 | endif() 20 | else() 21 | message(STATUS "[ccache] Disabled.") 22 | endif() 23 | -------------------------------------------------------------------------------- /docs/demo/input-modes.md: -------------------------------------------------------------------------------- 1 | # Input Modes 2 | 3 | Just like with the power of Vi-like modes in some editors, Contour Terminal comes 4 | with so called vi-like modes to empower the advanced user with very fast access 5 | to the screen and its history. 6 | 7 | ## Select, Yank, Paste 8 | 9 | 13 | 14 | This little videos shows how to get into normal mode (Ctrl+Shift+Space) and move to the 15 | text that is to be yanked (`y`) into the clipboard. 16 | Mind, the clipboard is being pasted with newlines being stripped off (``). 17 | 18 | Note how the statusline at the bottom is reflecting the current input mode. 19 | 20 | For more information on what motions Contour supports, please refer to its documentation [here](../input-modes.md). 21 | -------------------------------------------------------------------------------- /src/text_shaper/mock_font_locator.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | namespace text 8 | { 9 | 10 | struct font_description_and_source 11 | { 12 | font_description description; 13 | font_source source; 14 | }; 15 | 16 | /** 17 | * Font locator API implementation that requires 18 | * manual configuration. 19 | * 20 | * This should be available on all platforms. 21 | */ 22 | class mock_font_locator: public font_locator 23 | { 24 | public: 25 | [[nodiscard]] font_source_list locate(font_description const& description) override; 26 | [[nodiscard]] font_source_list all() override; 27 | [[nodiscard]] font_source_list resolve(gsl::span codepoints) override; 28 | 29 | static void configure(std::vector registry); 30 | }; 31 | 32 | } // namespace text 33 | -------------------------------------------------------------------------------- /test/DECSLRM.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | clear 4 | 5 | echo -ne "123456789012345\n" 6 | echo -ne "ABCDEFGHIJKLMNO\n" 7 | echo -ne "ABCDEFGHIJKLMNO\n" 8 | echo -ne "ABCDEFGHIJKLMNO\n" 9 | echo -ne "ABCDEFGHIJKLMNO" 10 | 11 | echo -ne "\033[?69h" # DECLRMM enable 12 | echo -ne "\033[2;8s" # DECSLRM - set left/right margin 13 | echo -ne "\033[2;4r" # DECSTBM - set top/bottom margin 14 | #sleep 1 15 | 16 | # print from inside margins 17 | echo -ne "\033[2;2H" 18 | sleep 0.5 19 | for i in `seq 1 9`; do 20 | echo -ne "${i}" 21 | sleep 0.5 22 | done 23 | 24 | # print from outside margins 25 | echo -ne "\033[3;10H" 26 | sleep 0.5 27 | for i in `seq 1 9`; do 28 | echo -ne "${i}" 29 | sleep 0.5 30 | done 31 | 32 | 33 | # cleanup 34 | echo -ne "\033[?69l" # DECLRMM disable 35 | echo -ne "\033[r" # DECSTBM - set top/bottom margin 36 | sleep 0.5 37 | 38 | echo -ne "\033[6;1H" 39 | echo "" 40 | read 41 | -------------------------------------------------------------------------------- /src/text_shaper/fontconfig_locator.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | namespace text 8 | { 9 | 10 | /** 11 | * Font locator API implementation using `fontconfig` library. 12 | * 13 | * This should be available on all platforms. 14 | * 15 | * @note on Windows, fontconfig still can NOT find user installed fonts. 16 | */ 17 | class fontconfig_locator: public font_locator 18 | { 19 | public: 20 | fontconfig_locator(); 21 | 22 | [[nodiscard]] font_source_list locate(font_description const& description) override; 23 | [[nodiscard]] font_source_list all() override; 24 | [[nodiscard]] font_source_list resolve(gsl::span codepoints) override; 25 | 26 | private: 27 | struct private_tag; 28 | std::unique_ptr _d; 29 | }; 30 | 31 | } // namespace text 32 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Contour is a young project and under active development. Thus, only the latest master build and 6 | the latest `0.x.*` at least version `0.2.0` are supported. 7 | We highly recommend using the latest stable release builds. 8 | 9 | | Version | Supported | 10 | | ------- | ------------------ | 11 | | master | :white_check_mark: | 12 | | 0.2.x | :white_check_mark: | 13 | | < 0.2.0 | :x: | 14 | 15 | ## Reporting a Vulnerability 16 | 17 | If you have found a vulnerability, please either directly contact us at Discord 18 | or go to our Github Issues page at https://github.com/contour-terminal/contour/ 19 | and create a ticket with the necessary details. 20 | 21 | We are aware that this project is by far not perfect. Reporting bugs as well as 22 | vunerabilities helps us creating a great product for all of us. 23 | 24 | Many thanks for taking part in the community. 25 | -------------------------------------------------------------------------------- /.github/static/readme: -------------------------------------------------------------------------------- 1 | To build static image we need to build qt static docker container and put it on dockerhub : 2 | ``` 3 | docker buildx build --tag qt_static:20.04 --build-arg VERSION=20.04 --build-arg ARCH=x86_64 --progress=plain -f .github/static/DockerQt --load . 4 | docker buildx build --tag qt_static:22.04 --build-arg VERSION=22.04 --progress=plain -f .github/static/DockerQt --load . 5 | docker buildx build --tag qt_static:24.04 --build-arg VERSION=24.04 --progress=plain -f .github/static/DockerQt --load . 6 | docker tag qt_static:24.04 yaraslaut/static_qt:24.04 7 | docker push yaraslaut/static_qt:24.04 8 | ``` 9 | 10 | Then we can use it to build contour inside github actions with 11 | ``` 12 | docker buildx build --tag contour_static --progress=plain -f .github/static/DockerUbuntu --load . 13 | docker create --name contour_static contour_static 14 | docker cp contour_static:/contour/build/src/contour/contour . 15 | docker container rm contour_static 16 | ``` 17 | -------------------------------------------------------------------------------- /docs/vt-extensions/buffer-capture.md: -------------------------------------------------------------------------------- 1 | # Buffer Capture 2 | 3 | The screen's text buffer can be captured via VT sequence suitable for shell integration, such as `fzf`. 4 | 5 | ## Request Syntax 6 | 7 | ``` 8 | CSI > Pl ; Pr t 9 | ``` 10 | 11 | `Pl` is `1` if the lines are to be counted logically and `0` if the lines are to be counted visually. 12 | 13 | A logical line is may be a wrapped line that spans more than one visual line, whereas a visual line 14 | always maps to exactly one line on the screen. 15 | 16 | The parameter `Pr` is the number of lines to be captured. 17 | 18 | ## Response Syntax 19 | 20 | ``` 21 | PM 314 ; ST 22 | ``` 23 | 24 | The response is may span multiple `PM` sequences. 25 | The reply will always end with a PM message with an empty `` block, denoting the end of the reply. 26 | 27 | Each `` chunk will be UTF-8 encoded of the text lines to be captured. Each line will be delimited 28 | by a newline escape sequenced (`LF`). 29 | -------------------------------------------------------------------------------- /src/crispy/logstore.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #include 3 | 4 | namespace logstore 5 | { 6 | 7 | sink::sink(bool enabled, writer wr): _enabled { enabled }, _writer { std::move(wr) } 8 | { 9 | } 10 | 11 | sink::sink(bool enabled, std::ostream& output): 12 | sink(enabled, [out = &output](std::string_view text) { 13 | *out << text; 14 | out->flush(); 15 | }) 16 | { 17 | } 18 | 19 | sink::sink(bool enabled, std::shared_ptr f): 20 | sink(enabled, [f = std::move(f)](std::string_view text) { 21 | *f << text; 22 | f->flush(); 23 | }) 24 | { 25 | } 26 | 27 | sink& sink::console() 28 | { 29 | static auto instance = sink(false, std::cout); 30 | return instance; 31 | } 32 | 33 | sink& sink::error_console() // NOLINT(readability-identifier-naming) 34 | { 35 | static auto instance = sink(true, std::cerr); 36 | return instance; 37 | } 38 | 39 | } // namespace logstore 40 | -------------------------------------------------------------------------------- /.github/fedora/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM fedora:35 AS base 2 | 3 | ARG VERSION 4 | ARG CONTOUR_VERSION=$VERSION 5 | 6 | WORKDIR /app 7 | COPY . /app 8 | 9 | # Install Git and RPM development tools 10 | RUN dnf install -y \ 11 | git gcc rpm-build rpm-devel make coreutils diffutils patch rpmdevtools desktop-file-utils 12 | 13 | # Install contour dependencies 14 | RUN SYSDEP_ASSUME_YES=ON QTVER=6 ./scripts/install-deps.sh 15 | 16 | RUN useradd -d /app builder 17 | RUN echo "builder ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers 18 | USER builder 19 | 20 | RUN sudo chown builder:builder . 21 | 22 | # Setup RPM build directories 23 | RUN mkdir -p /app/rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS} 24 | 25 | RUN cp .github/fedora/contour.spec /app/rpmbuild/SPECS/contour.spec 26 | RUN mv contour-${CONTOUR_VERSION}.tar.gz /app/rpmbuild/SOURCES/contour-${CONTOUR_VERSION}.tar.gz 27 | 28 | # Build contour 29 | RUN cd /app/rpmbuild \ 30 | && rpmbuild --define "_topdir $(pwd)" -v -ba SPECS/contour.spec 31 | -------------------------------------------------------------------------------- /src/vtparser/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #cmake_minimum_required(VERSION 3.10) 2 | #project(vtparser VERSION "0.0.0" LANGUAGES CXX) 3 | 4 | add_library(vtparser STATIC 5 | Parser.cpp 6 | Parser.h 7 | Parser-impl.h 8 | ParserEvents.h 9 | ) 10 | set_target_properties(vtparser PROPERTIES CXX_CLANG_TIDY "${CLANG_TIDY_EXE}") 11 | target_link_libraries(vtparser PUBLIC 12 | Microsoft.GSL::GSL 13 | range-v3::range-v3 14 | unicode::unicode 15 | ) 16 | target_include_directories(vtparser PUBLIC 17 | $ 18 | $ 19 | ) 20 | 21 | option(VTPARSER_TESTING "Enables building of unittests for vtparser [default: ON]" ${CONTOUR_TESTING}) 22 | if(VTPARSER_TESTING) 23 | enable_testing() 24 | add_executable(vtparser_test 25 | Parser_test.cpp 26 | ) 27 | target_link_libraries(vtparser_test vtparser Catch2::Catch2WithMain) 28 | add_test(vtparser_test ./vtparser_test) 29 | endif() 30 | -------------------------------------------------------------------------------- /src/crispy/reference.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #pragma once 3 | 4 | #include 5 | 6 | template 7 | class reference 8 | { 9 | public: 10 | constexpr explicit reference(T& _ref) noexcept: ref_ { &_ref } {} 11 | constexpr reference(std::reference_wrapper _ref) noexcept: ref_ { &_ref.get() } {} 12 | 13 | reference(reference const&) = default; 14 | reference(reference&&) noexcept = default; 15 | reference& operator=(reference const&) = default; 16 | reference& operator=(reference&&) noexcept = default; 17 | 18 | constexpr reference& operator=(T& _ref) 19 | { 20 | ref_ = _ref.ref_; 21 | return *this; 22 | } 23 | 24 | constexpr T& get() noexcept { return *ref_; } 25 | constexpr T const& get() const noexcept { return *ref_; } 26 | 27 | private: 28 | T* ref_; 29 | }; 30 | 31 | template 32 | constexpr reference mut(T& _ref) noexcept 33 | { 34 | return reference(_ref); 35 | } 36 | -------------------------------------------------------------------------------- /src/text_shaper/font.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #include 3 | 4 | #include 5 | 6 | using std::string; 7 | using std::string_view; 8 | 9 | namespace text 10 | { 11 | 12 | string font_description::toPattern() const 13 | { 14 | string m; 15 | if (weight != font_weight::normal) 16 | m = std::format(" {}", weight); 17 | if (slant != font_slant::normal) 18 | m = std::format(" {}", slant); 19 | return std::format("{}{}", familyName, m); 20 | } 21 | 22 | font_description font_description::parse(string_view pattern) 23 | { 24 | font_description fd {}; 25 | 26 | // TODO: find proper style suffix 27 | // auto const i = pattern.rfind(' '); 28 | // if (i != pattern.npos) 29 | // { 30 | // fd.familyName = pattern.substr(0, i); 31 | // fd.styleName = pattern.substr(i + 1); 32 | // } 33 | // else 34 | fd.familyName = pattern; 35 | 36 | return fd; 37 | } 38 | 39 | } // namespace text 40 | -------------------------------------------------------------------------------- /src/crispy/compose_test.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #include 3 | 4 | #include 5 | 6 | TEST_CASE("compose.simple") 7 | { 8 | auto constexpr Doubled = [](int v) { 9 | return v + v; 10 | }; 11 | auto constexpr Squared = [](int v) { 12 | return v * v; 13 | }; 14 | auto constexpr A0 = 1; 15 | auto constexpr Res = A0 >> compose(Doubled) >> compose(Squared); 16 | static_assert(Res == 4); 17 | } 18 | 19 | TEST_CASE("compose.withArgs") 20 | { 21 | auto constexpr A0 = 1; 22 | auto constexpr A1 = [](int c, int v) { 23 | return c + v; 24 | }; 25 | auto constexpr A2 = [](int c1, int c2, int v) { 26 | return c1 + c2 + v; 27 | }; 28 | auto constexpr A3 = [](int c1, int c2, int c3, int v) { 29 | return c1 + c2 + c3 + v; 30 | }; 31 | auto constexpr Res = A0 >> compose(A1, 2) >> compose(A2, 3, 4) >> compose(A3, 5, 6, 7); 32 | static_assert(28 == Res); 33 | } 34 | -------------------------------------------------------------------------------- /src/crispy/deferred.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #pragma once 3 | 4 | #include 5 | 6 | #include 7 | 8 | namespace crispy 9 | { 10 | 11 | template 12 | struct deferred // NOLINT(readability-identifier-naming) 13 | { 14 | std::optional storage; 15 | 16 | [[nodiscard]] constexpr bool is_initialized() const noexcept { return storage.has_value(); } 17 | 18 | template 19 | void initialize(Args&&... args) 20 | { 21 | Require(!storage.has_value()); 22 | storage.emplace(std::forward(args)...); 23 | } 24 | 25 | T& get() { return storage.value(); } 26 | T const& get() const { return storage.value(); } 27 | 28 | T& operator*() { return storage.value(); } 29 | T const& operator*() const { return storage.value(); } 30 | 31 | T* operator->() { return &storage.value(); } 32 | T const* operator->() const { return &storage.value(); } 33 | }; 34 | 35 | } // namespace crispy 36 | -------------------------------------------------------------------------------- /src/crispy/times_test.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #include 3 | 4 | #include 5 | 6 | TEST_CASE("times.count-simple") 7 | { 8 | using namespace crispy; 9 | std::string s; 10 | times(5) | [&]() { 11 | s += 'A'; 12 | }; 13 | REQUIRE(s == "AAAAA"); 14 | } 15 | 16 | TEST_CASE("times.count") 17 | { 18 | using namespace crispy; 19 | std::string s; 20 | times(5) | [&](auto i) { 21 | s += std::to_string(i); 22 | }; 23 | REQUIRE(s == "01234"); 24 | } 25 | 26 | TEST_CASE("times.start_count") 27 | { 28 | using namespace crispy; 29 | std::string s; 30 | times(5, 2) | [&](auto i) { 31 | s += std::to_string(i); 32 | }; 33 | REQUIRE(s == "56"); 34 | } 35 | 36 | TEST_CASE("times.start_count_step") 37 | { 38 | using namespace crispy; 39 | std::string s; 40 | times(5, 3, 2) | [&](auto i) { 41 | s += std::to_string(i); 42 | }; 43 | REQUIRE(s == "579"); 44 | } 45 | -------------------------------------------------------------------------------- /src/vtbackend/logging.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #pragma once 3 | 4 | #include 5 | 6 | namespace vtbackend 7 | { 8 | 9 | auto const inline terminalLog = logstore::category("vt.session", "Logs general terminal events."); 10 | auto const inline inputLog = logstore::category("vt.input", "Logs terminal keyboard/mouse input events."); 11 | auto const inline vtParserLog = logstore::category("vt.parser", 12 | "Logs terminal parser errors.", 13 | logstore::category::state::Enabled, 14 | logstore::category::visibility::Hidden); 15 | 16 | #if defined(LIBTERMINAL_LOG_TRACE) 17 | auto const inline vtTraceSequenceLog = logstore::category("vt.trace.sequence", "Logs terminal screen trace."); 18 | #endif 19 | 20 | auto const inline renderBufferLog = logstore::category("vt.renderbuffer", "Render Buffer Objects"); 21 | 22 | } // namespace vtbackend 23 | -------------------------------------------------------------------------------- /src/vtparser/ParserExtension.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | namespace vtbackend 8 | { 9 | 10 | /// VT parser protocol extension. 11 | /// 12 | /// Used to implemented sub-parsers. 13 | /// 14 | /// @see SixelParser 15 | class ParserExtension 16 | { 17 | public: 18 | virtual ~ParserExtension() = default; 19 | 20 | virtual void pass(char ch) = 0; 21 | virtual void finalize() = 0; 22 | }; 23 | 24 | class SimpleStringCollector: public ParserExtension 25 | { 26 | public: 27 | explicit SimpleStringCollector(std::function done): _done { std::move(done) } {} 28 | 29 | void pass(char ch) override { _data.push_back(ch); } 30 | 31 | void finalize() override 32 | { 33 | if (_done) 34 | _done(_data); 35 | _data.clear(); 36 | } 37 | 38 | private: 39 | std::string _data; 40 | std::function _done; 41 | }; 42 | 43 | } // namespace vtbackend 44 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | Describe your changes in detail 4 | 5 | ```markdown 6 | Your issue description (body) goes here 7 | ``` 8 | 9 | ## Motivation and Context 10 | 11 | Why is this change required? What problem does it solve? 12 | 13 | ```markdown 14 | 15 | ``` 16 | 17 | ## How Has This Been Tested? 18 | 19 | - [ ] Please describe how you tested your changes. 20 | - [ ] see how your change affects other areas of the code, etc. 21 | 22 | ## Checklist: 23 | 24 | Go over all the following points, and put an `x` in all the boxes that apply. 25 | 26 | If you're unsure about any of these, don't hesitate to ask. We're here to help! 27 | 28 | - [ ] I have read the [**`CONTRIBUTING`**](https://github.com/contour-terminal/contour/blob/master/docs/CONTRIBUTING.md) document in my spoken language, and understand the terms 29 | - [ ] I have updated (or added) the documentation accordingly. 30 | - [ ] I have added tests to cover my changes. 31 | - [ ] I have gone through all the steps, and have thoroughly read the instructions 32 | -------------------------------------------------------------------------------- /.github/appimage/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:24.04 2 | 3 | RUN apt-get update 4 | RUN apt-get install -y sudo clang wget 5 | 6 | WORKDIR / 7 | COPY . /contour 8 | WORKDIR /contour 9 | 10 | RUN SYSDEP_ASSUME_YES=ON sh ./scripts/install-deps.sh 11 | 12 | RUN cmake -S . -B build -G Ninja \ 13 | -D CMAKE_BUILD_TYPE=RelWithDebInfo \ 14 | -D CMAKE_CXX_COMPILER=clang++ \ 15 | -D CONTOUR_USE_CPM=ON \ 16 | -D CMAKE_INSTALL_PREFIX=/contour/AppDir 17 | 18 | RUN cmake --build build 19 | RUN cmake --build build --target install 20 | 21 | RUN sudo apt install -y binutils coreutils desktop-file-utils fakeroot fuse libgdk-pixbuf2.0-dev patchelf python3-pip python3-setuptools squashfs-tools strace util-linux zsync 22 | RUN sudo wget https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage -O /usr/local/bin/appimagetool 23 | RUN sudo chmod +x /usr/local/bin/appimagetool 24 | RUN sudo sudo pip3 install appimage-builder --break-system-packages 25 | 26 | RUN appimage-builder --recipe $PWD/.github/appimage/AppImageBuilder.yml 27 | -------------------------------------------------------------------------------- /src/vtbackend/Color_test.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #include 3 | 4 | #include 5 | 6 | using namespace vtbackend; 7 | 8 | TEST_CASE("Color.Bright", "[Color]") 9 | { 10 | Color const c = Color(BrightColor::Cyan); 11 | REQUIRE(isBrightColor(c)); 12 | REQUIRE(getBrightColor(c) == int(BrightColor::Cyan)); 13 | } 14 | 15 | TEST_CASE("Color.Indexed", "[Color]") 16 | { 17 | Color const c = Color(IndexedColor::Blue); 18 | REQUIRE(isIndexedColor(c)); 19 | REQUIRE(getIndexedColor(c) == int(IndexedColor::Blue)); 20 | } 21 | 22 | TEST_CASE("Color.RGB", "[Color]") 23 | { 24 | RGBColor const rgb0 = RGBColor { 0x12, 0x34, 0x56 }; 25 | CHECK(rgb0.red == 0x12); 26 | CHECK(rgb0.green == 0x34); 27 | CHECK(rgb0.blue == 0x56); 28 | 29 | Color const c = Color(RGBColor { 0x12, 0x34, 0x56 }); 30 | REQUIRE(isRGBColor(c)); 31 | auto const rgb = getRGBColor(c); 32 | CHECK(rgb.red == 0x12); 33 | CHECK(rgb.green == 0x34); 34 | CHECK(rgb.blue == 0x56); 35 | } 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: Contour terminal emulator feature request 4 | labels: [feature-request, enhancement] 5 | --- 6 | 7 | 13 | 14 | ## Abstract 15 | 16 | 20 | 21 | ## Motivation 22 | 23 | 26 | 27 | ## Specification 28 | 29 | 35 | -------------------------------------------------------------------------------- /scripts/ci-prepare-contour.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | set -ex 4 | 5 | BUILD_DIR=${BUILD_DIR:-build} 6 | CMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE:-RelWithDebInfo}" 7 | 8 | prepare_build_ubuntu() 9 | { 10 | cmake \ 11 | --preset linux-release \ 12 | -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" \ 13 | ${EXTRA_CMAKE_FLAGS} 14 | } 15 | 16 | main_linux() 17 | { 18 | local ID=`lsb_release --id | awk '{print $NF}'` 19 | 20 | case "${ID}" in 21 | Ubuntu) 22 | prepare_build_ubuntu 23 | ;; 24 | *) 25 | echo "No automated build configuration is available yet." 26 | ;; 27 | esac 28 | } 29 | 30 | main_darwin() 31 | { 32 | echo "No automated build configuration is available yet." 33 | } 34 | 35 | main() 36 | { 37 | case "$OSTYPE" in 38 | linux-gnu*) 39 | main_linux 40 | ;; 41 | darwin*) 42 | main_darwin 43 | ;; 44 | *) 45 | echo "OS not supported." 46 | ;; 47 | esac 48 | } 49 | 50 | main 51 | -------------------------------------------------------------------------------- /src/crispy/interpolated_string.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #pragma once 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace crispy 11 | { 12 | 13 | struct string_interpolation 14 | { 15 | std::string_view name; 16 | std::set flags; 17 | std::map attributes; 18 | 19 | bool operator==(string_interpolation const& rhs) const noexcept 20 | { 21 | return name == rhs.name && flags == rhs.flags && attributes == rhs.attributes; 22 | } 23 | 24 | bool operator!=(string_interpolation const& rhs) const noexcept { return !(*this == rhs); } 25 | }; 26 | 27 | using interpolated_string_fragment = std::variant; 28 | using interpolated_string = std::vector; 29 | 30 | string_interpolation parse_interpolation(std::string_view text); 31 | interpolated_string parse_interpolated_string(std::string_view text); 32 | 33 | } // namespace crispy 34 | -------------------------------------------------------------------------------- /src/vtbackend/Capabilities_test.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | 8 | #include 9 | 10 | using namespace std::string_view_literals; 11 | using crispy::fromHexString; 12 | 13 | TEST_CASE("Capabilities.codeFromName") 14 | { 15 | vtbackend::capabilities::StaticDatabase const tcap; 16 | auto const capName = fromHexString("62656c"sv).value(); 17 | auto const tn = tcap.codeFromName(capName); 18 | REQUIRE(tn.has_value()); 19 | CHECK(tn.value().code == 0x626c); 20 | CHECK(tn.value().hex() == "626C"); 21 | } 22 | 23 | TEST_CASE("Capabilities.get") 24 | { 25 | vtbackend::capabilities::StaticDatabase const tcap; 26 | auto const rgb = tcap.stringCapability("RGB"); 27 | REQUIRE(rgb == "8/8/8"); 28 | 29 | auto const colors = tcap.numericCapability("colors"); 30 | REQUIRE(colors == std::numeric_limits::max()); 31 | 32 | auto const bce = tcap.numericCapability("bce"); 33 | REQUIRE(bce); 34 | } 35 | -------------------------------------------------------------------------------- /docs/demo/ime.md: -------------------------------------------------------------------------------- 1 | # Input method editor (IME) 2 | 3 | 4 | An input method (or input method editor, commonly abbreviated IME) is an operating system component or program that enables users to generate characters not natively available on their input devices by using sequences of characters (or mouse operations) that are available to them. Using an input method is usually necessary for languages that have more graphemes than there are keys on the keyboard. More info can be found on [wikipedia](https://en.wikipedia.org/wiki/Input_method) and with more info how to setup it and use here [wiki.archlinux](https://wiki.archlinux.org/title/Input_method) 5 | 6 | ## Example of emoji input in contour using ibus 7 | 8 | List of emoji from [unicode](http://unicode.org/emoji/charts/full-emoji-list.html) 9 | 10 | You can insert any unicode character using ibus IME by pressing Ctrl+Shift+u (default) and then insert unicode code for the emoji or other character. 11 | 12 | ![type:video](./ime/demo_ime.mp4) 13 | 14 | ## Example of input using pinyin via ibus 15 | 16 | ![type:video](./ime/pinyin_ime.mp4) 17 | -------------------------------------------------------------------------------- /src/crispy/StackTrace.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace crispy 8 | { 9 | 10 | struct debug_info 11 | { 12 | std::string text; 13 | }; 14 | 15 | class stack_trace 16 | { 17 | public: 18 | stack_trace(); 19 | stack_trace(stack_trace&&) = default; 20 | stack_trace& operator=(stack_trace&&) = default; 21 | stack_trace(const stack_trace&) = default; 22 | stack_trace& operator=(const stack_trace&) = default; 23 | ~stack_trace() = default; 24 | 25 | [[nodiscard]] std::vector symbols() const; 26 | [[nodiscard]] size_t size() const noexcept { return _frames.size(); } 27 | [[nodiscard]] bool empty() const noexcept { return _frames.empty(); } 28 | 29 | static std::string demangleSymbol(const char* symbol); 30 | static std::vector getFrames(size_t skip = 2, size_t max = 64); 31 | static std::optional getDebugInfoForFrame(void const* frameAddress); 32 | 33 | private: 34 | std::vector _frames; 35 | }; 36 | 37 | } // namespace crispy 38 | -------------------------------------------------------------------------------- /src/vtbackend/primitives.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using std::invalid_argument; 10 | using std::string; 11 | using std::transform; 12 | 13 | namespace vtbackend 14 | { 15 | 16 | CursorShape makeCursorShape(string const& name) 17 | { 18 | auto const lowerName = [](auto const& input) -> std::string { 19 | string output; 20 | transform(begin(input), end(input), back_inserter(output), [](auto ch) { return tolower(ch); }); 21 | return output; 22 | }(name); 23 | 24 | if (lowerName == "block") 25 | return CursorShape::Block; 26 | else if (lowerName == "rectangle") 27 | return CursorShape::Rectangle; 28 | else if (lowerName == "underscore") 29 | return CursorShape::Underscore; 30 | else if (lowerName == "bar") 31 | return CursorShape::Bar; 32 | else 33 | throw invalid_argument { "Invalid cursor shape: " + name }; 34 | } 35 | 36 | } // namespace vtbackend 37 | -------------------------------------------------------------------------------- /src/contour/display/shaders/dual_kawase_up.frag: -------------------------------------------------------------------------------- 1 | in highp vec4 vColor; 2 | out highp vec4 fColor; 3 | 4 | uniform highp sampler2D u_texture; 5 | uniform highp vec2 u_viewportResolution; 6 | uniform highp vec2 u_offset; 7 | uniform highp vec2 u_halfpixel; 8 | 9 | void main() 10 | { 11 | highp vec2 uv = vec2(gl_FragCoord.xy / u_viewportResolution); 12 | 13 | highp vec4 sum = texture(u_texture, uv + vec2(-u_halfpixel.x * 2.0, 0.0) * u_offset); 14 | sum += texture(u_texture, uv + vec2(-u_halfpixel.x, u_halfpixel.y) * u_offset) * 2.0; 15 | sum += texture(u_texture, uv + vec2(0.0, u_halfpixel.y * 2.0) * u_offset); 16 | sum += texture(u_texture, uv + vec2(u_halfpixel.x, u_halfpixel.y) * u_offset) * 2.0; 17 | sum += texture(u_texture, uv + vec2(u_halfpixel.x * 2.0, 0.0) * u_offset); 18 | sum += texture(u_texture, uv + vec2(u_halfpixel.x, -u_halfpixel.y) * u_offset) * 2.0; 19 | sum += texture(u_texture, uv + vec2(0.0, -u_halfpixel.y * 2.0) * u_offset); 20 | sum += texture(u_texture, uv + vec2(-u_halfpixel.x, -u_halfpixel.y) * u_offset) * 2.0; 21 | 22 | fColor = sum / 12.0; 23 | } 24 | -------------------------------------------------------------------------------- /scripts/ci-set-vars.ps1: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env powershell 2 | 3 | $VERSION = (Select-Xml -Path .\metainfo.xml -XPath '/component/releases/release[1]/@version').Node.Value 4 | 5 | if ("${Env:GITHUB_RUN_NUMBER}" -ne "") { 6 | # Guard, just in case it's run from outside the CI (e.g. for testing). 7 | $VERSION = "${VERSION}.${Env:GITHUB_RUN_NUMBER}" 8 | } 9 | 10 | switch -Regex ($Env:GITHUB_HEAD_REF) { 11 | "^release$" { 12 | $IS_PRE = 'false'; 13 | $SUFFIX = ''; 14 | $DELIM = ''; 15 | } 16 | Default { 17 | $IS_PRE = 'true'; 18 | $SUFFIX = "prerelease"; 19 | $DELIM = "-"; 20 | } 21 | } 22 | 23 | $VERSION_STRING = "${VERSION}${DELIM}${SUFFIX}" 24 | 25 | Set-Content -Path "version.txt" -Value "${VERSION_STRING}" 26 | echo "version=${VERSION}" >> "${Env:GITHUB_OUTPUT}" 27 | echo "VERSION_STRING=${VERSION_STRING}" >> "${Env:GITHUB_OUTPUT}" 28 | echo "RUN_ID=$Env:GITHUB_RUN_NUMBER" >> "${Env:GITHUB_OUTPUT}" 29 | echo "IS_PRERELEASE=$IS_PRE" >> "${Env:GITHUB_OUTPUT}" 30 | echo "RELEASENAME_SUFFIX=$SUFFIX" >> "${Env:GITHUB_OUTPUT}" 31 | 32 | # debug prints 33 | Get-Content -Path "${Env:GITHUB_OUTPUT}" 34 | -------------------------------------------------------------------------------- /src/text_shaper/font_locator_provider.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | namespace text 11 | { 12 | 13 | using std::make_unique; 14 | 15 | font_locator_provider& font_locator_provider::get() 16 | { 17 | auto static instance = font_locator_provider {}; 18 | return instance; 19 | } 20 | 21 | font_locator& font_locator_provider::native() 22 | { 23 | if (!_native) 24 | { 25 | #if defined(__APPLE__) 26 | _native = make_unique(); 27 | #elif defined(_WIN32) 28 | _native = make_unique(); 29 | #else 30 | _native = make_unique(); 31 | #endif 32 | } 33 | 34 | return *_native; 35 | } 36 | 37 | font_locator& font_locator_provider::mock() 38 | { 39 | if (!_mock) 40 | _mock = make_unique(); 41 | 42 | return *_mock; 43 | } 44 | 45 | } // namespace text 46 | -------------------------------------------------------------------------------- /src/crispy/sort_test.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | 8 | using namespace std; 9 | 10 | TEST_CASE("sort.six") 11 | { 12 | std::array a = { 1, 2, 3, 6, 5, 4 }; 13 | crispy::sort(a); 14 | REQUIRE(a == array { 1, 2, 3, 4, 5, 6 }); 15 | } 16 | 17 | TEST_CASE("sort.zero") 18 | { 19 | std::array a = {}; 20 | crispy::sort(a); 21 | REQUIRE(a == array {}); 22 | } 23 | 24 | TEST_CASE("sort.one") 25 | { 26 | std::array a = { 3 }; 27 | crispy::sort(a); 28 | REQUIRE(a == array { 3 }); 29 | } 30 | 31 | TEST_CASE("sort.two") 32 | { 33 | std::array a = { 2, 1 }; 34 | crispy::sort(a); 35 | REQUIRE(a == array { 1, 2 }); 36 | } 37 | 38 | TEST_CASE("sort.reverse") 39 | { 40 | array a = { 6, 5, 4, 3, 2, 1, 0 }; 41 | crispy::sort(a); 42 | REQUIRE(a == array { 0, 1, 2, 3, 4, 5, 6 }); 43 | } 44 | 45 | TEST_CASE("sort.ordered") 46 | { 47 | array a = { 0, 1, 2, 3, 4, 5, 6 }; 48 | crispy::sort(a); 49 | REQUIRE(a == array { 0, 1, 2, 3, 4, 5, 6 }); 50 | } 51 | -------------------------------------------------------------------------------- /scripts/ci/Xvfb-contour-run.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # 3 | # Usage: Xvfb-contour-run.sh 4 | 5 | set -x 6 | 7 | LIBGL_ALWAYS_SOFTWARE="${LIBGL_ALWAYS_SOFTWARE:-true}" 8 | CONTOUR_BIN=${CONTOUR_BIN:-contour} 9 | LOG="error,config,pty,gui.session,gui.display,vt.renderer,font.locator" # "all" 10 | LOG="all" 11 | DISPLAY=:99 12 | #CONTOUR_PREFIX=gdb --batch --command=./scripts/test.gdb --args 13 | 14 | DUMP_DIR="${1}" 15 | shift 16 | 17 | export LIBGL_ALWAYS_SOFTWARE 18 | 19 | if [[ ! ~/.terminfo ]] && [[ -d ./build/src/contour/terminfo ]] 20 | then 21 | ln -sf ./build/src/contour/terminfo ~/.terminfo 22 | fi 23 | 24 | Xvfb $DISPLAY -screen 0 1280x1024x24 & 25 | XVFB_PID=$! 26 | trap "kill $XVFB_PID" EXIT 27 | 28 | sleep 3 29 | 30 | ldd `which $CONTOUR_BIN` 31 | 32 | export CONTOUR_SYNC_PTY_OUTPUT=1 33 | 34 | $CONTOUR_PREFIX \ 35 | $CONTOUR_BIN terminal \ 36 | debug "$LOG" \ 37 | display ${DISPLAY} \ 38 | early-exit-threshold 0 \ 39 | ${@} 40 | 41 | # ~/opt/notcurses/bin/notcurses-demo -p ~/opt/notcurses/share/notcurses 42 | 43 | if [[ "$GITHUB_OUTPUT" != "" ]]; then 44 | echo "exitCode=$?" >> "$GITHUB_OUTPUT" 45 | fi 46 | -------------------------------------------------------------------------------- /src/vtbackend/RenderBuffer.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | namespace vtbackend 8 | { 9 | 10 | bool RenderDoubleBuffer::swapBuffers(std::chrono::steady_clock::time_point now) noexcept 11 | { 12 | // If the terminal thread (writer) cannot try_lock (w/o wait time) 13 | // the front buffer, it'll just flush back buffer instead of swapping 14 | // buffers as the front buffer is apparently still in use by the 15 | // renderer thread and we want to avoid render-thread imposed 16 | // wait times in the terminal thread as much as possible. 17 | 18 | if (!readerLock.try_lock()) 19 | return false; 20 | 21 | auto const _ = std::lock_guard(readerLock, std::adopt_lock); 22 | 23 | for (;;) 24 | { 25 | auto current = currentBackBufferIndex.load(); 26 | if (currentBackBufferIndex.compare_exchange_weak(current, (current + 1) % 2)) 27 | break; 28 | } 29 | 30 | lastUpdate = now; 31 | state = RenderBufferState::WaitingForRefresh; 32 | return true; 33 | } 34 | 35 | } // namespace vtbackend 36 | -------------------------------------------------------------------------------- /cmake/presets/common.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 6, 3 | "configurePresets": [ 4 | { 5 | "name": "contour-common", 6 | "hidden": true, 7 | "description": "Common settings for all configurations", 8 | "binaryDir": "${sourceDir}/build/${presetName}", 9 | "cacheVariables": { 10 | "CONTOUR_INSTALL_TOOLS": "ON", 11 | "CONTOUR_TESTING": "ON", 12 | "LIBTERMINAL_BUILD_BENCH_HEADLESS": "ON", 13 | "LIBUNICODE_TESTING": "OFF", 14 | "PEDANTIC_COMPILER": "ON", 15 | "PEDANTIC_COMPILER_WERROR": "ON" 16 | } 17 | }, 18 | { 19 | "name": "release", 20 | "hidden": true, 21 | "cacheVariables": { 22 | "CMAKE_BUILD_TYPE": "RelWithDebInfo" 23 | } 24 | }, 25 | { 26 | "name": "debug", 27 | "hidden": true, 28 | "cacheVariables": { 29 | "CMAKE_BUILD_TYPE": "Debug", 30 | "CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}" 31 | } 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /src/vtbackend/JumpHistory.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #pragma once 3 | 4 | #include 5 | 6 | #include 7 | 8 | namespace vtbackend 9 | { 10 | 11 | class JumpHistory 12 | { 13 | 14 | public: 15 | template 16 | void add(T&& cell) 17 | { 18 | applyOffset(); 19 | _history.push_back(std::forward(cell)); 20 | } 21 | CellLocation jumpToLast(CellLocation current); 22 | CellLocation jumpToMarkBackward(CellLocation current); 23 | CellLocation jumpToMarkForward(CellLocation current); 24 | void addOffset(LineOffset offset) { _offsetSinceLastJump += offset; } 25 | 26 | private: 27 | std::vector _history; 28 | size_t _current = 0; 29 | LineOffset _offsetSinceLastJump { 0 }; 30 | void applyOffset() 31 | { 32 | if (unbox(_offsetSinceLastJump) == 0) 33 | return; 34 | for (auto& cell: _history) 35 | { 36 | // minus since we are going in the history 37 | cell.line -= _offsetSinceLastJump; 38 | } 39 | _offsetSinceLastJump = LineOffset { 0 }; 40 | } 41 | }; 42 | } // namespace vtbackend 43 | -------------------------------------------------------------------------------- /scripts/ci-install-run-deps.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | set -ex 4 | 5 | # Installs dependencies for running Contour GUI executable on CI 6 | 7 | sudo apt install -y \ 8 | \ 9 | libfontconfig1 \ 10 | libfreetype6 \ 11 | libharfbuzz0b \ 12 | libssh-4 \ 13 | libyaml-cpp0.8 \ 14 | \ 15 | libqt6core5compat6 \ 16 | libqt6gui6t64 \ 17 | libqt6multimedia6 \ 18 | libqt6multimediaquick6 \ 19 | libqt6multimediawidgets6 \ 20 | libqt6network6t64 \ 21 | libqt6opengl6t64 \ 22 | libqt6openglwidgets6t64 \ 23 | libqt6qml6 \ 24 | libqt6qmlcompiler6 \ 25 | libqt6qmlcore6 \ 26 | libqt6qmllocalstorage6 \ 27 | libqt6qmlmodels6 \ 28 | libqt6qmlworkerscript6 \ 29 | libqt6qmlxmllistmodel6 \ 30 | libqt6quick6 \ 31 | libqt6quickcontrols2-6 \ 32 | libqt6quickcontrols2impl6 \ 33 | libqt6quickdialogs2-6 \ 34 | libqt6quickdialogs2quickimpl6 \ 35 | libqt6quickdialogs2utils6 \ 36 | libqt6quicklayouts6 \ 37 | libqt6quickparticles6 \ 38 | libqt6quickshapes6 \ 39 | libqt6quicktemplates2-6 \ 40 | libqt6quicktest6 \ 41 | libqt6quickwidgets6 \ 42 | libqt6shadertools6 \ 43 | \ 44 | ncurses-bin ${@} 45 | -------------------------------------------------------------------------------- /src/crispy/defines.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #pragma once 3 | 4 | #ifdef __has_include 5 | #if __has_include() 6 | #include 7 | #endif 8 | #endif 9 | 10 | #if defined(__GNUC__) || defined(__clang__) 11 | #define CRISPY_PACKED __attribute__((packed)) 12 | #else 13 | #define CRISPY_PACKED /*!*/ 14 | #endif 15 | 16 | #if (defined(__cpp_concepts) && __cpp_concepts >= 201500L) \ 17 | && (defined(__cpp_lib_concepts) && __cpp_lib_concepts >= 202002L) 18 | #define CRISPY_CONCEPTS_SUPPORTED 19 | #define CRISPY_REQUIRES(x) requires x 20 | #else 21 | #define CRISPY_REQUIRES(x) /*!*/ 22 | #endif 23 | 24 | #if (defined(__cpp_consteval) && __cpp_consteval >= 201811L) 25 | #define CRISPY_CONSTEVAL consteval 26 | #else 27 | #define CRISPY_CONSTEVAL constexpr 28 | #endif 29 | 30 | // Use this only when constexpr std algorithm is not supported but we still 31 | // wanna mark function constexpr using the std algorithms in their body 32 | #if (defined(__cpp_lib_constexpr_algorithms) && __cpp_lib_constexpr_algorithms >= 201806L) 33 | #define CRISPY_CONSTEXPR constexpr 34 | #else 35 | #define CRISPY_CONSTEXPR /**/ 36 | #endif 37 | -------------------------------------------------------------------------------- /src/vtbackend/Metrics.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #pragma once 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace vtbackend 9 | { 10 | 11 | /// Used for collecting VT sequence usage metrics. 12 | struct Metrics 13 | { 14 | // XXX Too bad the key is a string. 15 | std::map sequences; 16 | 17 | void operator()(Sequence const& seq) { sequences[seq.text()]++; } 18 | 19 | /// @returns an ordered list of collected metrics, with highest frequencey first. 20 | [[nodiscard]] std::vector> ordered() const 21 | { 22 | std::vector> vec; 23 | vec.reserve(sequences.size()); 24 | for (auto const& [name, freq]: sequences) 25 | vec.emplace_back(name, freq); 26 | 27 | std::sort(vec.begin(), vec.end(), [](auto const& a, auto const& b) { 28 | if (a.second > b.second) 29 | return true; 30 | if (a.second == b.second) 31 | return a.first > b.first; 32 | return false; 33 | }); 34 | return vec; 35 | } 36 | }; 37 | 38 | } // namespace vtbackend 39 | -------------------------------------------------------------------------------- /src/vtrasterizer/utils.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #pragma once 3 | 4 | #include 5 | 6 | #include 7 | 8 | #include 9 | 10 | namespace vtrasterizer 11 | { 12 | 13 | auto const inline rendererLog = 14 | logstore::category("vt.renderer", "Logs general information about VT renderer."); 15 | auto const inline rasterizerLog = logstore::category("vt.rasterizer", "Logs details about text rendering."); 16 | 17 | std::vector downsampleRGBA(std::vector const& bitmap, 18 | vtbackend::ImageSize size, 19 | vtbackend::ImageSize newSize); 20 | 21 | std::vector downsample(std::vector const& sourceBitmap, 22 | vtbackend::ImageSize targetSize, 23 | uint8_t factor); 24 | 25 | std::vector downsample(std::vector const& bitmap, 26 | uint8_t numComponents, 27 | vtbackend::ImageSize size, 28 | vtbackend::ImageSize newSize); 29 | 30 | } // namespace vtrasterizer 31 | -------------------------------------------------------------------------------- /src/crispy/utils.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #if defined(_WIN32) 4 | #define WIN32_LEAN_AND_MEAN 5 | #include 6 | #elif defined(__OpenBSD__) 7 | #include 8 | #define pthread_getname_np pthread_get_name_np 9 | #else 10 | #include 11 | #endif 12 | 13 | #include 14 | 15 | namespace crispy 16 | { 17 | 18 | using namespace std::string_literals; 19 | 20 | std::string threadName() 21 | { 22 | #if defined(_WIN32) 23 | auto const ThreadHandle = GetCurrentThread(); 24 | PWSTR pwsz = nullptr; 25 | HRESULT hr = GetThreadDescription(ThreadHandle, &pwsz); 26 | if (SUCCEEDED(hr)) 27 | { 28 | int const len = WideCharToMultiByte(CP_UTF8, 0, pwsz, -1, nullptr, 0, nullptr, nullptr); 29 | std::string utf8Str(len, '\0'); 30 | WideCharToMultiByte(CP_UTF8, 0, pwsz, -1, utf8Str.data(), len, nullptr, nullptr); 31 | utf8Str.resize(len - 1); 32 | LocalFree(pwsz); 33 | return utf8Str; 34 | } 35 | return ""s; 36 | #else 37 | char text[32] = {}; 38 | pthread_getname_np(pthread_self(), text, sizeof(text)); 39 | return text; 40 | #endif 41 | } 42 | 43 | } // namespace crispy 44 | -------------------------------------------------------------------------------- /src/contour/Audio.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 11 | #include 12 | #else 13 | #include 14 | #endif 15 | namespace contour 16 | { 17 | 18 | #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 19 | using QtAudioSink = QAudioSink; 20 | #else 21 | using QtAudioSink = QAudioOutput; 22 | #endif 23 | 24 | class Audio: public QObject 25 | { 26 | Q_OBJECT 27 | 28 | public: 29 | Audio(); 30 | ~Audio() override; 31 | signals: 32 | void play(int volume, int duration, std::vector const& notes); 33 | private slots: 34 | void handleStateChanged(QAudio::State state); 35 | void handlePlayback(int volume, int duration, std::vector const& notes); 36 | 37 | private: 38 | void fillBuffer(int volume, int duration, gsl::span notes); 39 | static std::vector createMusicalNote(double volume, int duration, int note) noexcept; 40 | 41 | QByteArray _byteArray; 42 | QBuffer _audioBuffer; 43 | QThread _soundThread; 44 | std::unique_ptr _audioSink; 45 | }; 46 | } // namespace contour 47 | -------------------------------------------------------------------------------- /src/vtbackend/GraphicsAttributes.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | namespace vtbackend 11 | { 12 | 13 | /// Character graphics rendition information. 14 | struct GraphicsAttributes 15 | { 16 | Color foregroundColor { DefaultColor() }; 17 | Color backgroundColor { DefaultColor() }; 18 | Color underlineColor { DefaultColor() }; 19 | CellFlags flags {}; 20 | 21 | [[nodiscard]] constexpr GraphicsAttributes with(CellFlags f) const noexcept 22 | { 23 | auto copy = *this; 24 | copy.flags |= f; 25 | return copy; 26 | } 27 | }; 28 | 29 | static_assert(std::is_trivially_copy_assignable_v); 30 | 31 | constexpr bool operator==(GraphicsAttributes const& a, GraphicsAttributes const& b) noexcept 32 | { 33 | return a.backgroundColor == b.backgroundColor && a.foregroundColor == b.foregroundColor 34 | && a.flags == b.flags && a.underlineColor == b.underlineColor; 35 | } 36 | 37 | constexpr bool operator!=(GraphicsAttributes const& a, GraphicsAttributes const& b) noexcept 38 | { 39 | return !(a == b); 40 | } 41 | 42 | } // namespace vtbackend 43 | -------------------------------------------------------------------------------- /src/contour/shell-integration/shell-integration.zsh: -------------------------------------------------------------------------------- 1 | # vim:et:ts=4:sw=4 2 | # 3 | #// SPDX-License-Identifier: Apache-2.0 4 | 5 | # Example hook to change profile based on directory. 6 | # update_profile() 7 | # { 8 | # case "$PWD" in 9 | # "$HOME"/work*) contour set profile to work ;; 10 | # "$HOME"/projects*) contour set profile to main ;; 11 | # *) contour set profile to mobile ;; 12 | # esac 13 | # } 14 | 15 | autoload -Uz add-zsh-hook 16 | 17 | precmd_hook_contour() 18 | { 19 | # Disable text reflow for the command prompt (and below). 20 | print -n '\e[?2028l' >$TTY 21 | 22 | # Marks the current line (command prompt) so that you can jump to it via key bindings. 23 | echo -n '\e[>M' >$TTY 24 | 25 | # Informs contour terminal about the current working directory, so that e.g. OpenFileManager works. 26 | echo -ne '\e]7;'$(pwd)'\e\\' >$TTY 27 | 28 | # Example hook to update configuration profile based on base directory. 29 | # update_profile >$TTY 30 | } 31 | 32 | preexec_hook_contour() 33 | { 34 | # Enables text reflow for the main page area again, so that a window resize will reflow again. 35 | print -n "\e[?2028h" >$TTY 36 | } 37 | 38 | add-zsh-hook precmd precmd_hook_contour 39 | add-zsh-hook preexec preexec_hook_contour 40 | -------------------------------------------------------------------------------- /src/crispy/range.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | namespace crispy 8 | { 9 | 10 | template 11 | // NOLINTNEXTLINE(readability-identifier-naming) 12 | class range 13 | { 14 | public: 15 | using iterator = Iter; 16 | using const_iterator = Iter; // std::add_const_t; 17 | 18 | range(Iter begin, Iter end): _begin { begin }, _end { end } {} 19 | 20 | [[nodiscard]] constexpr iterator begin() const { return _begin; } 21 | [[nodiscard]] constexpr iterator end() const { return _end; } 22 | [[nodiscard]] constexpr const_iterator cbegin() const { return _begin; } 23 | [[nodiscard]] constexpr const_iterator cend() const { return _end; } 24 | 25 | [[nodiscard]] constexpr size_t size() const noexcept 26 | { 27 | return static_cast(std::distance(_begin, _end)); 28 | } 29 | 30 | private: 31 | Iter _begin; 32 | Iter _end; 33 | }; 34 | 35 | template 36 | range(Iter, Iter) -> range; 37 | 38 | template 39 | auto reversed(Container&& container) 40 | { 41 | return range(std::forward(container).rbegin(), std::forward(container).rend()); 42 | } 43 | 44 | } // namespace crispy 45 | -------------------------------------------------------------------------------- /src/contour/shell-integration/shell-integration.fish: -------------------------------------------------------------------------------- 1 | # vim:et:ts=4:sw=4 2 | # 3 | #// SPDX-License-Identifier: Apache-2.0 4 | 5 | # Example hook to change profile based on directory. 6 | # function update_profile 7 | # switch "$PWD" 8 | # case "$HOME/work"* 9 | # contour set profile to work 10 | # case "$HOME/projects"* 11 | # contour set profile to main 12 | # case '*' 13 | # contour set profile to mobile 14 | # end 15 | # end 16 | 17 | 18 | function precmd_hook_contour -d "Shell Integration hook to be invoked before each prompt" -e fish_prompt 19 | # Disable text reflow for the command prompt (and below). 20 | printf '\e[?2028l' 21 | 22 | # Marks the current line (command prompt) so that you can jump to it via key bindings. 23 | #printf '\e[>M' 24 | printf "\e[>M" 25 | 26 | # Informs contour terminal about the current working directory, so that e.g. OpenFileManager works. 27 | printf "\e]7;$PWD\e\\" 28 | 29 | # Example hook to update configuration profile based on base directory. 30 | # update_profile 31 | end 32 | 33 | function preexec_hook_contour -d "Run after printing prompt" -e fish_preexec 34 | # Enables text reflow for the main page area again, so that a window resize will reflow again. 35 | printf "\e[?2028h" 36 | end 37 | -------------------------------------------------------------------------------- /src/crispy/TrieMap_test.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | TEST_CASE("trie_map.simple") 11 | { 12 | auto m = crispy::trie_map {}; 13 | 14 | m.insert("aa", 1); 15 | m.insert("aba", 2); 16 | m.insert("abb", 3); 17 | REQUIRE(m.size() == 3); 18 | 19 | auto const aa = m.search("aa"); 20 | CHECK(std::get>(aa).value == 1); 21 | 22 | auto const ab = m.search("ab"); 23 | REQUIRE(std::holds_alternative(ab)); 24 | 25 | auto const aba = m.search("aba"); 26 | REQUIRE(std::holds_alternative>(aba)); 27 | CHECK(std::get>(aba).value == 2); 28 | 29 | auto const abb = m.search("abb"); 30 | REQUIRE(std::holds_alternative>(abb)); 31 | CHECK(std::get>(abb).value == 3); 32 | 33 | auto const abz = m.search("abz"); 34 | REQUIRE(std::holds_alternative(abz)); 35 | 36 | m.clear(); 37 | REQUIRE(m.size() == 0); 38 | REQUIRE(!m.contains("aa")); 39 | REQUIRE(!m.contains("aba")); 40 | REQUIRE(!m.contains("abb")); 41 | } 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Visual Studio Code 2 | .vscode 3 | .vscode/** 4 | _deps 5 | _deps/** 6 | [bB]uild 7 | [bB]uild/** 8 | [rR]elease 9 | [rR]elease/** 10 | [tT]arget 11 | 12 | # Visual Studio 13 | .vs 14 | .vs/** 15 | out 16 | out/** 17 | CMakeSettings.json 18 | vcpkg_installed/** 19 | 20 | # VIM: code completion / IntelliSense 21 | .clangd 22 | .clangd/** 23 | .cache 24 | .cache/** 25 | compile_commands.json 26 | .vimspector.json 27 | 28 | # CMake (CTest) 29 | /Testing/ 30 | CMakeUserPresets.json 31 | 32 | # This is a workspace-local sandbox directory with test files not to be included in Git. 33 | sandbox 34 | sandbox/** 35 | 36 | # Never add any log files. 37 | *.log 38 | 39 | # Never accidentally add any screenshot files. 40 | *.vt 41 | 42 | # NVIDIA Nsight Graphics (Debugger, project files) 43 | *.nsight-gfxproj 44 | 45 | # OS X 46 | .DS_Store 47 | 48 | # Linux/KDE 49 | .directory 50 | 51 | # Python virtual environment 52 | .venv 53 | 54 | # debian: dpkg-buildpackage intermediates 55 | debian/files 56 | debian/tmp/** 57 | debian/.debhelper/** 58 | debian/contour.substvars 59 | debian/contour/** 60 | configure-stamp 61 | build-stamp 62 | install-stamp 63 | 64 | src/contour/ui/RequestPermission.qml 65 | src/contour/ui/Terminal.qml 66 | src/contour/ui/main.qml 67 | 68 | # mkdocs 69 | docs/vt-sequence/index.md 70 | 71 | version.txt 72 | -------------------------------------------------------------------------------- /src/vtbackend/JumpHistory.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #include 3 | 4 | namespace vtbackend 5 | { 6 | 7 | CellLocation JumpHistory::jumpToLast(CellLocation current) 8 | { 9 | applyOffset(); 10 | CellLocation last = _history.back(); 11 | if (last == current) 12 | { 13 | _history.pop_back(); 14 | if (_history.empty()) 15 | { 16 | return current; 17 | } 18 | last = _history.back(); 19 | } 20 | _history.pop_back(); 21 | _history.push_back(current); 22 | _current = _history.size(); 23 | return last; 24 | } 25 | 26 | CellLocation JumpHistory::jumpToMarkBackward([[maybe_unused]] CellLocation current) 27 | { 28 | applyOffset(); 29 | if (_current == 0) 30 | { 31 | // loop 32 | _current = _history.size() - 1; 33 | } 34 | else 35 | { 36 | _current--; 37 | } 38 | return _history[_current]; 39 | } 40 | 41 | CellLocation JumpHistory::jumpToMarkForward([[maybe_unused]] CellLocation current) 42 | { 43 | applyOffset(); 44 | if (_current == _history.size()) 45 | { 46 | // loop 47 | _current = 0; 48 | } 49 | else 50 | { 51 | _current++; 52 | } 53 | return _history[_current]; 54 | } 55 | 56 | } // namespace vtbackend 57 | -------------------------------------------------------------------------------- /docs/configuration/advanced/mouse.md: -------------------------------------------------------------------------------- 1 | # Advanced Mouse configuration 2 | 3 | This keyboard modifier can be used to bypass the terminal's mouse protocol, 4 | which can be used to select screen content even if the an application 5 | mouse protocol has been activated (Default: Shift). 6 | 7 | The same modifier values apply as with input modifiers (see below). 8 | 9 | bypass_mouse_protocol_modifier: Shift 10 | 11 | Modifier to be pressed in order to initiate block-selection 12 | using the left mouse button. 13 | 14 | This is usually the Control modifier, but on macOS that is not possible, 15 | so Alt or Meta would be recommended instead. 16 | 17 | Supported modifiers: 18 | 19 | - Alt 20 | - Control 21 | - Shift 22 | - Meta 23 | 24 | Default: Control 25 | 26 | mouse_block_selection_modifier: Control 27 | 28 | Selects an action to perform when a text selection has been made. 29 | 30 | Possible values are: 31 | 32 | |---------------------------|-------------------------------------------------- 33 | | None | Does nothing 34 | | CopyToClipboard | Copies the selection to the primary clipboard. 35 | | CopyToSelectionClipboard | Copies the selection to the selection clipboard. This is not supported on all platforms. 36 | 37 | Default: CopyToSelectionClipboard 38 | 39 | on_mouse_select: SelectClipboard 40 | 41 | -------------------------------------------------------------------------------- /scripts/check-release.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | set -e 4 | 5 | project_root="`dirname $0`/.." 6 | metainfo_xml="$project_root/metainfo.xml" 7 | 8 | error_count=0 9 | 10 | error() { 11 | echo 1>&2 "[RELEASE CHECK FAILED] ${*}" 12 | error_count=$[error_count + 1] 13 | } 14 | 15 | version_string=`xmllint --xpath 'string(/component/releases/release[1]/@version)' $metainfo_xml` 16 | 17 | release_date=`xmllint --xpath 'string(/component/releases/release[1]/@date)' $metainfo_xml` 18 | [[ "${release_date}" == "" ]] && error "Release date must be present." 19 | 20 | release_type=`xmllint --xpath 'string(/component/releases/release[1]/@type)' $metainfo_xml` 21 | [[ "${release_type}" = "development" ]] && error "Release type must not be 'development'" 22 | 23 | # Ensure the release date points to today 24 | year=$(echo $release_date | cut -d- -f1) 25 | month=$(echo $release_date | cut -d- -f2) 26 | day=$(echo $release_date | cut -d- -f3) 27 | [[ "$year" = $(date +%Y) ]] || error "Release year does not match the current year ($day vs $(date +%Y)." 28 | [[ "$month" = $(date +%m) ]] || error "Release month does not match the current month ($day vs $(date +%m)." 29 | [[ "$day" = $(date +%d) ]] || error "Release day does not match the current day ($day vs $(date +%d)." 30 | 31 | if [[ $error_count -ne 0 ]]; then 32 | echo 1>&2 "Please fix them." 33 | exit 1 34 | fi 35 | -------------------------------------------------------------------------------- /src/contour/display/Vertex.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class Vertex 7 | { 8 | public: 9 | Q_DECL_CONSTEXPR Vertex() = default; 10 | Q_DECL_CONSTEXPR explicit Vertex(const QVector3D& position); 11 | 12 | [[nodiscard]] Q_DECL_CONSTEXPR const QVector3D& position() const; 13 | void setPosition(const QVector3D& position); 14 | 15 | // OpenGL Helpers 16 | static constexpr int PositionTupleSize = 3; 17 | static Q_DECL_CONSTEXPR int positionOffset(); 18 | static Q_DECL_CONSTEXPR int stride(); 19 | 20 | private: 21 | QVector3D _position; 22 | }; 23 | 24 | // Constructors 25 | Q_DECL_CONSTEXPR inline Vertex::Vertex(const QVector3D& position): _position(position) 26 | { 27 | } 28 | 29 | // Accessors / Mutators 30 | Q_DECL_CONSTEXPR inline const QVector3D& Vertex::position() const 31 | { 32 | return _position; 33 | } 34 | void inline Vertex::setPosition(const QVector3D& position) 35 | { 36 | _position = position; 37 | } 38 | 39 | // OpenGL Helpers 40 | Q_DECL_CONSTEXPR inline int Vertex::positionOffset() 41 | { 42 | return offsetof(Vertex, _position); 43 | } 44 | Q_DECL_CONSTEXPR inline int Vertex::stride() 45 | { 46 | return sizeof(Vertex); 47 | } 48 | 49 | // Note: Q_MOVABLE_TYPE means it can be memcpy'd. 50 | Q_DECLARE_TYPEINFO(Vertex, Q_MOVABLE_TYPE); 51 | -------------------------------------------------------------------------------- /docs/demo/statusline.md: -------------------------------------------------------------------------------- 1 | # Statusline 2 | 3 | The Statusline in Contour is an implementation of the 4 | [DEC VT320 statusline feature](https://www.vt100.net/docs/vt320-uu/appendixe.html). 5 | We aim to revive this feature as we see great use to it. 6 | 7 | ## Indicator Statusline 8 | 9 | This is the most obvious and probably most used one. It's the statusline as 10 | you may be used to it. 11 | 12 | Contour shows relevant contextual information on it, such as: 13 | 14 | - the clock, 15 | - current VT emulation mode, 16 | - input mode 17 | 18 | and much more, depending on the context. 19 | 20 | This line can be actively toggled by the user via configuration, e.g. via: 21 | 22 | ```yaml 23 | input_mapping: 24 | - { mods: [Control, Alt], key: '.', action: ToggleStatusLine } 25 | ``` 26 | 27 | ## Host Programmable Statusline 28 | 29 | This statusline can be alternatively displayed and can be 30 | written to and fully controlled like the main display. 31 | 32 | There are not many applications yet doing so, but there would be a great use to 33 | such a feature by any application that has fast output but a rarely updating 34 | status line, maybe even tmux or screen could make use of it to trivialize 35 | the common use-case. 36 | 37 | If you consider playing around with it, have a look at the VT sequences 38 | 39 | - `DECSASD` - Select Active Status Display 40 | - `DECSSDT` - Select Status Display (Line) Type 41 | -------------------------------------------------------------------------------- /src/contour/display/ShaderConfig.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #pragma once 3 | 4 | #include 5 | 6 | #include 7 | 8 | #include 9 | 10 | #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 11 | #include 12 | 13 | #include 14 | #else 15 | #include 16 | #include 17 | #endif 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | namespace contour::display 24 | { 25 | 26 | enum class ShaderClass : uint8_t 27 | { 28 | Background, 29 | Text 30 | }; 31 | 32 | struct ShaderSource 33 | { 34 | QString location; 35 | QString contents; 36 | }; 37 | 38 | struct ShaderConfig 39 | { 40 | ShaderSource vertexShader; 41 | ShaderSource fragmentShader; 42 | }; 43 | 44 | bool useOpenGLES() noexcept; 45 | QSurfaceFormat createSurfaceFormat(); 46 | 47 | inline std::string to_string(ShaderClass shaderClass) 48 | { 49 | switch (shaderClass) 50 | { 51 | case ShaderClass::Background: return "background"; 52 | case ShaderClass::Text: return "text"; 53 | } 54 | 55 | crispy::unreachable(); 56 | } 57 | 58 | ShaderConfig builtinShaderConfig(ShaderClass shaderClass); 59 | 60 | std::unique_ptr createShader(ShaderConfig const& shaderConfig); 61 | 62 | } // namespace contour::display 63 | -------------------------------------------------------------------------------- /src/text_shaper/directwrite_shaper.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | namespace text 10 | { 11 | 12 | /** 13 | * Text shaping and rendering engine using open source technologies, 14 | * fontconfig + harfbuzz + freetype. 15 | */ 16 | class directwrite_shaper: public shaper 17 | { 18 | public: 19 | directwrite_shaper(DPI _dpi, font_locator& _locator); 20 | 21 | void set_dpi(DPI _dpi) override; 22 | void set_locator(font_locator& _locator) override; 23 | void clear_cache() override; 24 | 25 | std::optional load_font(font_description const& _description, font_size _size) override; 26 | 27 | font_metrics metrics(font_key _key) const override; 28 | 29 | void shape(font_key _font, 30 | std::u32string_view _text, 31 | gsl::span _clusters, 32 | unicode::Script _script, 33 | unicode::PresentationStyle _presentation, 34 | shape_result& _result) override; 35 | 36 | std::optional shape(font_key _font, char32_t _codepoint) override; 37 | 38 | std::optional rasterize(glyph_key _glyph, render_mode _mode) override; 39 | 40 | private: 41 | struct Private; 42 | std::unique_ptr d; 43 | }; 44 | 45 | } // namespace text 46 | -------------------------------------------------------------------------------- /src/vtrasterizer/BackgroundRenderer.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | 11 | namespace vtrasterizer 12 | { 13 | 14 | class RenderTarget; 15 | 16 | class BackgroundRenderer: public Renderable 17 | { 18 | public: 19 | /// Constructs the decoration renderer. 20 | /// 21 | /// @param gridMetrics 22 | /// @param defaultColor 23 | /// @param renderTarget 24 | BackgroundRenderer(GridMetrics const& gridMetrics, vtbackend::RGBColor const& defaultColor); 25 | 26 | void setRenderTarget(RenderTarget& renderTarget, DirectMappingAllocator& directMappingAllocator) override; 27 | 28 | constexpr void setOpacity(float value) noexcept { _opacity = static_cast(value * 255.f); } 29 | 30 | // TODO: pass background color directly (instead of whole grid cell), 31 | // because there is no need to detect bg/fg color more than once per grid cell! 32 | 33 | /// Queues up a render with given background 34 | void renderCell(vtbackend::RenderCell const& cell); 35 | 36 | void renderLine(vtbackend::RenderLine const& line); 37 | 38 | void inspect(std::ostream& output) const override; 39 | 40 | private: 41 | // private data 42 | vtbackend::RGBColor const& _defaultColor; 43 | uint8_t _opacity = 255; 44 | }; 45 | 46 | } // namespace vtrasterizer 47 | -------------------------------------------------------------------------------- /src/crispy/compose.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #pragma once 3 | #include 4 | #include 5 | 6 | /** 7 | * Provides a facility for function composition. 8 | * 9 | * Suppose you write code like this: 10 | * 11 | * auto result = h(g(f(x)), x); 12 | * 13 | * 14 | * What you can do, and what is much more readable: 15 | * 16 | * auto result = f(x) >> compose(g) >> compose(h, x); 17 | * 18 | * 19 | * @param _fun the function to be composed with. 20 | * @param _args zero or more function parameters to be curried with the given function. 21 | * The input resulting value of the left-hand-side of the function composition 22 | * will be the last parameter being passed to _fun. 23 | * 24 | * @return the result of applying all parameters to the composed function. 25 | */ 26 | template 27 | constexpr auto compose(F fun, Args... args) -> std::pair> 28 | { 29 | return std::pair { std::move(fun), std::make_tuple(std::forward(args)...) }; 30 | } 31 | 32 | /** 33 | * Function composition operator, to be used with the compose() function. 34 | */ 35 | template 36 | constexpr auto operator>>(S input, std::pair> chain) 37 | { 38 | return std::apply(chain.first, 39 | std::tuple_cat(std::move(chain.second), std::tuple { std::move(input) })); 40 | } 41 | -------------------------------------------------------------------------------- /src/crispy/interpolated_string_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | 7 | TEST_CASE("interpolated_string.parse_interpolation") 8 | { 9 | using crispy::parse_interpolation; 10 | 11 | auto const interpolation = parse_interpolation("Clock:Bold,Italic,Color=#FFFF00"); 12 | CHECK(interpolation.name == "Clock"); 13 | CHECK(interpolation.flags.size() == 2); 14 | CHECK(interpolation.flags.count("Bold")); 15 | CHECK(interpolation.flags.count("Italic") == 1); 16 | CHECK(interpolation.attributes.size() == 1); 17 | CHECK(interpolation.attributes.count("Color")); 18 | CHECK(interpolation.attributes.at("Color") == "#FFFF00"); 19 | } 20 | 21 | TEST_CASE("interpolated_string.parse_interpolated_string") 22 | { 23 | using crispy::parse_interpolated_string; 24 | 25 | auto const interpolated = parse_interpolated_string("< {Clock:Bold,Italic,Color=#FFFF00} | {VTType}"); 26 | 27 | CHECK(interpolated.size() == 4); 28 | 29 | REQUIRE(std::holds_alternative(interpolated[0])); 30 | REQUIRE(std::get(interpolated[0]) == "< "); 31 | 32 | REQUIRE(std::holds_alternative(interpolated[1])); 33 | 34 | REQUIRE(std::holds_alternative(interpolated[2])); 35 | REQUIRE(std::get(interpolated[2]) == " | "); 36 | 37 | REQUIRE(std::holds_alternative(interpolated[3])); 38 | } 39 | -------------------------------------------------------------------------------- /src/crispy/Owned.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #pragma once 3 | 4 | #include 5 | 6 | namespace crispy 7 | { 8 | 9 | /** 10 | * Owned behaves mostly like std::unique_ptr except that it can be also 11 | * used within packed structs. 12 | */ 13 | template 14 | struct CRISPY_PACKED owned 15 | { 16 | public: 17 | ~owned() { reset(); } 18 | owned() noexcept = default; 19 | owned(owned&& v) noexcept: _ptr { v.release() } {} 20 | owned& operator=(owned&& v) noexcept 21 | { 22 | _ptr = v.release(); 23 | return *this; 24 | } 25 | 26 | owned(owned const& v) noexcept = delete; 27 | owned& operator=(owned const& v) = delete; 28 | 29 | [[nodiscard]] T* get() noexcept { return _ptr; } 30 | [[nodiscard]] T const* get() const noexcept { return _ptr; } 31 | 32 | constexpr T* operator->() noexcept { return _ptr; } 33 | constexpr T const* operator->() const noexcept { return _ptr; } 34 | 35 | constexpr T& operator*() noexcept { return *_ptr; } 36 | constexpr T const& operator*() const noexcept { return *_ptr; } 37 | 38 | constexpr operator bool() const noexcept { return _ptr != nullptr; } 39 | 40 | void reset(T* p = nullptr) 41 | { 42 | delete _ptr; 43 | _ptr = p; 44 | } 45 | 46 | T* release() noexcept 47 | { 48 | auto p = _ptr; 49 | _ptr = nullptr; 50 | return p; 51 | } 52 | 53 | private: 54 | T* _ptr = nullptr; 55 | }; 56 | 57 | } // namespace crispy 58 | -------------------------------------------------------------------------------- /src/vtrasterizer/CursorRenderer.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | namespace vtrasterizer 13 | { 14 | 15 | /// Takes care of rendering the text cursor. 16 | class CursorRenderer: public Renderable 17 | { 18 | public: 19 | CursorRenderer(GridMetrics const& gridMetrics, vtbackend::CursorShape shape); 20 | 21 | void setRenderTarget(RenderTarget& renderTarget, DirectMappingAllocator& directMappingAllocator) override; 22 | void setTextureAtlas(TextureAtlas& atlas) override; 23 | 24 | void clearCache() override; 25 | 26 | [[nodiscard]] vtbackend::CursorShape shape() const noexcept { return _shape; } 27 | void setShape(vtbackend::CursorShape shape); 28 | 29 | void render(crispy::point pos, int columnWidth, vtbackend::RGBColor color); 30 | 31 | void inspect(std::ostream& output) const override; 32 | 33 | private: 34 | void initializeDirectMapping(); 35 | using Renderable::createTileData; 36 | [[nodiscard]] TextureAtlas::TileCreateData createTileData(vtbackend::CursorShape shape, 37 | int columnWidth, 38 | atlas::TileLocation tileLocation); 39 | 40 | DirectMapping _directMapping {}; 41 | vtbackend::CursorShape _shape; 42 | }; 43 | 44 | } // namespace vtrasterizer 45 | -------------------------------------------------------------------------------- /src/vtpty/ConPty.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #pragma once 3 | 4 | #include 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | namespace vtpty 15 | { 16 | 17 | /// ConPty implementation for newer Windows 10 versions. 18 | class ConPty: public Pty 19 | { 20 | public: 21 | explicit ConPty(PageSize const& windowSize); 22 | ~ConPty() override; 23 | 24 | void start() override; 25 | void close() override; 26 | void waitForClosed() override; 27 | [[nodiscard]] bool isClosed() const noexcept override; 28 | 29 | [[nodiscard]] std::optional read(crispy::buffer_object& storage, 30 | std::optional timeout, 31 | size_t size) override; 32 | void wakeupReader() override; 33 | int write(std::string_view data) override; 34 | [[nodiscard]] PageSize pageSize() const noexcept override; 35 | void resizeScreen(PageSize cells, std::optional pixels = std::nullopt) override; 36 | 37 | PtySlave& slave() noexcept override; 38 | HPCON master() const noexcept { return _master; } 39 | 40 | private: 41 | std::mutex _mutex; // used to guard close() 42 | PageSize _size; 43 | HPCON _master; 44 | HANDLE _input; 45 | HANDLE _output; 46 | std::vector _buffer; 47 | std::unique_ptr _slave; 48 | }; 49 | 50 | } // namespace vtpty 51 | -------------------------------------------------------------------------------- /src/vtpty/MockViewPty.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #pragma once 4 | 5 | #include 6 | 7 | #include 8 | 9 | namespace vtpty 10 | { 11 | 12 | class MockViewPty: public Pty 13 | { 14 | public: 15 | explicit MockViewPty(PageSize windowSize): _pageSize { windowSize } {} 16 | 17 | void setReadData(std::string_view data); 18 | 19 | PtySlave& slave() noexcept override; 20 | [[nodiscard]] std::optional read(crispy::buffer_object& storage, 21 | std::optional timeout, 22 | size_t size) override; 23 | void wakeupReader() override; 24 | int write(std::string_view data) override; 25 | [[nodiscard]] PageSize pageSize() const noexcept override; 26 | void resizeScreen(PageSize cells, std::optional pixels = std::nullopt) override; 27 | 28 | void start() override; 29 | void close() override; 30 | void waitForClosed() override; 31 | [[nodiscard]] bool isClosed() const noexcept override; 32 | 33 | [[nodiscard]] std::string& stdinBuffer() noexcept { return _inputBuffer; } 34 | [[nodiscard]] std::string_view& stdoutBuffer() noexcept { return _outputBuffer; } 35 | 36 | private: 37 | PageSize _pageSize; 38 | std::optional _pixelSize; 39 | std::string _inputBuffer; 40 | std::string_view _outputBuffer; 41 | bool _closed = false; 42 | PtySlaveDummy _slave; 43 | }; 44 | 45 | } // namespace vtpty 46 | -------------------------------------------------------------------------------- /src/contour/display/shaders/background_image.frag: -------------------------------------------------------------------------------- 1 | uniform highp mat4 u_projection; 2 | uniform lowp sampler2D u_backgroundImage; 3 | uniform highp vec2 u_viewportResolution; 4 | uniform highp vec2 u_backgroundResolution; 5 | uniform highp float u_blur; 6 | uniform highp float u_opacity; 7 | uniform highp float u_time; 8 | 9 | in highp vec2 fs_TexCoord; 10 | 11 | out highp vec4 fragColor; 12 | 13 | // {{{ Gaussian blur 14 | #define BlurSamples 128 15 | 16 | highp float gaussian(highp vec2 i) 17 | { 18 | const highp float TwoPi = 6.28318530718; 19 | const highp float Sigma = 0.25 * float(BlurSamples); 20 | const highp float SigmaSquared = Sigma * Sigma; 21 | highp vec2 iOverSigma = i / Sigma; 22 | return exp(-0.5 * dot(iOverSigma, iOverSigma)) / (TwoPi * SigmaSquared); 23 | } 24 | 25 | highp vec4 blur(highp sampler2D sp, highp vec2 uv, highp vec2 scale) 26 | { 27 | highp vec4 outputColor = vec4(0); 28 | int s = BlurSamples; 29 | 30 | for ( int i = 0; i < s*s; i++ ) { 31 | highp vec2 d = vec2(i%s, i/s) - float(BlurSamples)/2.; 32 | outputColor += gaussian(d) * texture( sp, uv + scale * d); 33 | } 34 | 35 | return outputColor / outputColor.a; 36 | } 37 | // }}} 38 | 39 | void main() 40 | { 41 | if (u_blur >= 1.0) 42 | fragColor = blur(u_backgroundImage, 43 | fs_TexCoord, 44 | 1.0 / u_backgroundResolution.xy); 45 | else 46 | fragColor = texture(u_backgroundImage, fs_TexCoord.xy); 47 | 48 | fragColor *= u_opacity; 49 | } 50 | -------------------------------------------------------------------------------- /src/text_shaper/open_shaper.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | namespace text 10 | { 11 | 12 | class font_locator; 13 | 14 | /** 15 | * Text shaping and rendering engine using open source technologies, 16 | * fontconfig + harfbuzz + freetype. 17 | */ 18 | class open_shaper: public shaper 19 | { 20 | public: 21 | explicit open_shaper(DPI dpi, font_locator& locator); 22 | 23 | void set_dpi(DPI dpi) override; 24 | 25 | void set_locator(font_locator& locator) override; 26 | 27 | void clear_cache() override; 28 | 29 | [[nodiscard]] std::optional load_font(font_description const& description, 30 | font_size size) override; 31 | 32 | [[nodiscard]] font_metrics metrics(font_key key) const override; 33 | 34 | void shape(font_key font, 35 | std::u32string_view codepoints, 36 | gsl::span clusters, 37 | unicode::Script script, 38 | unicode::PresentationStyle presentation, 39 | shape_result& result) override; 40 | 41 | [[nodiscard]] std::optional shape(font_key font, char32_t codepoint) override; 42 | 43 | [[nodiscard]] std::optional rasterize(glyph_key glyph, render_mode mode) override; 44 | 45 | private: 46 | struct private_open_shaper; 47 | std::unique_ptr _d; 48 | }; 49 | 50 | } // namespace text 51 | -------------------------------------------------------------------------------- /cmake/Modules/MacOSXBundleInfo.plist.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${MACOSX_BUNDLE_EXECUTABLE_NAME} 9 | CFBundleGetInfoString 10 | ${MACOSX_BUNDLE_INFO_STRING} 11 | CFBundleIconFile 12 | ${MACOSX_BUNDLE_ICON_FILE} 13 | CFBundleIdentifier 14 | ${MACOSX_BUNDLE_GUI_IDENTIFIER} 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleLongVersionString 18 | ${MACOSX_BUNDLE_LONG_VERSION_STRING} 19 | CFBundleName 20 | ${MACOSX_BUNDLE_BUNDLE_NAME} 21 | CFBundlePackageType 22 | APPL 23 | CFBundleShortVersionString 24 | ${MACOSX_BUNDLE_SHORT_VERSION_STRING} 25 | CFBundleSignature 26 | ???? 27 | CFBundleVersion 28 | ${MACOSX_BUNDLE_BUNDLE_VERSION} 29 | CSResourcesFileMapped 30 | 31 | NSHumanReadableCopyright 32 | ${MACOSX_BUNDLE_COPYRIGHT} 33 | NSPrincipalClass 34 | NSApplication 35 | NSHighResolutionCapable 36 | True 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/vtbackend/Functions.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | using crispy::for_each; 19 | using crispy::times; 20 | 21 | using std::accumulate; 22 | using std::array; 23 | using std::for_each; 24 | using std::pair; 25 | using std::sort; 26 | using std::string; 27 | using std::string_view; 28 | using std::stringstream; 29 | 30 | namespace vtbackend 31 | { 32 | 33 | Function const* select(FunctionSelector const& selector, 34 | gsl::span availableDefinitions) noexcept 35 | { 36 | auto a = size_t { 0 }; 37 | auto b = availableDefinitions.size() - 1; 38 | while (a <= b) 39 | { 40 | auto const i = (a + b) / 2; 41 | auto const& fui = availableDefinitions[i]; 42 | auto const rel = compare(selector, fui); 43 | // std::cout << std::format(" - a:{:>2} b:{:>2} i:{} rel:{} I: {}\n", a, b, i, rel < 0 ? '<' : rel > 0 44 | // ? '>' : '=', I); 45 | if (rel > 0) 46 | a = i + 1; 47 | else if (rel < 0) 48 | { 49 | if (i == 0) 50 | return nullptr; 51 | b = i - 1; 52 | } 53 | else 54 | return &fui; 55 | } 56 | return nullptr; 57 | } 58 | 59 | } // namespace vtbackend 60 | -------------------------------------------------------------------------------- /docs/configuration/advanced/renderer.md: -------------------------------------------------------------------------------- 1 | # Advanced Configuration: Renderer 2 | 3 | ### `renderer.backend` 4 | 5 | Currently two rendering backends are supported. `OpenGL`, the default, 6 | and `software`, which will force a fall back to a software-emulated OpenGL 7 | driver. Specifying `default` will automatically pick the default. 8 | 9 | ```yml 10 | renderer: 11 | backend: OpenGL 12 | ``` 13 | 14 | ### `renderer.tile_hashtable_slots` 15 | 16 | Defines the number of hashtable slots to map to the texture tiles. 17 | Larger values may increase performance, but too large may also decrease. 18 | This value is rounted up to a value equal to the power of two. 19 | 20 | Default: `4096` 21 | 22 | ```yml 23 | renderer: 24 | tile_hashtable_slots: 4096 25 | ``` 26 | 27 | ### `renderer.tile_cache_count` 28 | 29 | Defines the number of tiles that must fit at lest into the texture atlas. 30 | 31 | This does not include direct mapped tiles (US-ASCII glyphs, 32 | cursor shapes and decorations), if `tile_direct_mapping` is set to true). 33 | 34 | Value must be at least as large as grid cells available in the terminal view. 35 | This value is automatically adjusted if too small. 36 | 37 | Default: `4000` 38 | 39 | ```yml 40 | renderer: 41 | tile_cache_count: 4000 42 | ``` 43 | 44 | ### `renderer.tile_direct_mapping` 45 | 46 | Enables/disables the use of direct-mapped texture atlas tiles for 47 | the most often used ones (US-ASCII, cursor shapes, underline styles) 48 | 49 | You most likely do not want to touch this and leave it enabled. 50 | 51 | Default: true 52 | 53 | ```yml 54 | renderer: 55 | tile_direct_mapping: true 56 | ``` 57 | -------------------------------------------------------------------------------- /docs/demo/line-marks.md: -------------------------------------------------------------------------------- 1 | # Line Marks 2 | 3 | Ever wanted to jump quickly to the top of the previous prompt? 4 | With a little bit of shell integration, you can make the shell tell 5 | the terminal which lines in your screen and scrollback buffer to remember. 6 | 7 | ## Setting a line mark 8 | 9 | This is what a shell integration would do, but you can simply 10 | mark lines yourself by trivially writing to `stdout`, as follows: 11 | 12 | ```sh 13 | printf "\033[>M" 14 | ``` 15 | 16 | This will tell the terminal to remember the line as a jump-target 17 | 18 | ## Jump via shortcut 19 | 20 | Ensure you have a similar configuration set as follows to relatively jump 21 | up or down of your marked lines. 22 | 23 | ```yaml 24 | input_mapping: 25 | - { mods: [Control, Alt], key: K, action: ScrollMarkUp, mode: "~Alt" } 26 | - { mods: [Control, Alt], key: J, action: ScrollMarkDown, mode: "~Alt" } 27 | ``` 28 | 29 | ## Jump extension to Vi-like Normal Mode 30 | 31 | Use `[m` and `]m` to jump the the next line mark up and down when being in 32 | normal input mode. 33 | 34 | Please see [Input Modes](../input-modes.md) for more information. 35 | 36 | ## Line marks as text objects 37 | 38 | In Vi-like normal mode, you can span a text object in between two line marks, 39 | as follows: 40 | 41 | - `vim` - visual select within two line marks (excluding marked lines) 42 | - `vam` - visual select around two line marks (including marked line) 43 | - `yim` - yank within two line marks (excluding marked lines) 44 | - `yam` - yank around two line marks (including marked line) 45 | 46 | Please see [Input Modes](../input-modes.md) for more information. 47 | -------------------------------------------------------------------------------- /docs/drafts/daemon-mode.md: -------------------------------------------------------------------------------- 1 | # Contour Daemon Mode 2 | 3 | THIS IS A DRAFT DOCUMENT 4 | 5 | Let's implement a terminal multiplexing server that `Contour` can connect to. 6 | 7 | ## Requirements 8 | 9 | - support for handling multiple sessions 10 | - support concurrent clients to the same sessions 11 | - a session has one or more terminals, with a server managed layout (stacked, tabbed, tiled, ...) 12 | - connected clients to the same terminal can independently scroll in history. 13 | - communication via `AF_UNIX` and `AF_INET`. 14 | - attached clients to a terminal receive: 15 | - An initial fullscreen redraw event, 16 | - and then incremental updates via VT sequences, 17 | - A fullscreen redraw can be requested at any time. 18 | - history data can be requested on demand by the client 19 | - history-clear events can be pushed to all clients by the server 20 | - any necessary resources (such as image data) will be sent on-demand to the client. 21 | 22 | ## Server Design Thoughts 23 | 24 | - standalone executable only linking to the absolutely necessary libraries (such as libterminal, but not Qt) 25 | - should support binary upgrade 26 | - server protocol must be specced (see below?) 27 | 28 | ... 29 | 30 | ## Client Design Thoughts 31 | 32 | - have specialized `Terminal` class with a networked `Pty` that 33 | - sends input (keyboard/mouse) events via network 34 | - receives screen update events from remote BUT maintains local screen buffer for fast redraws 35 | 36 | ... 37 | 38 | ## Terminal Multiplexing Protocol 39 | 40 | - either text based for easy adoption or protobuf-based (or alike) for optimal performance (or both?) 41 | 42 | TODO 43 | 44 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml.disabled: -------------------------------------------------------------------------------- 1 | name: "Code scanning" 2 | 3 | on: 4 | push: 5 | paths-ignore: 6 | - 'docs/**' 7 | - '.github/ISSUE_TEMPLATE/**' 8 | - '.github/*.yml' 9 | - 'LICENSE.txt' 10 | - '*.md' 11 | - '*.sh' 12 | branches: 13 | - master 14 | pull_request: 15 | branches: 16 | - master 17 | 18 | # Attempt to cancel any in-progress jobs for a given PR. 19 | concurrency: 20 | group: codeql-${{ github.ref }} 21 | cancel-in-progress: true 22 | 23 | jobs: 24 | CodeQL-Build: 25 | runs-on: ubuntu-latest 26 | 27 | steps: 28 | - name: Checkout repository 29 | uses: actions/checkout@v3 30 | with: 31 | # We must fetch at least the immediate parents so that if this is 32 | # a pull request then we can checkout the head. 33 | fetch-depth: 2 34 | 35 | # Initializes the CodeQL tools for scanning. 36 | - name: Initialize CodeQL 37 | uses: github/codeql-action/init@v1 38 | with: 39 | languages: cpp 40 | 41 | - name: apt update 42 | run: sudo apt -q update 43 | - name: install dependencies 44 | run: ./scripts/install-deps.sh 45 | - name: "create build directory" 46 | run: mkdir build 47 | - name: "cmake" 48 | run: cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -S . -B build 49 | - name: "build" 50 | run: cmake --build build/ -- -j3 51 | - name: "test: libcrispy" 52 | run: ./build/src/crispy/crispy_test 53 | - name: "test: libterminal" 54 | run: ./build/src/vtbackend/vtbackend_test 55 | 56 | - name: Perform CodeQL Analysis 57 | uses: github/codeql-action/analyze@v1 58 | -------------------------------------------------------------------------------- /docs/configuration/advanced/misc.md: -------------------------------------------------------------------------------- 1 | # Miscelenous Advanced Configuration Options 2 | 3 | ## Platform plugin 4 | 5 | Overrides the auto-detected platform plugin to be loaded. 6 | 7 | Possible (incomplete list of) values are: 8 | 9 | - auto The platform will be auto-detected. 10 | - xcb Uses XCB plugin (for X11 environment). 11 | - cocoa Used to be run on macOS. 12 | - direct2d Windows platform plugin using Direct2D. 13 | - winrt Windows platform plugin using WinRT. 14 | 15 | Default: auto 16 | 17 | platform_plugin: auto 18 | 19 | 20 | ## Default PTY read buffer size 21 | 22 | This is an advance option. Use with care! 23 | Default: 16384 24 | 25 | read_buffer_size: 16384 26 | 27 | 28 | ## New-Terminal spawn behaviour 29 | 30 | This flag determines whether to spawn new process or not when creating new terminal 31 | 32 | If this option is set to `false`, then simply a new terminal window is being 33 | created rather thena fully creating a new process. 34 | 35 | Default: `false` 36 | 37 | spawn_new_process: false 38 | 39 | # Text reflow on resize 40 | 41 | Whether or not to reflow the lines on terminal resize events. 42 | 43 | Default: `true` 44 | 45 | reflow_on_resize: true 46 | 47 | # Backspace character 48 | 49 | There is little consistency between systems as to what should be sent when the 50 | user presses the backspace key. By default Contour sends ^? but if you prefer 51 | to send ^H (or something else again) then you can add an entry to the 52 | `input_mapping` section of your config file like: 53 | 54 | ``` 55 | - { mods: [], key: BackSpace, action: SendChars, chars: "\x08" } 56 | - { mods: [Control], key: BackSpace, action: SendChars, chars: "\x7F" } 57 | ``` 58 | -------------------------------------------------------------------------------- /src/vtrasterizer/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(_header_files 2 | BackgroundRenderer.h 3 | BoxDrawingRenderer.h 4 | CursorRenderer.h 5 | DecorationRenderer.h 6 | GridMetrics.h 7 | ImageRenderer.h 8 | Pixmap.h 9 | RenderTarget.h 10 | Renderer.h 11 | TextClusterGrouper.h 12 | TextRenderer.h 13 | TextureAtlas.h 14 | utils.h 15 | ) 16 | 17 | set(_source_files 18 | BackgroundRenderer.cpp 19 | BoxDrawingRenderer.cpp 20 | CursorRenderer.cpp 21 | DecorationRenderer.cpp 22 | ImageRenderer.cpp 23 | Pixmap.cpp 24 | RenderTarget.cpp 25 | Renderer.cpp 26 | TextClusterGrouper.cpp 27 | TextRenderer.cpp 28 | utils.cpp 29 | ) 30 | 31 | set(_test_files 32 | TextClusterGrouper_test.cpp 33 | ) 34 | 35 | source_group(Sources FILES ${_source_files}) 36 | source_group(Headers FILES ${_header_files}) 37 | source_group(Tests FILES ${_test_files}) 38 | 39 | add_library(vtrasterizer STATIC) 40 | target_sources(vtrasterizer PRIVATE ${_source_files} PUBLIC ${_header_files}) 41 | 42 | set_target_properties(vtrasterizer PROPERTIES CXX_CLANG_TIDY "${CLANG_TIDY_EXE}") 43 | 44 | target_include_directories(vtrasterizer PUBLIC ${PROJECT_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/src) 45 | target_link_libraries(vtrasterizer PUBLIC vtbackend crispy::core text_shaper range-v3::range-v3) 46 | 47 | if(CONTOUR_TESTING) 48 | enable_testing() 49 | add_executable(vtrasterizer_test) 50 | target_sources(vtrasterizer_test PRIVATE ${_test_files}) 51 | target_link_libraries(vtrasterizer_test vtrasterizer Catch2::Catch2WithMain) 52 | add_test(vtrasterizer_test ./vtrasterizer_test) 53 | endif() 54 | 55 | message(STATUS "[vtrasterizer] Compile unit tests: ${CONTOUR_TESTINGG}") 56 | -------------------------------------------------------------------------------- /scripts/ci-set-vars.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | set -e 4 | 5 | if ! which xmllint; then 6 | echo "No xmllint installed" 7 | exit 1 8 | fi 9 | 10 | project_root="`dirname $0`/.." 11 | metainfo_xml="$project_root/metainfo.xml" 12 | 13 | VERSION_TRIPLE=`xmllint --xpath 'string(/component/releases/release[1]/@version)' $metainfo_xml` 14 | #RELEASE_TYPE=`xmllint --xpath 'string(/component/releases/release[1]/@type)' $metainfo_xml` 15 | 16 | if [[ "${GITHUB_RUN_NUMBER}" != "" ]]; then 17 | VERSION="${VERSION_TRIPLE}.${GITHUB_RUN_NUMBER}" 18 | else 19 | VERSION="${VERSION_TRIPLE}" 20 | fi 21 | 22 | if [[ -z "${GITHUB_OUTPUT}" ]]; then 23 | GITHUB_OUTPUT="/dev/stdout" 24 | fi 25 | 26 | if [[ "${GITHUB_HEAD_REF}" == "release" ]]; then 27 | IS_PRE='false'; 28 | SUFFIX=""; 29 | VERSION_STRING="${VERSION}" 30 | else 31 | IS_PRE='true'; 32 | SUFFIX="prerelease"; 33 | VERSION_STRING="${VERSION}-${SUFFIX}" 34 | fi 35 | 36 | # TODO: pass "/path/to/version.txt" target filename via CLI param "${1}", and only write that if given. 37 | echo "${VERSION_STRING}" >version.txt 38 | 39 | RELEASEBODY=$(xmllint --xpath '/component/releases/release[1]/description/ul/li' $metainfo_xml | 40 | sed 's/
  • / - /g' | 41 | sed 's,
  • ,,g') 42 | RELEASEBODY="${RELEASEBODY//\"/\\\"}" 43 | RELEASEBODY="${RELEASEBODY//$'\r'/''}" 44 | 45 | echo "version=${VERSION}" >> "$GITHUB_OUTPUT" 46 | echo "VERSION_STRING=${VERSION_STRING}" >> "$GITHUB_OUTPUT" 47 | echo "RUN_ID=${GITHUB_RUN_NUMBER}" >> "$GITHUB_OUTPUT" 48 | echo "IS_PRERELEASE=${IS_PRE}" >> "$GITHUB_OUTPUT" 49 | echo "RELEASENAME_SUFFIX=${SUFFIX}" >> "$GITHUB_OUTPUT" 50 | 51 | echo "${RELEASEBODY}" >release-body.md 52 | echo "${RELEASEBODY}" 53 | -------------------------------------------------------------------------------- /src/vtpty/PageSize.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace vtpty 8 | { 9 | 10 | namespace detail::tags 11 | { 12 | struct LineCount 13 | { 14 | }; 15 | struct ColumnCount 16 | { 17 | }; 18 | } // namespace detail::tags 19 | 20 | /// ColumnCount simply represents a number of columns. 21 | using ColumnCount = boxed::boxed; 22 | 23 | /// LineCount represents a number of lines. 24 | using LineCount = boxed::boxed; 25 | 26 | struct PageSize 27 | { 28 | LineCount lines; 29 | ColumnCount columns; 30 | 31 | [[nodiscard]] int area() const noexcept { return unbox(lines) * unbox(columns); } 32 | }; 33 | 34 | constexpr PageSize operator+(PageSize pageSize, LineCount lines) noexcept 35 | { 36 | return PageSize { pageSize.lines + lines, pageSize.columns }; 37 | } 38 | 39 | constexpr PageSize operator-(PageSize pageSize, LineCount lines) noexcept 40 | { 41 | return PageSize { pageSize.lines - lines, pageSize.columns }; 42 | } 43 | 44 | constexpr bool operator==(PageSize a, PageSize b) noexcept 45 | { 46 | return a.lines == b.lines && a.columns == b.columns; 47 | } 48 | 49 | constexpr bool operator!=(PageSize a, PageSize b) noexcept 50 | { 51 | return !(a == b); 52 | } 53 | 54 | constexpr ImageSize operator*(ImageSize a, PageSize b) noexcept 55 | { 56 | return ImageSize { a.width * boxed_cast(b.columns), a.height * boxed_cast(b.lines) }; 57 | } 58 | 59 | constexpr ImageSize operator/(ImageSize a, PageSize s) noexcept 60 | { 61 | return { Width::cast_from(unbox(a.width) / unbox(s.columns)), 62 | Height::cast_from(unbox(a.height) / unbox(s.lines)) }; 63 | } 64 | } // namespace vtpty 65 | -------------------------------------------------------------------------------- /docs/drafts/terminal-emulators.md: -------------------------------------------------------------------------------- 1 | # List of terminal emulator 2 | 3 | - xterm | X terminal 4 | - rxvt | fork of xterm with a lot of old code deleted 5 | - urxvt | fork of rxvt with unicode support added 6 | - konsole | KDE default terminal 7 | - gnome-terminal (libvte) | GNOME default terminal 8 | 9 | - cmder 10 | - conhost 11 | - mintty 12 | - putty 13 | 14 | - screen 15 | - tmux 16 | 17 | - Alacritty | #1 throughput performance 18 | - Contour 19 | - Fluent Terminal | 100% customizability 20 | - Guake | Quake-like terminal feeling 21 | - Hyper Terminal 22 | - Kitty | Extensibility (via kittens, Python) 23 | - Kuake | Quake-like terminal feeling 24 | - ST | <2k lines, minimalistic 25 | - Terminal.app | macOS default 26 | - Terminator | GUI multiplexer 27 | - Terminology | visually appealing features 28 | - Termite (libvte) 29 | - Termius 30 | - Windows Terminal | Modern Windows terminal, from MS itself and OSS 31 | - cool-retro-term (konsole) 32 | - fbpad 33 | - fbterm 34 | - iTerm2 35 | - kmscon 36 | - that\_terminal | recording TUI videos 37 | - yakuake (konsole) 38 | 39 | - hterm (JS terminal lib from Chromium project) 40 | - libvte 41 | - libvterm 42 | - xterm.js 43 | 44 | ### image protocol supporting terms 45 | 46 | - iTerm 3.0+ | Sixel 47 | - kitty | own protocol (refusing to implement Sixel himself) 48 | - konsole | Sixel 49 | - libvte | waiting for "the ONE image protocol" 50 | - terminology | own protocol 51 | - xterm | Sixel 52 | -------------------------------------------------------------------------------- /src/crispy/App.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #pragma once 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace crispy 13 | { 14 | 15 | /// General purpose Application main with CLI parameter handling and stuff. 16 | class app 17 | { 18 | public: 19 | app(std::string appName, std::string appTitle, std::string appVersion, std::string appLicense); 20 | virtual ~app(); 21 | 22 | static app* instance() noexcept { return _instance; } 23 | 24 | [[nodiscard]] virtual crispy::cli::command parameterDefinition() const = 0; 25 | [[nodiscard]] cli::flag_store const& parameters() const noexcept { return _flags.value(); } 26 | 27 | void link(std::string command, std::function handler); 28 | 29 | virtual int run(int argc, char const* argv[]); 30 | 31 | [[nodiscard]] std::string const& appName() const noexcept { return _appName; } 32 | [[nodiscard]] std::string const& appVersion() const noexcept { return _appVersion; } 33 | [[nodiscard]] std::filesystem::path const& localStateDir() const noexcept { return _localStateDir; } 34 | 35 | static void customizeLogStoreOutput(); 36 | 37 | protected: 38 | static void listDebugTags(); 39 | 40 | private: 41 | int versionAction(); 42 | int licenseAction(); 43 | int helpAction(); 44 | 45 | static app* _instance; // NOLINT(readability-identifier-naming) 46 | 47 | std::string _appName; 48 | std::string _appTitle; 49 | std::string _appVersion; 50 | std::string _appLicense; 51 | std::filesystem::path _localStateDir; 52 | std::optional _syntax; 53 | std::optional _flags; 54 | std::map> _handlers; 55 | }; 56 | 57 | } // namespace crispy 58 | -------------------------------------------------------------------------------- /cmake/FindPackageMessage.cmake: -------------------------------------------------------------------------------- 1 | # Distributed under the OSI-approved BSD 3-Clause License. See accompanying 2 | # file Copyright.txt or https://cmake.org/licensing for details. 3 | 4 | #[=======================================================================[.rst: 5 | FindPackageMessage 6 | ------------------ 7 | 8 | .. code-block:: cmake 9 | 10 | find_package_message( "message for user" "find result details") 11 | 12 | This function is intended to be used in FindXXX.cmake modules files. 13 | It will print a message once for each unique find result. This is 14 | useful for telling the user where a package was found. The first 15 | argument specifies the name (XXX) of the package. The second argument 16 | specifies the message to display. The third argument lists details 17 | about the find result so that if they change the message will be 18 | displayed again. The macro also obeys the QUIET argument to the 19 | find_package command. 20 | 21 | Example: 22 | 23 | .. code-block:: cmake 24 | 25 | if(X11_FOUND) 26 | find_package_message(X11 "Found X11: ${X11_X11_LIB}" 27 | "[${X11_X11_LIB}][${X11_INCLUDE_DIR}]") 28 | else() 29 | ... 30 | endif() 31 | #]=======================================================================] 32 | 33 | function(find_package_message pkg msg details) 34 | # Avoid printing a message repeatedly for the same find result. 35 | if(NOT ${pkg}_FIND_QUIETLY) 36 | string(REPLACE "\n" "" details "${details}") 37 | set(DETAILS_VAR FIND_PACKAGE_MESSAGE_DETAILS_${pkg}) 38 | if(NOT "${details}" STREQUAL "${${DETAILS_VAR}}") 39 | # The message has not yet been printed. 40 | message(STATUS "${msg}") 41 | 42 | # Save the find details in the cache to avoid printing the same 43 | # message again. 44 | set("${DETAILS_VAR}" "${details}" 45 | CACHE INTERNAL "Details about finding ${pkg}") 46 | endif() 47 | endif() 48 | endfunction() 49 | -------------------------------------------------------------------------------- /src/vtpty/MockPty.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | using namespace std::chrono; 6 | using std::optional; 7 | using std::string_view; 8 | 9 | namespace vtpty 10 | { 11 | 12 | MockPty::MockPty(PageSize size): _pageSize { size } 13 | { 14 | } 15 | 16 | PtySlave& MockPty::slave() noexcept 17 | { 18 | return _slave; 19 | } 20 | 21 | std::optional MockPty::read(crispy::buffer_object& storage, 22 | std::optional /*timeout*/, 23 | size_t size) 24 | { 25 | auto const n = std::min({ size, _outputBuffer.size() - _outputReadOffset, storage.bytesAvailable() }); 26 | auto const chunk = string_view { _outputBuffer.data() + _outputReadOffset, n }; 27 | _outputReadOffset += n; 28 | auto const pooled = storage.writeAtEnd(chunk); 29 | return ReadResult { .data = string_view(pooled.data(), pooled.size()), .fromStdoutFastPipe = false }; 30 | } 31 | 32 | void MockPty::wakeupReader() 33 | { 34 | // No-op. as we're a mock-pty. 35 | } 36 | 37 | int MockPty::write(std::string_view data) 38 | { 39 | // Writing into stdin. 40 | _inputBuffer += std::string_view(data.data(), data.size()); 41 | return static_cast(data.size()); 42 | } 43 | 44 | PageSize MockPty::pageSize() const noexcept 45 | { 46 | return _pageSize; 47 | } 48 | 49 | void MockPty::resizeScreen(PageSize cells, std::optional pixels) 50 | { 51 | _pageSize = cells; 52 | _pixelSize = pixels; 53 | } 54 | 55 | void MockPty::start() 56 | { 57 | _closed = false; 58 | } 59 | 60 | void MockPty::close() 61 | { 62 | _closed = true; 63 | } 64 | 65 | void MockPty::waitForClosed() 66 | { 67 | // No-op. as we're a mock-pty. 68 | } 69 | 70 | bool MockPty::isClosed() const noexcept 71 | { 72 | return _closed; 73 | } 74 | 75 | } // namespace vtpty 76 | -------------------------------------------------------------------------------- /src/vtpty/MockViewPty.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace vtpty 8 | { 9 | 10 | void MockViewPty::setReadData(std::string_view data) 11 | { 12 | assert(_outputBuffer.empty()); 13 | _outputBuffer = data; 14 | } 15 | 16 | PtySlave& MockViewPty::slave() noexcept 17 | { 18 | return _slave; 19 | } 20 | 21 | std::optional MockViewPty::read(crispy::buffer_object& storage, 22 | std::optional /*timeout*/, 23 | size_t size) 24 | { 25 | auto const n = std::min({ _outputBuffer.size(), storage.bytesAvailable(), size }); 26 | auto result = storage.writeAtEnd(_outputBuffer.substr(0, n)); 27 | _outputBuffer.remove_prefix(n); 28 | return ReadResult { .data = std::string_view(result.data(), result.size()), .fromStdoutFastPipe = false }; 29 | } 30 | 31 | void MockViewPty::wakeupReader() 32 | { 33 | // No-op. as we're a mock-pty. 34 | } 35 | 36 | int MockViewPty::write(std::string_view data) 37 | { 38 | // Writing into stdin. 39 | _inputBuffer += std::string_view(data.data(), data.size()); 40 | return static_cast(data.size()); 41 | } 42 | 43 | PageSize MockViewPty::pageSize() const noexcept 44 | { 45 | return _pageSize; 46 | } 47 | 48 | void MockViewPty::resizeScreen(PageSize cells, std::optional pixels) 49 | { 50 | _pageSize = cells; 51 | _pixelSize = pixels; 52 | } 53 | 54 | void MockViewPty::start() 55 | { 56 | _closed = false; 57 | } 58 | 59 | void MockViewPty::close() 60 | { 61 | _closed = true; 62 | } 63 | 64 | void MockViewPty::waitForClosed() 65 | { 66 | // No-op. as we're a mock-pty. 67 | } 68 | 69 | bool MockViewPty::isClosed() const noexcept 70 | { 71 | return _closed; 72 | } 73 | 74 | } // namespace vtpty 75 | -------------------------------------------------------------------------------- /docs/vt-extensions/vertical-line-marks.md: -------------------------------------------------------------------------------- 1 | # Vertical Line Markers 2 | 3 | Suppose you type a lot in the terminal, and I bet you do. Some commands may have inconveniently long 4 | output and you need a way to conveniently scroll the terminal viewport up to the top of that 5 | command. This is what this feature is there for. You can easily walk up/down your markers 6 | like you'd walk up code folds or markers in VIM or other editors. 7 | 8 | ## Set a mark: 9 | 10 | ```sh 11 | echo -ne "\033[>M" 12 | ``` 13 | 14 | ## Example key bindings in Contour: 15 | 16 | ```yaml 17 | input_mapping: 18 | - { mods: [Alt, Shift], key: 'k', mode: '~Alt', action: ScrollMarkUp } 19 | - { mods: [Alt, Shift], key: 'j', mode: '~Alt', action: ScrollMarkDown } 20 | ``` 21 | 22 | It is recommended to integrate the marker into your command prompt, such as `$PS1` in bash or sh to 23 | have automatic markers set. 24 | 25 | ## Integration into ZSH: 26 | 27 | zsh is way to configurable to give a fully generic answer here, but to show how you can integrate vertical line markers when using the [powerlevel9k](https://github.com/Powerlevel9k/powerlevel9k), this is what your `~/.zshrc` config could contain: 28 | 29 | ```sh 30 | prompt_setmark() { 31 | echo -ne "%{\033[>M%}" 32 | } 33 | POWERLEVEL9K_LEFT_PROMPT_ELEMENTS=(setmark user dir vcs) 34 | ``` 35 | 36 | ## Integration into Bash 37 | 38 | Bash is usually highly customized to your needs, but the bottom line would be as suggested below. You can create your custom `prompt_setmark` function that contains `\\[` and `\\]` as enclosing markers for the escape sequence to tell your shell that they do not change the current cursor position, and then use this function in our `PS1` environment variable or invoked inside your function assigned to `PROMPT_COMMAND`. 39 | 40 | ```sh 41 | prompt_setmark() { 42 | echo -ne "\\[\033[>M\\]" 43 | } 44 | 45 | # extending existing PS1 46 | export PS1="`prompt_setmark`${PS1}" 47 | ``` 48 | 49 | -------------------------------------------------------------------------------- /src/text_shaper/mock_font_locator.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | using std::nullopt; 8 | using std::optional; 9 | using std::string; 10 | using std::string_view; 11 | using std::unique_ptr; 12 | using std::vector; 13 | 14 | using namespace std::string_view_literals; 15 | 16 | namespace text 17 | { 18 | 19 | namespace mock_detail 20 | { 21 | static std::vector registry; 22 | } 23 | 24 | void mock_font_locator::configure(std::vector registry) 25 | { 26 | mock_detail::registry = std::move(registry); 27 | } 28 | 29 | font_source_list mock_font_locator::locate(font_description const& description) 30 | { 31 | locatorLog()("Locating font chain for: {}", description); 32 | 33 | font_source_list output; 34 | 35 | for (auto const& item: mock_detail::registry) 36 | { 37 | if (item.description != description) 38 | continue; 39 | 40 | output.emplace_back(item.source); 41 | break; 42 | } 43 | 44 | for (auto const& item: mock_detail::registry) 45 | { 46 | if (item.description.slant != description.slant) 47 | continue; 48 | 49 | if (item.description.weight != description.weight) 50 | continue; 51 | 52 | if (item.description.spacing != description.spacing && description.strictSpacing) 53 | continue; 54 | 55 | output.emplace_back(item.source); 56 | } 57 | 58 | return output; 59 | } 60 | 61 | font_source_list mock_font_locator::all() 62 | { 63 | font_source_list output; 64 | 65 | for (auto const& item: mock_detail::registry) 66 | output.emplace_back(item.source); 67 | 68 | return output; 69 | } 70 | 71 | font_source_list mock_font_locator::resolve(gsl::span /*codepoints*/) 72 | { 73 | return {}; 74 | } 75 | 76 | } // namespace text 77 | -------------------------------------------------------------------------------- /src/vtbackend/InputBinding.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | namespace vtbackend 10 | { 11 | 12 | template 13 | struct InputBinding 14 | { 15 | MatchModes modes; 16 | Modifiers modifiers; 17 | Input input; 18 | Binding binding; 19 | }; 20 | 21 | template 22 | bool match(InputBinding const& binding, MatchModes modes, Modifiers modifiers, Input input) 23 | { 24 | return binding.modes == modes && binding.modifiers == modifiers && binding.input == input; 25 | } 26 | 27 | template 28 | bool operator==(InputBinding const& a, InputBinding const& b) noexcept 29 | { 30 | return a.modes == b.modes && a.modifiers == b.modifiers && a.input == b.input; 31 | } 32 | 33 | template 34 | bool operator!=(InputBinding const& a, InputBinding const& b) noexcept 35 | { 36 | return !(a == b); 37 | } 38 | 39 | template 40 | bool operator<(InputBinding const& a, InputBinding const& b) noexcept 41 | { 42 | if (a.modes < b.modes) 43 | return true; 44 | if (a.modes != b.modes) 45 | return false; 46 | 47 | if (a.modifiers < b.modifiers) 48 | return true; 49 | if (a.modifiers != b.modifiers) 50 | return false; 51 | 52 | if (a.input < b.input) 53 | return true; 54 | 55 | return false; 56 | } 57 | 58 | } // namespace vtbackend 59 | 60 | template 61 | struct std::formatter> 62 | { 63 | auto parse(format_parse_context& ctx) -> format_parse_context::iterator { return ctx.begin(); } 64 | auto format(vtbackend::InputBinding const& binding, auto& ctx) const 65 | { 66 | return std::format_to(ctx.out(), "{} {} {}", binding.modes, binding.modifiers, binding.input); 67 | } 68 | }; 69 | -------------------------------------------------------------------------------- /cmake/presets/os-macos.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 6, 3 | "include": [ "common.json" ], 4 | "configurePresets": [ 5 | { 6 | "name": "macos-common", 7 | "inherits": "contour-common", 8 | "generator": "Ninja", 9 | "hidden": true, 10 | "condition": { 11 | "type": "equals", 12 | "lhs": "${hostSystemName}", 13 | "rhs": "Darwin" 14 | }, 15 | "cacheVariables": { 16 | "LIBTERMINAL_BUILD_BENCH_HEADLESS": "OFF", 17 | "CODE_SIGN_CERTIFICATE_ID": "Developer ID Application: Christian Parpart (6T525MU9UR)", 18 | "CMAKE_PREFIX_PATH": "/opt/homebrew/opt/qt" 19 | } 20 | }, 21 | { 22 | "name": "macos-debug", 23 | "displayName": "MacOS - Debug", 24 | "inherits": ["macos-common", "debug"] 25 | }, 26 | { 27 | "name": "macos-release", 28 | "displayName": "MacOS - Release", 29 | "inherits": ["macos-common", "release"] 30 | } 31 | ], 32 | "buildPresets": [ 33 | { 34 | "name": "macos-debug", 35 | "displayName": "MacOS/X - Debug", 36 | "configurePreset": "macos-debug" 37 | }, 38 | { 39 | "name": "macos-release", 40 | "displayName": "MacOS/X - Release", 41 | "configurePreset": "macos-release" 42 | } 43 | ], 44 | "testPresets": [ 45 | { 46 | "name": "macos-debug", 47 | "configurePreset": "macos-debug", 48 | "output": {"outputOnFailure": true}, 49 | "execution": { 50 | "noTestsAction": "error", 51 | "stopOnFailure": true 52 | } 53 | } 54 | ], 55 | "packagePresets": [ 56 | { 57 | "name": "macos-release", 58 | "configurePreset": "macos-release", 59 | "generators": [ "DragNDrop" ] 60 | } 61 | ] 62 | } 63 | -------------------------------------------------------------------------------- /src/crispy/sort.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #pragma once 3 | 4 | #include 5 | 6 | #include 7 | 8 | namespace crispy 9 | { 10 | 11 | namespace detail 12 | { 13 | template 14 | constexpr size_type partition(Container& container, Comp compare, size_type low, size_type high) 15 | { 16 | auto i = low - 1; 17 | auto& pivot = container[high]; 18 | 19 | for (auto const j: crispy::times(low, static_cast(high - low))) 20 | { 21 | if (compare(container[j], pivot) <= 0) 22 | { 23 | i++; 24 | std::swap(container[i], container[j]); 25 | } 26 | } 27 | 28 | i++; 29 | std::swap(container[i], container[high]); 30 | return i; 31 | } 32 | } // namespace detail 33 | 34 | template 35 | constexpr void sort(Container& container, Comp compare, size_t low, size_t high) 36 | { 37 | if (low < high) 38 | { 39 | auto const pi = detail::partition(container, compare, low, high); 40 | if (pi > 0) 41 | sort(container, compare, low, pi - 1); 42 | sort(container, compare, pi + 1, high); 43 | } 44 | } 45 | 46 | template 47 | constexpr void sort(Container& container, Comp compare) 48 | { 49 | if (auto const count = std::size(container); count > 1) 50 | sort(container, compare, 0, count - 1); 51 | } 52 | 53 | template 54 | constexpr void sort(Container& container) 55 | { 56 | if (auto const count = std::size(container); count > 1) 57 | sort( 58 | container, 59 | [](auto const& a, auto const& b) { 60 | if (a < b) 61 | return -1; 62 | if (a > b) 63 | return +1; 64 | return 0; 65 | }, 66 | 0, 67 | count - 1); 68 | } 69 | 70 | } // namespace crispy 71 | -------------------------------------------------------------------------------- /src/contour/display/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if(CONTOUR_QT_VERSION EQUAL "6") 2 | find_package(Qt6 COMPONENTS Core Gui Quick QuickControls2 Multimedia OpenGL REQUIRED) 3 | else() 4 | find_package(Qt5 COMPONENTS Gui Gui Quick QuickControls2 Multimedia REQUIRED) # apt install qtbase5-dev libqt5gui5 5 | endif() 6 | 7 | set(QT_RESOURCES DisplayResources.qrc) 8 | if(CONTOUR_QT_VERSION EQUAL "5") 9 | qt5_add_resources(QT_RESOURCES ${QT_RESOURCES}) 10 | endif() 11 | 12 | add_library(ContourTerminalDisplay STATIC 13 | Blur.cpp Blur.h 14 | OpenGLRenderer.cpp OpenGLRenderer.h 15 | ShaderConfig.cpp ShaderConfig.h 16 | TerminalDisplay.cpp TerminalDisplay.h 17 | ${QT_RESOURCES} 18 | ) 19 | set_target_properties(ContourTerminalDisplay PROPERTIES AUTOMOC ON) 20 | set_target_properties(ContourTerminalDisplay PROPERTIES AUTORCC ON) 21 | 22 | # Disable all deprecated Qt functions prior to Qt 6.0 23 | target_compile_definitions(ContourTerminalDisplay PRIVATE QT_DISABLE_DEPRECATED_BEFORE=0x050F00) 24 | 25 | target_compile_definitions(ContourTerminalDisplay PRIVATE CONTOUR_BUILD_TYPE="${CMAKE_BUILD_TYPE}") 26 | 27 | target_compile_definitions(ContourTerminalDisplay PRIVATE CONTOUR_APP_ID="${AppId}") 28 | 29 | option(CONTOUR_DEBUG_OPENGL "Enables OpenGL debugging." OFF) 30 | if(CONTOUR_DEBUG_OPENGL) 31 | target_compile_definitions(ContourTerminalDisplay PRIVATE Q_ENABLE_OPENGL_FUNCTIONS_DEBUG) 32 | endif() 33 | 34 | if(CONTOUR_PERF_STATS) 35 | target_compile_definitions(ContourTerminalDisplay PRIVATE CONTOUR_PERF_STATS=1) 36 | endif() 37 | 38 | target_include_directories(ContourTerminalDisplay PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/../..") 39 | target_link_libraries(ContourTerminalDisplay vtrasterizer) 40 | if(CONTOUR_QT_VERSION EQUAL "6") 41 | target_link_libraries(ContourTerminalDisplay Qt6::Core Qt6::Gui Qt6::OpenGL Qt6::Multimedia Qt6::Quick Qt6::QuickControls2) 42 | else() 43 | target_link_libraries(ContourTerminalDisplay Qt5::Core Qt5::Gui Qt5::Qml Qt5::Quick Qt5::QuickControls2) 44 | endif() 45 | set_target_properties(ContourTerminalDisplay PROPERTIES AUTOMOC ON) 46 | -------------------------------------------------------------------------------- /src/vtpty/MockPty.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #pragma once 3 | 4 | #include 5 | 6 | #include 7 | 8 | #include 9 | 10 | namespace vtpty 11 | { 12 | 13 | /// Mock-PTY, to be used in unit tests. 14 | class MockPty: public Pty 15 | { 16 | public: 17 | explicit MockPty(PageSize windowSize); 18 | ~MockPty() override = default; 19 | 20 | PtySlave& slave() noexcept override; 21 | [[nodiscard]] std::optional read(crispy::buffer_object& storage, 22 | std::optional timeout, 23 | size_t size) override; 24 | void wakeupReader() override; 25 | int write(std::string_view data) override; 26 | [[nodiscard]] PageSize pageSize() const noexcept override; 27 | void resizeScreen(PageSize cells, std::optional pixels = std::nullopt) override; 28 | 29 | void start() override; 30 | void close() override; 31 | void waitForClosed() override; 32 | [[nodiscard]] bool isClosed() const noexcept override; 33 | 34 | [[nodiscard]] std::string& stdinBuffer() noexcept { return _inputBuffer; } 35 | [[nodiscard]] std::string const& stdinBuffer() const noexcept { return _inputBuffer; } 36 | 37 | [[nodiscard]] bool isStdoutDataAvailable() const noexcept 38 | { 39 | return _outputReadOffset < _outputBuffer.size(); 40 | } 41 | 42 | void appendStdOutBuffer(std::string_view that) 43 | { 44 | if (_outputReadOffset == _outputBuffer.size()) 45 | { 46 | _outputReadOffset = 0; 47 | _outputBuffer = that; 48 | } 49 | else 50 | { 51 | _outputBuffer += that; 52 | } 53 | } 54 | 55 | private: 56 | PageSize _pageSize; 57 | std::optional _pixelSize; 58 | std::string _inputBuffer; 59 | std::string _outputBuffer; 60 | std::size_t _outputReadOffset = 0; 61 | bool _closed = false; 62 | PtySlaveDummy _slave; 63 | }; 64 | 65 | } // namespace vtpty 66 | -------------------------------------------------------------------------------- /test/colors-256.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Test 256 color support along with bold and dim attributes. 4 | # Copyright (C) 2014 Egmont Koblinger 5 | # 6 | # This program is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License along 17 | # with this program; if not, write to the Free Software Foundation, Inc., 18 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | 20 | format_number() { 21 | local c=$'\u254F' 22 | if [ $1 -lt 10 ]; then 23 | printf "$c %d" $1 24 | else 25 | printf "$c%02d" $(($1%100)) 26 | fi 27 | } 28 | 29 | somecolors() { 30 | local from="$1" 31 | local to="$2" 32 | local prefix="$3" 33 | local line 34 | 35 | for line in \ 36 | "\e[2mdim " \ 37 | "normal " \ 38 | "\e[1mbold " \ 39 | "\e[1;2mbold+dim "; do 40 | echo -ne "$line" 41 | i=$from 42 | while [ $i -le $to ]; do 43 | echo -ne "\e[$prefix${i}m" 44 | format_number $i 45 | i=$((i+1)) 46 | done 47 | echo $'\e[0m\e[K' 48 | done 49 | } 50 | 51 | allcolors() { 52 | echo "-- 8 standard colors: SGR ${1}0..${1}7 --" 53 | somecolors 0 7 "$1" 54 | echo 55 | echo "-- 8 bright colors: SGR ${2}0..${2}7 --" 56 | somecolors 0 7 "$2" 57 | echo 58 | echo "-- 256 colors: SGR ${1}8;5;0..255 --" 59 | somecolors 0 15 "${1}8;5;" 60 | echo 61 | somecolors 16 51 "${1}8;5;" 62 | somecolors 52 87 "${1}8;5;" 63 | somecolors 88 123 "${1}8;5;" 64 | somecolors 124 159 "${1}8;5;" 65 | somecolors 160 195 "${1}8;5;" 66 | somecolors 196 231 "${1}8;5;" 67 | echo 68 | somecolors 232 255 "${1}8;5;" 69 | } 70 | 71 | allcolors 3 9 72 | echo 73 | allcolors 4 10 74 | -------------------------------------------------------------------------------- /.github/actions/spelling/allow/terminal-terms.txt: -------------------------------------------------------------------------------- 1 | ANSIDSR 2 | ANSISYSSC 3 | APC 4 | APPCURSOR 5 | APPKEYPAD 6 | BSU 7 | CELLBACKGROUND 8 | CELLFOREGROUND 9 | CHT 10 | CNL 11 | COLORBG 12 | COLORCURSOR 13 | COLORFG 14 | COLORMOUSEBG 15 | COLORMOUSEFG 16 | COLORSPECIAL 17 | COLORTERM 18 | CSI 19 | CSIUENHCE 20 | CSIUENTER 21 | CSIULEAVE 22 | CSIUQUERY 23 | CSIu 24 | DECALN 25 | DECARM 26 | DECAUPSS 27 | DECAWM 28 | DECBI 29 | DECBKM 30 | DECCARA 31 | DECCIR 32 | DECCKM 33 | DECCOLM 34 | DECDC 35 | DECDLD 36 | DECERA 37 | DECFI 38 | DECFRA 39 | DECIC 40 | DECKPAM 41 | DECKPM 42 | DECKPNM 43 | DECLRM 44 | DECLRMM 45 | DECNKM 46 | DECNRCM 47 | DECOM 48 | DECPCTERM 49 | DECPS 50 | DECRA 51 | DECRC 52 | DECRLM 53 | DECRM 54 | DECRQM 55 | DECRQPSR 56 | DECRQSS 57 | DECRS 58 | DECSASD 59 | DECSC 60 | DECSCA 61 | DECSCL 62 | DECSCLM 63 | DECSCPP 64 | DECSCUSR 65 | DECSDM 66 | DECSED 67 | DECSEL 68 | DECSERA 69 | DECSIN 70 | DECSIXEL 71 | DECSLPP 72 | DECSLRM 73 | DECSM 74 | DECSNLS 75 | DECSPP 76 | DECSSCLS 77 | DECSSDT 78 | DECSTBM 79 | DECSTR 80 | DECSWT 81 | DECTABSR 82 | DECTCEM 83 | DECTEK 84 | DECXCPR 85 | DSRs 86 | ESC 87 | EXTRABLACK 88 | EXTRALIGHT 89 | Ghostty 90 | Guake 91 | HPR 92 | HVP 93 | LNM 94 | NEL 95 | OSC 96 | RCOLORBG 97 | RCOLORCURSOR 98 | RCOLORFG 99 | RCOLORHIGHLIGHTBG 100 | RCOLORHIGHLIGHTFG 101 | RCOLORMOUSEBG 102 | RCOLORMOUSEFG 103 | RCOLPAL 104 | RIS 105 | SETCOLPAL 106 | SETCWD 107 | SETFONTALL 108 | SETICON 109 | SETMASK 110 | SETWINTITLE 111 | SETXPROP 112 | SGCI 113 | SGRRESTORE 114 | SGRSAVE 115 | Setulc 116 | Sixel 117 | Smol 118 | Smulx 119 | TEs 120 | TMUX 121 | UTF 122 | VTEs 123 | WHEELDOWN 124 | WHEELUP 125 | WINDOWMANIP 126 | WINMANIP 127 | XTCAPTURE 128 | XTGETTCAP 129 | XTPOPCOLORS 130 | XTPOPSGR 131 | XTPUSHCOLORS 132 | XTPUSHSGR 133 | XTREPORTCOLORS 134 | XTRESTORE 135 | XTSAVE 136 | XTSETTCAP 137 | XTSHIFTESCAPE 138 | XTSMGRAPHICS 139 | XTVERSION 140 | XTWINOPS 141 | ZWJ 142 | acsc 143 | cbt 144 | ccc 145 | conhost 146 | conpty 147 | contourterminal 148 | ctl 149 | ctlseqs 150 | cuf 151 | cursorline 152 | cursorto 153 | cuu 154 | dcl 155 | dcs 156 | fbterm 157 | zellij 158 | -------------------------------------------------------------------------------- /docs/internals/CODING_STYLE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Coding Style Guidelines 4 | 5 | - Prefer CppCoreGuidelines if possible. 6 | - Avoid `friend` keyword. 7 | - Use signed integers unless the integer is holding a bit pattern. 8 | - Use the smallest integer type that is required to hold all desired values. 9 | - Do not cast from signed to unsigned unless you want the bit pattern, prefer to cast from unsigned 10 | to signed instead. 11 | 12 | ## Naming Conventions 13 | 14 | - namespaces: `snake_case` 15 | - types: UpperCamelCase 16 | - temporary variable: lowerCamelCase 17 | - private member variables: lowerCamelCase with trailing underscore 18 | - public member variable: lowerCamelCase 19 | - constexpr variable: UpperCamelCase 20 | - function names: lowerCamelCase 21 | - function parameters: lowerCamelCase with leading underscore 22 | - Template parameter name: UpperCamelCase 23 | - preprocessor definitions: `SCREAMING_CASE` 24 | - east const instead of west const 25 | 26 | ### Example 27 | 28 | ```cpp 29 | namespace org::coding_style::naming_conventions 30 | { 31 | void eastConst() { 32 | int const a = 42; // a is const 33 | int const* p = &a; // value in p is const, p is not const. 34 | int const *const p = &a; // both value and p are const. 35 | } 36 | 37 | enum class Role { Employed, Unemployed }; 38 | 39 | struct User 40 | { 41 | std::string firstname; 42 | std::string lastname; 43 | Role role; 44 | }; 45 | 46 | class Actor 47 | { 48 | public: 49 | Actor(std::string _firstname, std::string _lastname, Role _role) : 50 | user_{ std::move(_firstname), std::move(_lastname), _role }, 51 | credits_{ 0 } 52 | {} 53 | 54 | void giveOrTakeCredits(int _amount) noexcept 55 | { 56 | constexpr auto Scalar = 2; 57 | credits += Scalar * _amount; 58 | } 59 | 60 | std::string name() const 61 | { 62 | auto const result = user.firstname + " " + user.lastname; 63 | return result; 64 | } 65 | 66 | private: 67 | User user_; 68 | int credits_; 69 | }; 70 | } 71 | ``` 72 | -------------------------------------------------------------------------------- /src/vtbackend/Sequence.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | using std::accumulate; 11 | using std::string; 12 | using std::stringstream; 13 | 14 | namespace vtbackend 15 | { 16 | 17 | std::string Sequence::raw() const 18 | { 19 | stringstream sstr; 20 | 21 | switch (_category) 22 | { 23 | case FunctionCategory::C0: break; 24 | case FunctionCategory::ESC: sstr << "\033"; break; 25 | case FunctionCategory::CSI: sstr << "\033["; break; 26 | case FunctionCategory::DCS: sstr << "\033P"; break; 27 | case FunctionCategory::OSC: sstr << "\033]"; break; 28 | } 29 | 30 | // if (parameterCount() > 1 || (parameterCount() == 1 && _parameters.at(0) != 0)) 31 | { 32 | for (size_t i = 0; i < parameterCount(); ++i) 33 | { 34 | if (i) 35 | sstr << ';'; 36 | 37 | sstr << param(i); 38 | for (size_t k = 1; k < subParameterCount(i); ++k) 39 | sstr << ':' << subparam(i, k); 40 | } 41 | } 42 | 43 | sstr << intermediateCharacters(); 44 | 45 | if (_finalChar) 46 | sstr << _finalChar; 47 | 48 | if (!_dataString.empty()) 49 | sstr << _dataString << "\033\\"; 50 | 51 | return sstr.str(); 52 | } 53 | 54 | string Sequence::text() const 55 | { 56 | stringstream sstr; 57 | 58 | if (_category == FunctionCategory::C0) 59 | { 60 | sstr << to_short_string(ControlCode::C0(_finalChar)); 61 | return sstr.str(); 62 | } 63 | 64 | sstr << std::format("{}", _category); 65 | 66 | if (_leaderSymbol) 67 | sstr << ' ' << _leaderSymbol; 68 | 69 | if (parameterCount() > 1 || (parameterCount() == 1 && _parameters.at(0) != 0)) 70 | sstr << ' ' << _parameters.str(); 71 | 72 | if (!intermediateCharacters().empty()) 73 | sstr << ' ' << intermediateCharacters(); 74 | 75 | if (_finalChar) 76 | sstr << ' ' << _finalChar; 77 | 78 | if (!_dataString.empty()) 79 | sstr << " \"" << crispy::escape(_dataString) << "\" ST"; 80 | 81 | return sstr.str(); 82 | } 83 | 84 | } // namespace vtbackend 85 | -------------------------------------------------------------------------------- /src/vtrasterizer/BoxDrawingRenderer.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #pragma once 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | namespace vtrasterizer 13 | { 14 | 15 | // TODO: I think I should cincerely rename this class to 16 | // something more suitable. it's not about box-drawing alone anymore, 17 | // but about manually rendering anything that needs to properly fit 18 | // into the grid cell. 19 | // - box drawing symbols 20 | // - symbols for legacy computing 21 | // - mathematical symbols 22 | 23 | /// Takes care of rendering the text cursor. 24 | class BoxDrawingRenderer: public Renderable 25 | { 26 | public: 27 | explicit BoxDrawingRenderer(GridMetrics const& gridMetrics): Renderable { gridMetrics } {} 28 | 29 | void setRenderTarget(RenderTarget& renderTarget, DirectMappingAllocator& directMappingAllocator) override; 30 | void clearCache() override; 31 | 32 | [[nodiscard]] static bool renderable(char32_t codepoint) noexcept; 33 | 34 | /// Renders boxdrawing character. 35 | /// 36 | /// @param codepoint the boxdrawing character's codepoint. 37 | [[nodiscard]] bool render(vtbackend::LineOffset line, 38 | vtbackend::ColumnOffset column, 39 | char32_t codepoint, 40 | vtbackend::RGBColor color); 41 | 42 | void inspect(std::ostream& output) const override; 43 | 44 | private: 45 | AtlasTileAttributes const* getOrCreateCachedTileAttributes(char32_t codepoint); 46 | 47 | using Renderable::createTileData; 48 | [[nodiscard]] std::optional createTileData( 49 | char32_t codepoint, atlas::TileLocation tileLocation); 50 | 51 | [[nodiscard]] static std::optional buildBoxElements(char32_t codepoint, 52 | ImageSize size, 53 | int lineThickness); 54 | [[nodiscard]] std::optional buildElements(char32_t codepoint); 55 | }; 56 | 57 | } // namespace vtrasterizer 58 | -------------------------------------------------------------------------------- /src/crispy/point.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | namespace crispy 8 | { 9 | 10 | struct [[nodiscard]] point 11 | { 12 | int x {}; 13 | int y {}; 14 | }; 15 | 16 | template 17 | constexpr inline T Zero {}; 18 | template <> 19 | constexpr inline point Zero = point { 0, 0 }; 20 | 21 | constexpr point operator*(point p, double s) noexcept 22 | { 23 | return point { 24 | static_cast(static_cast(p.x) * s), 25 | static_cast(static_cast(p.y) * s), 26 | }; 27 | } 28 | 29 | constexpr point operator+(point a, point b) noexcept 30 | { 31 | return point { a.x + b.x, a.y + b.y }; 32 | } 33 | 34 | constexpr point& operator+=(point& a, point b) noexcept 35 | { 36 | a.x += b.x; 37 | a.y += b.y; 38 | return a; 39 | } 40 | 41 | constexpr void swap(point& a, point& b) noexcept 42 | { 43 | point const c = a; 44 | a = b; 45 | b = c; 46 | } 47 | 48 | constexpr inline int compare(point const& a, point const& b) noexcept 49 | { 50 | if (auto const dr = a.y - b.y; dr != 0) 51 | return dr; 52 | else 53 | return a.x - b.x; 54 | } 55 | 56 | constexpr inline bool operator<(point const& a, point const& b) noexcept 57 | { 58 | return compare(a, b) < 0; 59 | } 60 | 61 | constexpr inline bool operator<=(point const& a, point const& b) noexcept 62 | { 63 | return compare(a, b) <= 0; 64 | } 65 | 66 | constexpr inline bool operator>(point const& a, point const& b) noexcept 67 | { 68 | return compare(a, b) > 0; 69 | } 70 | 71 | constexpr inline bool operator>=(point const& a, point const& b) noexcept 72 | { 73 | return compare(a, b) >= 0; 74 | } 75 | 76 | constexpr inline bool operator==(point const& a, point const& b) noexcept 77 | { 78 | return a.x == b.x && a.y == b.y; 79 | } 80 | 81 | constexpr inline bool operator!=(point const& a, point const& b) noexcept 82 | { 83 | return !(a == b); 84 | } 85 | 86 | } // namespace crispy 87 | 88 | template <> 89 | struct std::formatter: formatter 90 | { 91 | auto format(crispy::point coord, auto& ctx) const 92 | { 93 | return formatter::format(std::format("({}, {})", coord.x, coord.y), ctx); 94 | } 95 | }; 96 | -------------------------------------------------------------------------------- /src/vtparser/Parser_test.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | 9 | using namespace std; 10 | 11 | class MockParserEvents final: public vtparser::NullParserEvents 12 | { 13 | public: 14 | std::string text; 15 | std::string apc; 16 | std::string pm; 17 | size_t maxCharCount = 80; 18 | 19 | void error(string_view const& msg) override { INFO(std::format("Parser error received. {}", msg)); } 20 | void print(char32_t ch) override { text += unicode::convert_to(ch); } 21 | size_t print(std::string_view s, size_t cellCount) override 22 | { 23 | text += s; 24 | return maxCharCount -= cellCount; 25 | } 26 | 27 | void startAPC() override { apc += "{"; } 28 | void putAPC(char ch) override { apc += ch; } 29 | void dispatchAPC() override { apc += "}"; } 30 | 31 | void startPM() override { pm += "{"; } 32 | void putPM(char ch) override { pm += ch; } 33 | void dispatchPM() override { pm += "}"; } 34 | }; 35 | 36 | TEST_CASE("Parser.utf8_single", "[Parser]") 37 | { 38 | MockParserEvents textListener; 39 | auto p = vtparser::Parser(textListener); 40 | 41 | p.parseFragment("\xC3\xB6"); // ö 42 | 43 | REQUIRE(textListener.text == "\xC3\xB6"); 44 | } 45 | 46 | TEST_CASE("Parser.PM") 47 | { 48 | MockParserEvents listener; 49 | auto p = vtparser::Parser(listener); 50 | REQUIRE(p.state() == vtparser::State::Ground); 51 | // Also include ✅ in the payload to ensure such codepoints work, too. 52 | p.parseFragment("ABC\033^hello ✅ world\033\\DEF"sv); 53 | CHECK(p.state() == vtparser::State::Ground); 54 | CHECK(listener.pm == "{hello ✅ world}"); 55 | CHECK(listener.text == "ABCDEF"); 56 | } 57 | 58 | TEST_CASE("Parser.APC") 59 | { 60 | MockParserEvents listener; 61 | auto p = vtparser::Parser(listener); 62 | REQUIRE(p.state() == vtparser::State::Ground); 63 | p.parseFragment("ABC\033\\\033_Gi=1,a=q;\033\\DEF"sv); 64 | REQUIRE(p.state() == vtparser::State::Ground); 65 | REQUIRE(listener.apc == "{Gi=1,a=q;}"); 66 | REQUIRE(listener.text == "ABCDEF"); 67 | } 68 | -------------------------------------------------------------------------------- /src/crispy/algorithm.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | #pragma once 3 | 4 | #include 5 | 6 | namespace crispy 7 | { 8 | 9 | // XXX Some C++20 backports 10 | 11 | template 12 | auto find_if(Container const& container, Pred&& pred) 13 | { 14 | return std::find_if(begin(container), end(container), std::forward(pred)); 15 | } 16 | 17 | template 18 | constexpr bool any_of(Container const& container, Fn&& fn) 19 | { 20 | return std::any_of(begin(container), end(container), std::forward(fn)); 21 | } 22 | 23 | template 24 | bool none_of(Container const& container, Fn&& fn) 25 | { 26 | return std::none_of(begin(container), end(container), std::forward(fn)); 27 | } 28 | 29 | template 30 | bool any_of(ExecutionPolicy ep, Container&& container, Fn&& fn) 31 | { 32 | return std::any_of(ep, 33 | begin(std::forward(container)), 34 | end(std::forward(container)), 35 | std::forward(fn)); 36 | } 37 | 38 | template 39 | void copy(Container const& container, OutputIterator outputIterator) 40 | { 41 | std::copy(std::begin(container), std::end(container), outputIterator); 42 | } 43 | 44 | template 45 | void for_each(Container&& container, Fn fn) 46 | { 47 | std::for_each(begin(std::forward(container)), end(std::forward(container)), fn); 48 | } 49 | 50 | template 51 | void for_each(ExecutionPolicy ep, Container&& container, Fn&& fn) 52 | { 53 | std::for_each(ep, 54 | begin(std::forward(container)), 55 | end(std::forward(container)), 56 | std::forward(fn)); 57 | } 58 | 59 | template 60 | auto count(Container&& container, T&& value) 61 | { 62 | return std::count(begin(std::forward(container)), 63 | end(std::forward(container)), 64 | std::forward(value)); 65 | } 66 | 67 | } // namespace crispy 68 | -------------------------------------------------------------------------------- /test/decoration.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | function decoration_color() { 4 | local color="${1:-250:250:70}" 5 | echo -ne "\033[58:2:${color}m" 6 | } 7 | 8 | function decoration() { 9 | local on="${1}" 10 | local name="${2}" 11 | local off="${3}" 12 | local color="${4:-250:250:70}" 13 | local bar="║" # │ 14 | decoration_color "${color}" 15 | echo -ne "\033[44m" 16 | echo -ne "${on}\t${bar} \033[${on}m✅${name} 👪\033[${off}m\n" 17 | } 18 | 19 | # Reference: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters 20 | 21 | decoration "0" "Reset or normal" 22 | decoration "1" "Bold or increased intensity" 23 | decoration "2" "Faint, decreased intensity, or dim" 24 | decoration "3" "Italic" 25 | decoration "4" "Single underline" 26 | #decoration "4:0" "No underline" 27 | decoration "4:1" "Single underline" 28 | decoration "4:2" "Doubly underline" 29 | decoration "4:3" "Curly underline" 30 | decoration "4:4" "Dotted underline" 31 | decoration "4:5" "Dashed underline" 32 | decoration "5" "Slow blink" 33 | decoration "6" "Rapid blink" 34 | decoration "7" "Reverse video or invert" 35 | decoration "8" "Conceal or hide" 36 | decoration "9" "Crossed-out or strike" 37 | #decoration 21 "Doubly underline or not bold" 38 | #decoration 22 "Normal intensity" 39 | #decoration 23 "Neither italic, nor blackletter" 40 | # decoration 24 "Not underlined" 41 | # decoration 25 "Not blinking" 42 | # decoration 26 "Proportional spacing" 43 | # decoration 27 "Not reversed" 44 | # decoration 28 "Reveal or not concealed" 45 | # decoration 29 "Not crossed out" 46 | # decoration 50 "Disable proportional spacing" 47 | # decoration 51 "Framed" 54 "170:170:255" 48 | # decoration 52 "Encircled" 49 | #decoration 53 "Overlined" 55 "250:30:250" 50 | # decoration 54 "Neither framed nor encircled" 51 | # decoration 55 "Not overlined" 52 | # #decoration 58 "Set underline color" 53 | # decoration 59 "Default underline color" 54 | # decoration 73 "Superscript" 55 | # decoration 74 "Subscript" 56 | # decoration 75 "Neither superscript nor subscript" 57 | 58 | # Decoration on the same line right next to each other 59 | decoration_color "250:250:50" 60 | echo -ne "\033[4:1m" 61 | echo -ne "Same" 62 | echo -ne "\033[0;4:2m" 63 | decoration_color "50:250:250" 64 | echo -ne "Line" 65 | echo -ne "\033[m\n" 66 | read 67 | -------------------------------------------------------------------------------- /src/contour/display/shaders/blur_gaussian.frag: -------------------------------------------------------------------------------- 1 | uniform highp sampler2D u_texture; 2 | uniform highp vec2 u_textureResolution; 3 | uniform highp vec2 u_viewportResolution; 4 | 5 | in highp vec2 fs_FragCoord; 6 | out highp vec4 fragColor; 7 | 8 | #define BlurSamples 64 9 | 10 | highp float gaussian(highp vec2 i) 11 | { 12 | const highp float TwoPi = 6.28318530718; 13 | const highp float Sigma = 0.25 * float(BlurSamples); 14 | const highp float SigmaSquared = Sigma * Sigma; 15 | highp vec2 iOverSigma = i / Sigma; 16 | return exp(-0.5 * dot(iOverSigma, iOverSigma)) / (TwoPi * SigmaSquared); 17 | } 18 | 19 | highp vec4 blur(highp sampler2D sp, highp vec2 uv, highp vec2 scale) 20 | { 21 | highp vec4 outputColor = vec4(0); 22 | const int BlurSamplesSquared = BlurSamples * BlurSamples; 23 | 24 | for (int i = 0; i < BlurSamplesSquared; i++) 25 | { 26 | highp vec2 d = vec2(i % BlurSamples, i / BlurSamples) - 0.5 * float(BlurSamples); 27 | outputColor += gaussian(d) * texture(sp, uv + scale * d); 28 | } 29 | 30 | return outputColor / outputColor.a; 31 | } 32 | 33 | // @param BlurSize radius (16 is a good value) 34 | // @param BlurQuality default 4.0 - more is better but slower 35 | // @param BlurDirections default 16.0 - more is better but slower 36 | highp vec4 blur2(highp sampler2D sp, highp vec2 uv, highp float BlurSize, highp float BlurQuality, highp float BlurDirections) 37 | { 38 | highp vec2 radius = BlurSize / u_viewportResolution; 39 | highp vec4 colorContribution = texture(sp, uv); 40 | 41 | highp float TwoPi = 6.28318530718; // 2 * Pi 42 | highp float CircleStepIncrement = TwoPi / BlurDirections; 43 | highp float NeighborStepIncrement = 1.0 / BlurQuality; 44 | 45 | for (highp float d = 0.0; d < TwoPi; d += CircleStepIncrement) 46 | for (highp float i = NeighborStepIncrement; i <= 1.0; i += NeighborStepIncrement) 47 | colorContribution += texture(sp, uv + vec2(cos(d), sin(d)) * radius * i); 48 | 49 | // Output to screen 50 | return colorContribution / (BlurQuality * BlurDirections - 15.0); 51 | } 52 | 53 | void main() 54 | { 55 | highp vec2 uv = vec2(gl_FragCoord.xy / u_viewportResolution); 56 | fragColor = blur2(u_texture, uv, 64.0, 8.0, 32.0); 57 | // fragColor = blur(u_texture, uv, 1.0 / u_textureResolution); 58 | } 59 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | set -ex 3 | 4 | # Minor helper to avoid repeating myself, 5 | # This shortcut allows you to simply invoke ../../autogen.sh when being 6 | # in directories like: 7 | # ./target/{Debug,RelWithDebInfo,Release} 8 | if [ -e ../../autogen.sh ] && [ "x$1" == "x" ] && 9 | [ -x "$(command -v realpath)" ] && 10 | [ "$(basename $(realpath ${PWD}/..))" = "target" ] 11 | then 12 | exec ../../autogen.sh $(basename $(realpath ${PWD})) 13 | fi 14 | 15 | if [ -x "$(command -v realpath)" ]; then 16 | ROOTDIR="$(realpath $(dirname $0))" 17 | else 18 | ROOTDIR="$(dirname $0)" 19 | fi 20 | 21 | CXX=${CXX="g++"} 22 | CXX_NAME=$(basename $CXX) 23 | 24 | BUILD_TYPE="${1:-Debug}" 25 | BUILD_DIR="${ROOTDIR}/target/$(uname -m)-$(uname -s)-${CXX_NAME}-${BUILD_TYPE}" 26 | 27 | EXTRA_CMAKE_FLAGS="$EXTRA_CMAKE_FLAGS -DLIBUNICODE_UCD_BASE_DIR=$ROOTDIR/_ucd" 28 | 29 | if test x$QTVER = x; then 30 | QTVER=6 31 | fi 32 | 33 | if test v$QTVER = v6; then 34 | EXTRA_CMAKE_FLAGS="${EXTRA_CMAKE_FLAGS} -DCONTOUR_QT_VERSION=6" 35 | else 36 | EXTRA_CMAKE_FLAGS="${EXTRA_CMAKE_FLAGS} -DCONTOUR_QT_VERSION=5" 37 | fi 38 | 39 | case "$OSTYPE" in 40 | darwin*) 41 | if test v$QTVER = v6; then 42 | EXTRA_CMAKE_FLAGS="${EXTRA_CMAKE_FLAGS} -DCMAKE_PREFIX_PATH=$(brew --prefix qt@6)" 43 | #EXTRA_CMAKE_FLAGS="${EXTRA_CMAKE_FLAGS} -DQt6_DIR=$(brew --prefix qt6)/lib/cmake/Qt6" 44 | #EXTRA_CMAKE_FLAGS="${EXTRA_CMAKE_FLAGS} -DQt6_DIR=$HOME/Qt/6.5.3/macos/lib/cmake/Qt6" 45 | else 46 | EXTRA_CMAKE_FLAGS="${EXTRA_CMAKE_FLAGS} -DQt5_DIR=$(brew --prefix qt5)/lib/cmake/Qt5" 47 | fi 48 | ;; 49 | *) 50 | ;; 51 | esac 52 | 53 | # if [ "${BUILD_TYPE}" != "Debug" ]; then 54 | # EXTRA_CMAKE_FLAGS="${EXTRA_CMAKE_FLAGS} -DCMAKE_INSTALL_PREFIX=~/usr/opt/contour" 55 | # fi 56 | 57 | echo "EXTRA_CMAKE_FLAGS: ${EXTRA_CMAKE_FLAGS}" 58 | 59 | exec cmake "${ROOTDIR}" \ 60 | -DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \ 61 | -DCMAKE_CXX_COMPILER="${CXX}" \ 62 | -DLIBTERMINAL_BUILD_BENCH_HEADLESS=ON \ 63 | -DPEDANTIC_COMPILER=ON \ 64 | -DPEDANTIC_COMPILER_WERROR=ON \ 65 | -DCMAKE_CXX_STANDARD=20 \ 66 | -DCODE_SIGN_CERTIFICATE_ID="${CODE_SIGN_ID:-}" \ 67 | ${EXTRA_CMAKE_FLAGS} \ 68 | -B "${BUILD_DIR}" \ 69 | -GNinja 70 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: Docs 2 | 3 | on: 4 | merge_group: 5 | push: 6 | paths: 7 | - 'docs/**' 8 | - '.github/workflows/docs.yml' 9 | - 'metainfo.xml' 10 | branches: 11 | - master 12 | 13 | concurrency: 14 | group: docs-${{ github.ref }} 15 | cancel-in-progress: true 16 | 17 | jobs: 18 | mkdocs: 19 | name: Deploy docs 20 | runs-on: ubuntu-24.04 21 | steps: 22 | - name: Checkout main 23 | uses: actions/checkout@v4 24 | 25 | - name: Extract changes for the releases from metainfo.xml 26 | run: | 27 | ./scripts/extract-changelog-from-metainfo.py metainfo.xml docs/release-notes.md 28 | 29 | - name: "update APT database" 30 | run: sudo apt -q update 31 | - name: "Install GCC" 32 | run: sudo apt -y install g++-14 33 | - name: "install dependencies" 34 | run: sudo env QTVER=6 SYSDEP_ASSUME_YES=ON ./scripts/install-deps.sh 35 | - name: "Post-fix embedded dependency permissions." 36 | run: sudo find _deps/sources -exec chown $UID {} \; 37 | - name: "create build directory" 38 | run: mkdir build 39 | - name: "Install-contour" 40 | run: cmake --preset linux-debug -B build -G Ninja 41 | - name: "build" 42 | run: cmake --build build 43 | - name: "create vt-sequence directory" 44 | run: mkdir docs/vt-sequence 45 | - name: "Generate documentation for global config" 46 | run: ./build/src/contour/contour documentation configuration global > docs/configuration/index.md 47 | - name: "Generate documentation for profile config" 48 | run: ./build/src/contour/contour documentation configuration profile > docs/configuration/profiles.md 49 | - name: "Generate vt documentation" 50 | run: ./build/src/contour/contour documentation vt > docs/vt-sequence/index.md 51 | - name: "Generate key mapping documentation" 52 | run: ./build/src/contour/contour documentation keys >> docs/configuration/key-mapping.md 53 | 54 | - name: Deploy docs 55 | uses: mhausenblas/mkdocs-deploy-gh-pages@master 56 | env: 57 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 58 | CUSTOM_DOMAIN: contour-terminal.org 59 | CONFIG_FILE: ./mkdocs.yml 60 | EXTRA_PACKAGES: build-base 61 | REQUIREMENTS: docs/requirements.txt 62 | 63 | 64 | --------------------------------------------------------------------------------