├── .clang-format ├── .github └── workflows │ └── cmake.yml ├── .gitignore ├── CMakeLists.txt ├── COPYING ├── LICENSE_htable ├── NEWS ├── README ├── README.md ├── ci.ctest ├── cmake ├── BuildTypes.cmake ├── CompileOptions.cmake ├── JoinPaths.cmake ├── Utilities.cmake └── modules │ ├── FindCairo.cmake │ ├── FindCheck.cmake │ ├── FindGTK3.cmake │ ├── FindPango.cmake │ ├── FindPangoCairo.cmake │ └── FindXKBCommon.cmake ├── etc ├── libtsm-config.cmake.in ├── libtsm.pc.in └── test.supp ├── external ├── CMakeLists.txt ├── wcwidth │ ├── .gitignore │ ├── .travis.yml │ ├── LICENSE.txt │ ├── README.md │ ├── test_wcwidth.c │ ├── wcwidth.c │ └── wcwidth.h └── xkbcommon │ └── xkbcommon-keysyms.h ├── src ├── CMakeLists.txt ├── config.h.in ├── gtktsm │ ├── CMakeLists.txt │ ├── gtktsm-app.c │ ├── gtktsm-app.h │ ├── gtktsm-terminal.c │ ├── gtktsm-terminal.h │ ├── gtktsm-win.c │ ├── gtktsm-win.h │ └── gtktsm.c ├── shared │ ├── CMakeLists.txt │ ├── shl-array.h │ ├── shl-htable.c │ ├── shl-htable.h │ ├── shl-llog.h │ ├── shl-macro.h │ ├── shl-pty.c │ ├── shl-pty.h │ ├── shl-ring.c │ └── shl-ring.h └── tsm │ ├── CMakeLists.txt │ ├── libtsm-int.h │ ├── libtsm.h │ ├── libtsm.sym │ ├── tsm-render.c │ ├── tsm-screen.c │ ├── tsm-selection.c │ ├── tsm-unicode.c │ ├── tsm-vte-charsets.c │ └── tsm-vte.c └── test ├── CMakeLists.txt ├── test_common.h ├── test_htable.c ├── test_screen.c ├── test_selection.c ├── test_symbol.c ├── test_valgrind.c ├── test_vte.c └── test_vte_mouse.c /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | BasedOnStyle: LLVM 4 | AccessModifierOffset: -4 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveAssignments: false 7 | AlignConsecutiveDeclarations: false 8 | AlignEscapedNewlines: Right 9 | AlignOperands: true 10 | AlignTrailingComments: true 11 | AllowAllParametersOfDeclarationOnNextLine: true 12 | AllowShortBlocksOnASingleLine: false 13 | AllowShortCaseLabelsOnASingleLine: false 14 | AllowShortFunctionsOnASingleLine: Empty 15 | AllowShortIfStatementsOnASingleLine: false 16 | AllowShortLoopsOnASingleLine: false 17 | AlwaysBreakAfterDefinitionReturnType: None 18 | AlwaysBreakAfterReturnType: None 19 | AlwaysBreakBeforeMultilineStrings: false 20 | AlwaysBreakTemplateDeclarations: true 21 | BinPackArguments: true 22 | BinPackParameters: true 23 | BraceWrapping: 24 | AfterClass: false 25 | AfterControlStatement: false 26 | AfterEnum: false 27 | AfterFunction: true 28 | AfterNamespace: false 29 | AfterObjCDeclaration: false 30 | AfterStruct: false 31 | AfterUnion: false 32 | AfterExternBlock: false 33 | BeforeCatch: false 34 | BeforeElse: false 35 | IndentBraces: false 36 | SplitEmptyFunction: true 37 | SplitEmptyRecord: true 38 | SplitEmptyNamespace: true 39 | BreakBeforeBinaryOperators: NonAssignment 40 | BreakBeforeBraces: Custom 41 | BreakBeforeInheritanceComma: false 42 | BreakInheritanceList: BeforeColon 43 | BreakBeforeTernaryOperators: true 44 | BreakConstructorInitializersBeforeComma: true 45 | BreakConstructorInitializers: BeforeComma 46 | BreakAfterJavaFieldAnnotations: false 47 | BreakStringLiterals: true 48 | ColumnLimit: 110 49 | CommentPragmas: '^ IWYU pragma:' 50 | CompactNamespaces: false 51 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 52 | ConstructorInitializerIndentWidth: 4 53 | ContinuationIndentWidth: 4 54 | Cpp11BracedListStyle: true 55 | DerivePointerAlignment: false 56 | DisableFormat: false 57 | ExperimentalAutoDetectBinPacking: false 58 | FixNamespaceComments: true 59 | ForEachMacros: 60 | - foreach 61 | - Q_FOREACH 62 | - Q_FOREVER 63 | - BOOST_FOREACH 64 | IncludeBlocks: Regroup 65 | IncludeCategories: 66 | - Regex: 'tensorflow_headers\.h' 67 | Priority: -1 68 | - Regex: '^"' 69 | Priority: 3 70 | - Regex: '^(<|")(tensorflow|third_party)/' 71 | Priority: 5 72 | - Regex: '^ 5 | David Herrmann 6 | Ran Benita 7 | 8 | = Copyright Notice = 9 | 10 | This software is licensed under the terms of the MIT license. Please see each 11 | source file for the related copyright notice and license. 12 | 13 | If a file does not contain a copright notice, the following license shall 14 | apply: 15 | 16 | Copyright (c) 2017-2018 Aetf 17 | Copyright (c) 2011-2013 David Herrmann 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining 20 | a copy of this software and associated documentation files 21 | (the "Software"), to deal in the Software without restriction, including 22 | without limitation the rights to use, copy, modify, merge, publish, 23 | distribute, sublicense, and/or sell copies of the Software, and to 24 | permit persons to whom the Software is furnished to do so, subject to 25 | the following conditions: 26 | 27 | The above copyright notice and this permission notice shall be included 28 | in all copies or substantial portions of the Software. 29 | 30 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 31 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 32 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 33 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 34 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 35 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 36 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 37 | 38 | == Third-Party Source == 39 | The hash-table implementation in src/shared/shl-htable.* uses internally the 40 | htable from CCAN, see LICENSE_htable. 41 | 42 | The wcwidth() implementation in ./external/wcwidth is from 43 | 44 | Copyright (C) Fredrik Fornwall 2016. 45 | Distributed under the MIT License. 46 | 47 | Implementation of wcwidth(3) as a C port of: 48 | https://github.com/jquast/wcwidth 49 | 50 | Report issues at: 51 | https://github.com/termux/wcwidth 52 | 53 | UCS-4 to UTF-8 encoding is copied from "terminology": 54 | 55 | Copyright (C) 2012-2012 Carsten Haitzler and various contributors 56 | 57 | All rights reserved. 58 | 59 | Redistribution and use in source and binary forms, with or without 60 | modification, are permitted provided that the following conditions are 61 | met: 62 | 63 | 1. Redistributions of source code must retain the above copyright 64 | notice, this list of conditions and the following disclaimer. 65 | 2. Redistributions in binary form must reproduce the above copyright 66 | notice, this list of conditions and the following disclaimer in the 67 | documentation and/or other materials provided with the distribution. 68 | 69 | THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 70 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 71 | AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 72 | THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 73 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 74 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 75 | USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 76 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 77 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 78 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 79 | 80 | The "solarized" color palettes in tsm_vte.c are from: 81 | 82 | Copyright (c) 2011 Ethan Schoonover 83 | 84 | Permission is hereby granted, free of charge, to any person obtaining a copy 85 | of this software and associated documentation files (the "Software"), to deal 86 | in the Software without restriction, including without limitation the rights 87 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 88 | copies of the Software, and to permit persons to whom the Software is 89 | furnished to do so, subject to the following conditions: 90 | 91 | The above copyright notice and this permission notice shall be included in 92 | all copies or substantial portions of the Software. 93 | 94 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 95 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 96 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 97 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 98 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 99 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 100 | THE SOFTWARE. 101 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | = libtsm Release News = 2 | 3 | CHANGES WITH 1: 4 | * TODO 5 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | = TSM - Terminal Emulator State Machine = 2 | 3 | TSM is a state machine for DEC VT100-VT520 compatible terminal emulators. It 4 | tries to support all common standards while keeping compatibility to existing 5 | emulators like xterm, gnome-terminal, konsole, .. 6 | 7 | TSM itself does not provide any rendering nor window management. It is a simple 8 | plain state machine without any external dependencies. It can be used to 9 | implement terminal emulators, but also to implement other applications that need 10 | to interpret terminal escape sequences. 11 | 12 | This library is very similar to libvte of the gnome project. However, libvte is 13 | highly bound to GTK+, which makes it unsuitable for non-graphics projects that 14 | need to parse escape sequences. Instead, TSM tries to restrict its API to 15 | terminal emulation only. Furthermore, TSM does not try to establish a new 16 | terminal emulation standard, but instead keeps compatibility as close to xterm 17 | as possible. This is why the TERM variable can be set to xterm-color256 with any 18 | TSM based terminal emulator. 19 | 20 | Website: 21 | http://www.freedesktop.org/wiki/Software/libtsm 22 | 23 | == Requirements == 24 | 25 | libtsm has no runtime requirements other than a ISO-C compatible C library. 26 | For keyboard key-symbols, the headers of libxkbcommon are needed during 27 | compile-time only. libtsm ships a copy of these headers if they are not 28 | available at compile-time. 29 | 30 | == Download == 31 | 32 | Released tarballs can be found at: 33 | http://www.freedesktop.org/software/kmscon/releases 34 | 35 | == Install == 36 | 37 | To compile libtsm, run the standard autotools commands: 38 | $ test -f ./configure || NOCONFIGURE=1 ./autogen.sh 39 | $ ./configure 40 | $ make 41 | $ make install 42 | To compile the test applications, run: 43 | $ make check 44 | 45 | == Documentation == 46 | 47 | There is currently no API documentation available. You can have a look at the 48 | example terminal-emulator "wlterm" available at: 49 | http://www.freedesktop.org/wiki/Software/kmscon/wlterm 50 | 51 | == License == 52 | 53 | This software is licensed under the terms of an MIT-like license. Please see 54 | ./COPYING for further information. 55 | 56 | == Contact == 57 | 58 | This software is maintained by: 59 | David Herrmann 60 | If you have any questions, do not hesitate to contact one of the maintainers. 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TSM - Terminal Emulator State Machine 2 | 3 | [![Build Status](https://travis-ci.com/Aetf/libtsm.svg?branch=master)](https://travis-ci.com/Aetf/libtsm) 4 | 5 | TSM is a state machine for DEC VT100-VT520 compatible terminal emulators. It 6 | tries to support all common standards while keeping compatibility to existing 7 | emulators like xterm, gnome-terminal, konsole, ... 8 | 9 | This is a personal modified version. For more information, please refer to its original [README](README). 10 | 11 | ## Added feature 12 | + More color palettes: 13 | * soft-black 14 | * [base16](https://github.com/chriskempson/base16-default-schemes){-light,-dark} 15 | * solarized{,-black,-white} 16 | * custom: set via API 17 | + Support underline/italic rendering (with [a patched version of kmscon](https://github.com/Aetf/kmscon)) 18 | + Support 24-bit true color 19 | + Support Ctrl + Arrow keys 20 | + Support custom title using OSC 21 | + Bug fixes: 22 | * [Repsonse to 'CSI c' contains random bytes][91335] 23 | * [Fix invalid cpr values](https://github.com/Aetf/libtsm/pull/2) 24 | 25 | [91335]: https://bugs.freedesktop.org/show_bug.cgi?id=91335 26 | 27 | ## Build 28 | ```bash 29 | mkdir build && cd build 30 | cmake .. 31 | make 32 | make install 33 | ``` 34 | 35 | ### Build options 36 | Options may be supplied when configuring cmake: 37 | ```bash 38 | cmake -DOPTION1=VALUE1 -DOPTION2=VALUE2 .. 39 | ``` 40 | The following options are available: 41 | 42 | |Name | Description | Default | 43 | |:---:|:---|:---:| 44 | | BUILD_SHARED_LIBS | Whether to build as a shared library | ON | 45 | | BUILD_TESTING | Whether to build test suits | OFF | 46 | | ENABLE_EXTRA_DEBUG | Whether to enable several non-standard debug options. | OFF | 47 | | BUILD_GTKTSM | Whether to build the gtktsm example. This is linux-only as it uses epoll and friends. Therefore is disabled by default. | OFF | 48 | 49 | ### Dependencies 50 | 51 | - [cmake](https://cmake.org) >= 3.5 52 | - `xkbcommon-keysyms.h` from xkbcommon (Optional. Will use private copy if not found.) 53 | 54 | The test suits needs: 55 | 56 | - [check](https://libcheck.github.io/check/) >= 0.9.10 57 | 58 | The gtktsm example needs: 59 | 60 | - gtk3 61 | - cairo 62 | - pango 63 | - xkbcommon 64 | -------------------------------------------------------------------------------- /ci.ctest: -------------------------------------------------------------------------------- 1 | set(CTEST_PROJECT_NAME libtsm) 2 | 3 | set(CTEST_SOURCE_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}) 4 | set(CTEST_BINARY_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/build/ctest") 5 | 6 | set(CTEST_CMAKE_GENERATOR "Unix Makefiles") 7 | 8 | set(ENV{CK_FORK} "no") 9 | find_program(VALGRIND valgrind) 10 | if(NOT VALGRIND) 11 | message(FATAL_ERROR "valgrind is required for memcheck") 12 | endif() 13 | set(CTEST_MEMORYCHECK_COMMAND "${VALGRIND}") 14 | string(CONCAT CTEST_MEMORYCHECK_COMMAND_OPTIONS 15 | " --tool=memcheck" 16 | " --leak-check=yes" 17 | " --show-reachable=yes" 18 | " --leak-resolution=high" 19 | " --error-exitcode=1" 20 | ) 21 | set(CTEST_MEMORYCHECK_SUPPRESSIONS_FILE "${CTEST_SOURCE_DIRECTORY}/etc/test.supp") 22 | 23 | function(print_section name) 24 | message(STATUS "********************") 25 | message(STATUS "${name}") 26 | message(STATUS "********************") 27 | endfunction() 28 | 29 | # 30 | # Start the tests 31 | # 32 | ctest_start("Continuous") 33 | 34 | print_section("Configuring project") 35 | ctest_configure(OPTIONS "-DBUILD_TESTING=ON;-DBUILD_GTKTSM=ON" RETURN_VALUE ret) 36 | if(NOT ${ret} EQUAL 0) 37 | message(FATAL_ERROR "Configure step failed with ${ret}") 38 | endif() 39 | 40 | print_section("Building project") 41 | ctest_build(RETURN_VALUE ret) 42 | if(NOT ${ret} EQUAL 0) 43 | message(FATAL_ERROR "Build step failed with ${ret}") 44 | endif() 45 | 46 | print_section("Running tests") 47 | ctest_test(RETURN_VALUE ret) 48 | if(NOT ${ret} EQUAL 0) 49 | message(FATAL_ERROR "Tests step failed with ${ret}") 50 | endif() 51 | 52 | # First make sure valgrind works 53 | print_section("Making sure valgrind works") 54 | ctest_memcheck(INCLUDE_LABEL "memcheck-xfail" RETURN_VALUE ret) 55 | if(${ret} EQUAL 0) 56 | message(FATAL_ERROR "Valgrind may not work correctly. Expected failed test got passed.") 57 | endif() 58 | 59 | print_section("Running memcheck") 60 | ctest_memcheck(EXCLUDE_LABEL "memcheck-xfail" RETURN_VALUE ret) 61 | if(NOT ${ret} EQUAL 0) 62 | message(FATAL_ERROR "Memcheck step failed with ${ret}") 63 | endif() 64 | -------------------------------------------------------------------------------- /cmake/BuildTypes.cmake: -------------------------------------------------------------------------------- 1 | # Add new build types for sanitizer and profiler 2 | # Address sanitizer and undefined beheavior sanitizer 3 | set(CMAKE_CXX_FLAGS_ASAN "-g -O1 -fno-omit-frame-pointer -fsanitize=address" 4 | CACHE STRING "Flags used by the C++ compiler during ASan builds." FORCE) 5 | 6 | set(CMAKE_C_FLAGS_ASAN "${CMAKE_CXX_FLAGS_ASAN}" 7 | CACHE STRING "Flags used by the C compiler during ASan builds." FORCE) 8 | 9 | set(CMAKE_EXE_LINKER_FLAGS_ASAN "-fsanitize=address" 10 | CACHE STRING "Flags used for linking binaries during ASan builds." FORCE) 11 | 12 | set(CMAKE_SHARED_LINKER_FLAGS_ASAN "-fsanitize=address" 13 | CACHE STRING "Flags used by the shared libraries linker during ASan builds." FORCE) 14 | 15 | # Thread sanitizer 16 | set(CMAKE_CXX_FLAGS_TSAN "-DNDEBUG -g -O0 -fno-omit-frame-pointer -fsanitize=thread" 17 | CACHE STRING "Flags used by the C++ compiler during TSan builds." FORCE) 18 | 19 | set(CMAKE_C_FLAGS_TSAN "${CMAKE_CXX_FLAGS_TSAN}" 20 | CACHE STRING "Flags used by the C compiler during TSan builds." FORCE) 21 | 22 | set(CMAKE_EXE_LINKER_FLAGS_TSAN "-fsanitize=thread" 23 | CACHE STRING "Flags used for linking binaries during TSan builds." FORCE) 24 | 25 | set(CMAKE_SHARED_LINKER_FLAGS_TSAN "-fsanitize=thread" 26 | CACHE STRING "Flags used by the shared libraries linker during TSan builds." FORCE) 27 | 28 | # Memory sanitizer 29 | set(CMAKE_CXX_FLAGS_MSAN "-DNDEBUG -g -O1 -fno-omit-frame-pointer -fsanitize=memory" 30 | CACHE STRING "Flags used by the C++ compiler during MSan builds." FORCE) 31 | 32 | set(CMAKE_C_FLAGS_MSAN "${CMAKE_CXX_FLAGS_MSAN}" 33 | CACHE STRING "Flags used by the C compiler during MSan builds." FORCE) 34 | 35 | set(CMAKE_EXE_LINKER_FLAGS_MSAN "-fsanitize=memory" 36 | CACHE STRING "Flags used for linking binaries during MSan builds." FORCE) 37 | 38 | set(CMAKE_SHARED_LINKER_FLAGS_MSAN "-fsanitize=memory" 39 | CACHE STRING "Flags used by the shared libraries linker during MSan builds." FORCE) 40 | 41 | set(KNOWN_BUILD_TYPES "") 42 | foreach(build IN ITEMS ASan TSan MSan) 43 | string(TOUPPER ${build} build_upper) 44 | mark_as_advanced( 45 | CMAKE_CXX_FLAGS_${build_upper} 46 | CMAKE_C_FLAGS_${build_upper} 47 | CMAKE_EXE_LINKER_FLAGS_${build_upper} 48 | CMAKE_SHARED_LINKER_FLAGS_${build_upper} 49 | ) 50 | list(APPEND KNOWN_BUILD_TYPES ${build}) 51 | endforeach() 52 | list(APPEND KNOWN_BUILD_TYPES Debug Release RelWithDebInfo MinSizeRel) 53 | 54 | if (NOT CMAKE_BUILD_TYPE IN_LIST KNOWN_BUILD_TYPES) 55 | message(FATAL_ERROR "Unknown build type: ${CMAKE_BUILD_TYPE}. Choices are ${KNOWN_BUILD_TYPES}") 56 | endif() 57 | 58 | # Update the documentation string of CMAKE_BUILD_TYPE for GUIs 59 | set(CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE}" 60 | CACHE STRING 61 | "Choose the type of build, options are: None;${KNOWN_BUILD_TYPES}." 62 | FORCE) 63 | 64 | message(STATUS "Using build type: ${CMAKE_BUILD_TYPE}") 65 | -------------------------------------------------------------------------------- /cmake/CompileOptions.cmake: -------------------------------------------------------------------------------- 1 | # Set compiler flags 2 | set(CMAKE_C_STANDARD 99) 3 | set(CMAKE_C_STANDARD_REQUIRED ON) 4 | 5 | # analogous to AC_USE_SYSTEM_EXTENSIONS in configure.ac 6 | add_definitions(-D_POSIX_C_SOURCE=199309L -D_GNU_SOURCE) 7 | 8 | # copied from Autoconf's AC_SYS_LARGEFILE 9 | if(NOT WIN32) 10 | add_definitions(-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE) 11 | endif(NOT WIN32) 12 | 13 | if(APPLE) 14 | add_compile_definitions(_DARWIN_C_SOURCE) 15 | link_directories("/usr/local/lib") 16 | include_directories("external") 17 | endif() 18 | 19 | # Set compiler flags for warnings 20 | if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") 21 | # using Clang or AppleClang 22 | 23 | # reasonable and standard 24 | add_compile_options_with_check(-Weverything) 25 | add_compile_options_with_check(-Werror) 26 | add_compile_options_with_check(-Wfatal-errors) 27 | 28 | add_compile_options_with_check(-Wold-style-cast) 29 | # helps catch hard to track down memory errors 30 | add_compile_options_with_check(-Wnon-virtual-dtor) 31 | # warn for potential performance problem casts 32 | add_compile_options_with_check(-Wcast-align) 33 | # warn if you overload (not override) a virtual function 34 | add_compile_options_with_check(-Woverloaded-virtual) 35 | elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") 36 | # using GCC 37 | 38 | # reasonable and standard 39 | add_compile_options_with_check(-Wall) 40 | add_compile_options_with_check(-Wextra) 41 | add_compile_options_with_check(-pedantic) 42 | add_compile_options_with_check(-Werror) 43 | #add_compile_options_with_check(-Wfatal-errors) 44 | 45 | add_compile_options_with_check(-Wold-style-cast) 46 | # helps catch hard to track down memory errors 47 | add_compile_options_with_check(-Wnon-virtual-dtor) 48 | # warn for potential performance problem casts 49 | add_compile_options_with_check(-Wcast-align) 50 | # warn if you overload (not override) a virtual function 51 | add_compile_options_with_check(-Woverloaded-virtual) 52 | elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel") 53 | # using Intel C++ 54 | message(WARNING "Using of Intel C++ is not supported, you are on your own.") 55 | elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") 56 | # using Visual Studio C++ 57 | 58 | # all reasonable warnings 59 | add_compile_options_with_check(/W4) 60 | # treat warnings as errors 61 | add_compile_options_with_check(/Wx) 62 | # enable warning on thread un-safe static member initialization 63 | add_compile_options_with_check(/W44640) 64 | endif() 65 | 66 | # Other flags are not added directly globally, but using a function 67 | # so later when defining targets, it can be set per target 68 | function(add_libtsm_compile_options target) 69 | # Make all files include "config.h" by default. This shouldn't cause any 70 | # problems and we cannot forget to include it anymore. 71 | target_compile_options(${target} PRIVATE 72 | -include ${CMAKE_BINARY_DIR}/config.h 73 | -pipe 74 | -fno-common 75 | -ffast-math 76 | -fno-strict-aliasing 77 | -ffunction-sections 78 | -fdata-sections 79 | ) 80 | 81 | # Linker flags 82 | ## Make the linker discard all unused symbols. 83 | if(APPLE) 84 | set(LDFLAGS "-Wl,-dead_strip -Wl,-dead_strip_dylibs -Wl,-bind_at_load") 85 | elseif(UNIX) 86 | set(LDFLAGS "-Wl,--as-needed -Wl,--gc-sections -Wl,-z,relro -Wl,-z,now") 87 | else() 88 | message("Unsupported platform, you are on your own.") 89 | endif() 90 | 91 | set_property(TARGET ${target} APPEND_STRING PROPERTY LINK_FLAGS " ${LDFLAGS}") 92 | endfunction() 93 | -------------------------------------------------------------------------------- /cmake/JoinPaths.cmake: -------------------------------------------------------------------------------- 1 | # This module provides function for joining paths 2 | # known from most languages 3 | # 4 | # SPDX-License-Identifier: (MIT OR CC0-1.0) 5 | # Copyright 2020 Jan Tojnar 6 | # https://github.com/jtojnar/cmake-snips 7 | # 8 | # Modelled after Python’s os.path.join 9 | # https://docs.python.org/3.7/library/os.path.html#os.path.join 10 | # Windows not supported 11 | function(join_paths joined_path first_path_segment) 12 | set(temp_path "${first_path_segment}") 13 | foreach(current_segment IN LISTS ARGN) 14 | if(NOT ("${current_segment}" STREQUAL "")) 15 | if(IS_ABSOLUTE "${current_segment}") 16 | set(temp_path "${current_segment}") 17 | else() 18 | set(temp_path "${temp_path}/${current_segment}") 19 | endif() 20 | endif() 21 | endforeach() 22 | set(${joined_path} "${temp_path}" PARENT_SCOPE) 23 | endfunction() 24 | -------------------------------------------------------------------------------- /cmake/Utilities.cmake: -------------------------------------------------------------------------------- 1 | include(CheckCCompilerFlag) 2 | 3 | # 4 | # Check compiler flag support with cached support. 5 | # Shadows the internal check_c_compiler_flag 6 | # 7 | macro(libtsm_check_c_compiler_flag flag retvar) 8 | string(REPLACE "-" "_" cache_name "COMPILER_SUPPORT_${flag}") 9 | check_c_compiler_flag(${flag} ${cache_name}) 10 | set(${retvar} ${cache_name}) 11 | endmacro() 12 | 13 | # Add a global default compile option 14 | function(add_compile_options_with_check flag) 15 | libtsm_check_c_compiler_flag(${flag} ret) 16 | if(${ret}) 17 | add_compile_options(${flag}) 18 | endif() 19 | endfunction() 20 | 21 | # Add a new private compile option to a target 22 | function(target_compile_options_with_check target flag) 23 | libtsm_check_c_compiler_flag(${flag} ret) 24 | if(${ret}) 25 | target_compile_options(${target} PRIVATE ${flag}) 26 | endif() 27 | endfunction() 28 | 29 | # Add compile option to a build type with check 30 | function(build_type_add_compile_option_with_check buildtype flag) 31 | libtsm_check_c_compiler_flag(${flag} ret) 32 | if(${ret}) 33 | set(CMAKE_CXX_FLAGS_${buildtype} "${CMAKE_CXX_FLAGS_${buildtype}}" ${flag}) 34 | endif() 35 | endfunction() 36 | 37 | # Print all properties for a target 38 | # From https://stackoverflow.com/questions/32183975/how-to-print-all-the-properties-of-a-target-in-cmake 39 | # 40 | # Get all propreties that cmake supports 41 | execute_process(COMMAND cmake --help-property-list OUTPUT_VARIABLE CMAKE_PROPERTY_LIST) 42 | # Convert command output into a CMake list 43 | string(REGEX REPLACE ";" "\\\\;" CMAKE_PROPERTY_LIST "${CMAKE_PROPERTY_LIST}") 44 | string(REGEX REPLACE "\n" ";" CMAKE_PROPERTY_LIST "${CMAKE_PROPERTY_LIST}") 45 | list(REMOVE_DUPLICATES CMAKE_PROPERTY_LIST) 46 | 47 | function(print_properties) 48 | message ("CMAKE_PROPERTY_LIST = ${CMAKE_PROPERTY_LIST}") 49 | endfunction(print_properties) 50 | 51 | function(print_target_properties tgt) 52 | if(NOT TARGET ${tgt}) 53 | message("There is no target named '${tgt}'") 54 | return() 55 | endif() 56 | 57 | foreach (prop ${CMAKE_PROPERTY_LIST}) 58 | string(REPLACE "" "${CMAKE_BUILD_TYPE}" prop ${prop}) 59 | # Fix https://stackoverflow.com/questions/32197663/how-can-i-remove-the-the-location-property-may-not-be-read-from-target-error-i 60 | if(prop STREQUAL "LOCATION" OR prop MATCHES "^LOCATION_" OR prop MATCHES "_LOCATION$") 61 | continue() 62 | endif() 63 | # message ("Checking ${prop}") 64 | get_property(propval TARGET ${tgt} PROPERTY ${prop} SET) 65 | if (propval) 66 | get_target_property(propval ${tgt} ${prop}) 67 | message ("${tgt} ${prop} = ${propval}") 68 | endif() 69 | endforeach(prop) 70 | endfunction(print_target_properties) 71 | 72 | # A simpler version to parse package components 73 | # Copied from extra-cmake-modules 74 | macro(find_package_parse_components module_name) 75 | set(fppc_options) 76 | set(fppc_oneValueArgs RESULT_VAR) 77 | set(fppc_multiValueArgs KNOWN_COMPONENTS DEFAULT_COMPONENTS) 78 | cmake_parse_arguments(FPPC "${fppc_options}" "${fppc_oneValueArgs}" "${fppc_multiValueArgs}" ${ARGN}) 79 | 80 | if(FPPC_UNPARSED_ARGUMENTS) 81 | message(FATAL_ERROR "Unexpected arguments to find_package_parse_components: ${FPPC_UNPARSED_ARGUMENTS}") 82 | endif() 83 | if(NOT FPPC_RESULT_VAR) 84 | message(FATAL_ERROR "Missing RESULT_VAR argument to find_package_parse_components") 85 | endif() 86 | if(NOT FPPC_KNOWN_COMPONENTS) 87 | message(FATAL_ERROR "Missing KNOWN_COMPONENTS argument to find_package_parse_components") 88 | endif() 89 | if(NOT FPPC_DEFAULT_COMPONENTS) 90 | set(FPPC_DEFAULT_COMPONENTS ${FPPC_KNOWN_COMPONENTS}) 91 | endif() 92 | 93 | if(${module_name}_FIND_COMPONENTS) 94 | set(fppc_requestedComps ${${module_name}_FIND_COMPONENTS}) 95 | 96 | list(REMOVE_DUPLICATES fppc_requestedComps) 97 | 98 | # This makes sure components are listed in the same order as 99 | # KNOWN_COMPONENTS (potentially important for inter-dependencies) 100 | set(${FPPC_RESULT_VAR}) 101 | foreach(fppc_comp ${FPPC_KNOWN_COMPONENTS}) 102 | list(FIND fppc_requestedComps "${fppc_comp}" fppc_index) 103 | if(NOT "${fppc_index}" STREQUAL "-1") 104 | list(APPEND ${FPPC_RESULT_VAR} "${fppc_comp}") 105 | list(REMOVE_AT fppc_requestedComps ${fppc_index}) 106 | endif() 107 | endforeach() 108 | # if there are any left, they are unknown components 109 | if(fppc_requestedComps) 110 | set(fppc_msgType STATUS) 111 | if(${module_name}_FIND_REQUIRED) 112 | set(fppc_msgType FATAL_ERROR) 113 | endif() 114 | if(NOT ${module_name}_FIND_QUIETLY) 115 | message(${fppc_msgType} "${module_name}: requested unknown components ${fppc_requestedComps}") 116 | endif() 117 | return() 118 | endif() 119 | else() 120 | set(${FPPC_RESULT_VAR} ${FPPC_DEFAULT_COMPONENTS}) 121 | endif() 122 | endmacro() 123 | 124 | # 125 | # Creates an imported library target for each component. See ECM's documentation for usage. 126 | # 127 | # This version addtionally treats empty __lib variable as an indication for interface only library 128 | # 129 | # The following additionall keyword arguments are accepted: 130 | # NAMESPACE - The namespace to use for the imported target, default to 131 | # 132 | macro(find_package_handle_library_components module_name) 133 | set(fphlc_options SKIP_PKG_CONFIG SKIP_DEPENDENCY_HANDLING) 134 | set(fphlc_oneValueArgs NAMESPACE) 135 | set(fphlc_multiValueArgs COMPONENTS) 136 | cmake_parse_arguments(FPHLC "${fphlc_options}" "${fphlc_oneValueArgs}" "${fphlc_multiValueArgs}" ${ARGN}) 137 | 138 | if(FPHLC_UNPARSED_ARGUMENTS) 139 | message(FATAL_ERROR "Unexpected arguments to find_package_handle_components: ${FPHLC_UNPARSED_ARGUMENTS}") 140 | endif() 141 | if(NOT FPHLC_COMPONENTS) 142 | message(FATAL_ERROR "Missing COMPONENTS argument to find_package_handle_components") 143 | endif() 144 | 145 | if(NOT FPHLC_NAMESPACE) 146 | set(FPHLC_NAMESPACE ${module_name}) 147 | endif() 148 | 149 | include(FindPackageHandleStandardArgs) 150 | find_package(PkgConfig QUIET) 151 | foreach(fphlc_comp ${FPHLC_COMPONENTS}) 152 | set(fphlc_dep_vars) 153 | set(fphlc_dep_targets) 154 | if(NOT SKIP_DEPENDENCY_HANDLING) 155 | foreach(fphlc_dep ${${module_name}_${fphlc_comp}_component_deps}) 156 | list(APPEND fphlc_dep_vars "${module_name}_${fphlc_dep}_FOUND") 157 | list(APPEND fphlc_dep_targets "${FPHLC_NAMESPACE}::${fphlc_dep}") 158 | endforeach() 159 | endif() 160 | 161 | if(NOT FPHLC_SKIP_PKG_CONFIG AND ${module_name}_${fphlc_comp}_pkg_config) 162 | pkg_check_modules(PKG_${module_name}_${fphlc_comp} QUIET 163 | ${${module_name}_${fphlc_comp}_pkg_config}) 164 | endif() 165 | 166 | if("${${module_name}_${fphlc_comp}_lib}" STREQUAL "") 167 | set(fphlc_library_type INTERFACE) 168 | set(fphlc_required_vars "${module_name}_${fphlc_comp}_INCLUDE_DIR") 169 | else() 170 | set(fphlc_library_type UNKNOWN) 171 | set(fphlc_required_vars "${module_name}_${fphlc_comp}_LIBRARY;${module_name}_${fphlc_comp}_INCLUDE_DIR") 172 | endif() 173 | 174 | find_path(${module_name}_${fphlc_comp}_INCLUDE_DIR 175 | NAMES ${${module_name}_${fphlc_comp}_header} 176 | HINTS ${PKG_${module_name}_${fphlc_comp}_INCLUDE_DIRS} 177 | PATH_SUFFIXES ${${module_name}_${fphlc_comp}_header_subdir} 178 | ) 179 | find_library(${module_name}_${fphlc_comp}_LIBRARY 180 | NAMES ${${module_name}_${fphlc_comp}_lib} 181 | HINTS ${PKG_${module_name}_${fphlc_comp}_LIBRARY_DIRS} 182 | ) 183 | 184 | set(${module_name}_${fphlc_comp}_VERSION "${PKG_${module_name}_${fphlc_comp}_VERSION}") 185 | if(NOT ${module_name}_VERSION) 186 | set(${module_name}_VERSION ${${module_name}_${fphlc_comp}_VERSION}) 187 | endif() 188 | 189 | find_package_handle_standard_args(${module_name}_${fphlc_comp} 190 | FOUND_VAR 191 | ${module_name}_${fphlc_comp}_FOUND 192 | REQUIRED_VARS 193 | ${fphlc_required_vars} 194 | ${fphlc_dep_vars} 195 | VERSION_VAR 196 | ${module_name}_${fphlc_comp}_VERSION 197 | ) 198 | 199 | mark_as_advanced( 200 | ${module_name}_${fphlc_comp}_LIBRARY 201 | ${module_name}_${fphlc_comp}_INCLUDE_DIR 202 | ) 203 | 204 | if(${module_name}_${fphlc_comp}_FOUND) 205 | if("${fphlc_library_type}" STREQUAL "UNKNOWN") 206 | list(APPEND ${module_name}_LIBRARIES 207 | "${${module_name}_${fphlc_comp}_LIBRARY}") 208 | endif() 209 | list(APPEND ${module_name}_INCLUDE_DIRS 210 | "${${module_name}_${fphlc_comp}_INCLUDE_DIR}") 211 | set(${module_name}_DEFINITIONS 212 | ${${module_name}_DEFINITIONS} 213 | ${PKG_${module_name}_${fphlc_comp}_DEFINITIONS}) 214 | if(NOT TARGET ${FPHLC_NAMESPACE}::${fphlc_comp}) 215 | add_library(${FPHLC_NAMESPACE}::${fphlc_comp} ${fphlc_library_type} IMPORTED) 216 | set_target_properties(${FPHLC_NAMESPACE}::${fphlc_comp} PROPERTIES 217 | INTERFACE_COMPILE_OPTIONS "${PKG_${module_name}_${fphlc_comp}_DEFINITIONS}" 218 | INTERFACE_INCLUDE_DIRECTORIES "${${module_name}_${fphlc_comp}_INCLUDE_DIR}" 219 | INTERFACE_LINK_LIBRARIES "${fphlc_dep_targets}" 220 | ) 221 | if("${fphlc_library_type}" STREQUAL "UNKNOWN") 222 | set_target_properties(${FPHLC_NAMESPACE}::${fphlc_comp} PROPERTIES 223 | IMPORTED_LOCATION "${${module_name}_${fphlc_comp}_LIBRARY}" 224 | ) 225 | endif() 226 | endif() 227 | list(APPEND ${module_name}_TARGETS 228 | "${FPHLC_NAMESPACE}::${fphlc_comp}") 229 | endif() 230 | endforeach() 231 | if(${module_name}_LIBRARIES) 232 | list(REMOVE_DUPLICATES ${module_name}_LIBRARIES) 233 | endif() 234 | if(${module_name}_INCLUDE_DIRS) 235 | list(REMOVE_DUPLICATES ${module_name}_INCLUDE_DIRS) 236 | endif() 237 | if(${module_name}_DEFINITIONS) 238 | list(REMOVE_DUPLICATES ${module_name}_DEFINITIONS) 239 | endif() 240 | if(${module_name}_TARGETS) 241 | list(REMOVE_DUPLICATES ${module_name}_TARGETS) 242 | endif() 243 | endmacro() 244 | 245 | # 246 | # Helper to link to an object library. 247 | # 248 | # The advantage of using this over target_link_library directly 249 | # is that when used privately, it will not pollute target exports, 250 | # See: https://gitlab.kitware.com/cmake/cmake/issues/17357 251 | # 252 | # This is done by not involving target_link_libraries but directly set 253 | # properties. 254 | # 255 | function(target_link_object_libraries target) 256 | set(tlol_options) 257 | set(tlol_oneValueArgs) 258 | set(tlol_multiValueArgs PRIVATE INTERFACE PUBLIC) 259 | cmake_parse_arguments(TLOL "${tlol_options}" "${tlol_oneValueArgs}" "${tlol_multiValueArgs}" ${ARGN}) 260 | 261 | if(TLOL_UNPARSED_ARGUMENTS) 262 | message(FATAL_ERROR "Unexpected arguments to target_link_object_libraries: ${TLOL_UNPARSED_ARGUMENTS}") 263 | endif() 264 | 265 | if(NOT TLOL_PRIVATE AND NOT TLOL_INTERFACE AND NOT TLOL_PUBLIC) 266 | message(FATAL_ERROR "Missing PRIVATE|INTERFACE|PUBLIC argument to target_link_object_libraries") 267 | endif() 268 | 269 | foreach(lib IN LISTS TLOL_PRIVATE) 270 | target_sources(${target} PRIVATE $ $) 271 | target_include_directories(${target} PRIVATE $) 272 | target_compile_options(${target} PRIVATE $) 273 | # This is not supported, as it will make exporting ${target} also needs exporting ${lib} even in PRIVATE mode 274 | #target_link_libraries(${target} PRIVATE $) 275 | endforeach() 276 | foreach(lib IN LISTS TLOL_INTERFACE) 277 | target_sources(${target} INTERFACE $ $) 278 | target_include_directories(${target} INTERFACE $) 279 | target_compile_options(${target} INTERFACE $) 280 | target_link_libraries(${target} INTERFACE $) 281 | endforeach() 282 | foreach(lib IN LISTS TLOL_PUBLIC) 283 | target_sources(${target} PUBLIC $ $) 284 | target_include_directories(${target} PUBLIC $) 285 | target_compile_options(${target} PUBLIC $) 286 | endforeach() 287 | endfunction() 288 | -------------------------------------------------------------------------------- /cmake/modules/FindCairo.cmake: -------------------------------------------------------------------------------- 1 | find_package(PkgConfig QUIET) 2 | pkg_check_modules(Cairo ${Cairo_FIND_REQUIRED} ${Cairo_FIND_QUITELY} cairo IMPORTED_TARGET) 3 | -------------------------------------------------------------------------------- /cmake/modules/FindCheck.cmake: -------------------------------------------------------------------------------- 1 | # Try to find libcheck 2 | # 3 | # This will define: 4 | # 5 | # Check_FOUND - True if libcheck is available 6 | # Check_LIBRARIES - Found libraries for libcheck 7 | # Check_INCLUDE_DIRS - Include directory for libcheck 8 | # Check_CFLAGS - Other compiler flags for using libcheck 9 | # Check_VERSION - Version of the found libcheck 10 | # 11 | # Additionally, the following imported targets will be defined: 12 | # 13 | # check::check 14 | # 15 | include(FindPackageHandleStandardArgs) 16 | 17 | set(module_name Check) 18 | 19 | # We rely on pkg-config to provide dependency info. 20 | find_package(PkgConfig REQUIRED QUIET) 21 | 22 | pkg_check_modules(PKG_${module_name} QUIET check) 23 | 24 | find_path(${module_name}_INCLUDE_DIR 25 | NAMES check.h 26 | HINTS ${PKG_${module_name}_INCLUDE_DIRS} 27 | ) 28 | find_library(${module_name}_LIBRARY 29 | NAMES check_pic check 30 | HINTS ${PKG_${module_name}_LIBRARY_DIRS} 31 | ) 32 | 33 | set(${module_name}_VERSION "${PKG_${module_name}_VERSION}") 34 | if(NOT ${module_name}_VERSION) 35 | set(${module_name}_VERSION ${${module_name}_VERSION}) 36 | 37 | if(${module_name}_INCLUDE_DIR) 38 | file(STRINGS "${${module_name}_INCLUDE_DIR}/check.h" ${module_name}_MAJOR_VERSION REGEX "^#define CHECK_MAJOR_VERSION +\\(?([0-9]+)\\)?$") 39 | string(REGEX REPLACE "^#define CHECK_MAJOR_VERSION \\(?([0-9]+)\\)?" "\\1" ${module_name}_MAJOR_VERSION "${${module_name}_MAJOR_VERSION}") 40 | file(STRINGS "${${module_name}_INCLUDE_DIR}/check.h" ${module_name}_MINOR_VERSION REGEX "^#define CHECK_MINOR_VERSION +\\(?([0-9]+)\\)?$") 41 | string(REGEX REPLACE "^#define CHECK_MINOR_VERSION \\(?([0-9]+)\\)?" "\\1" ${module_name}_MINOR_VERSION "${${module_name}_MINOR_VERSION}") 42 | file(STRINGS "${${module_name}_INCLUDE_DIR}/check.h" ${module_name}_MICRO_VERSION REGEX "^#define CHECK_MICRO_VERSION +\\(?([0-9]+)\\)?$") 43 | string(REGEX REPLACE "^#define CHECK_MICRO_VERSION \\(?([0-9]+)\\)?" "\\1" ${module_name}_MICRO_VERSION "${${module_name}_MICRO_VERSION}") 44 | set(${module_name}_VERSION "${${module_name}_MAJOR_VERSION}.${${module_name}_MINOR_VERSION}.${${module_name}_MICRO_VERSION}") 45 | unset(${module_name}_MAJOR_VERSION) 46 | unset(${module_name}_MINOR_VERSION) 47 | unset(${module_name}_MICRO_VERSION) 48 | endif() 49 | endif() 50 | 51 | find_package_handle_standard_args(${module_name} 52 | FOUND_VAR 53 | ${module_name}_FOUND 54 | REQUIRED_VARS 55 | ${module_name}_LIBRARY 56 | ${module_name}_INCLUDE_DIR 57 | VERSION_VAR 58 | ${module_name}_VERSION 59 | ) 60 | 61 | mark_as_advanced( 62 | ${module_name}_LIBRARY 63 | ${module_name}_INCLUDE_DIR 64 | ) 65 | 66 | if(${module_name}_FOUND) 67 | list(APPEND ${module_name}_LIBRARIES "${PKG_${module_name}_LIBRARIES}") 68 | list(APPEND ${module_name}_CFLAGS "${PKG_${module_name}_CFLAGS_OTHER}") 69 | list(APPEND ${module_name}_LDFLAGS "${PKG_${module_name}_LDFLAGS_OTHER}") 70 | 71 | message("PKG_${module_name}_LDFLAGS_OTHER = ${PKG_${module_name}_LDFLAGS_OTHER}") 72 | 73 | if("-pthread" IN_LIST ${module_name}_LDFLAGS) 74 | include(CMakeFindDependencyMacro) 75 | find_dependency(Threads REQUIRED) 76 | list(APPEND ${module_name}_LIBRARIES Threads::Threads) 77 | endif() 78 | 79 | list(APPEND ${module_name}_INCLUDE_DIRS "${${module_name}_INCLUDE_DIR}") 80 | if(NOT TARGET check::check) 81 | add_library(check::check INTERFACE IMPORTED) 82 | set_target_properties(check::check PROPERTIES 83 | INTERFACE_COMPILE_OPTIONS "${${module_name}_CFLAGS}" 84 | INTERFACE_INCLUDE_DIRECTORIES "${${module_name}_INCLUDE_DIRS}" 85 | INTERFACE_LINK_LIBRARIES "${${module_name}_LIBRARIES}" 86 | ) 87 | endif() 88 | endif() 89 | 90 | include(FeatureSummary) 91 | set_package_properties(${module_name} PROPERTIES 92 | URL "https://libcheck.github.io/check/" 93 | DESCRIPTION "A unit testing framework for C" 94 | ) 95 | 96 | unset(module_name) 97 | -------------------------------------------------------------------------------- /cmake/modules/FindGTK3.cmake: -------------------------------------------------------------------------------- 1 | find_package(PkgConfig QUIET) 2 | pkg_check_modules(GTK3 ${GTK3_FIND_REQUIRED} ${GTK3_FIND_QUIETLY} gtk+-3.0 IMPORTED_TARGET) 3 | -------------------------------------------------------------------------------- /cmake/modules/FindPango.cmake: -------------------------------------------------------------------------------- 1 | find_package(PkgConfig QUIET) 2 | pkg_check_modules(Pango ${Pango_FIND_REQUIRED} ${Pango_FIND_QUIETLY} pango IMPORTED_TARGET) 3 | -------------------------------------------------------------------------------- /cmake/modules/FindPangoCairo.cmake: -------------------------------------------------------------------------------- 1 | # FindPangoCairo.cmake 2 | # 3 | # CMake support for PangoCairo. 4 | # 5 | find_package(PkgConfig QUIET) 6 | pkg_check_modules(PangoCairo ${PangoCairo_FIND_REQUIRED} ${PangoCairo_FIND_QUIETLY} pangocairo IMPORTED_TARGET) 7 | -------------------------------------------------------------------------------- /cmake/modules/FindXKBCommon.cmake: -------------------------------------------------------------------------------- 1 | # Try to find xkbcommon 2 | # 3 | # Available components: 4 | # XKBCommon - The whole xkbcommon library 5 | # KeySyms - The interface library containing only xkbcommon-keysyms.h 6 | # 7 | # This will define: 8 | # 9 | # XKBCommon_FOUND - True if xkbcommon is available 10 | # XKBCommon_LIBRARIES - Found libraries for xkbcommon 11 | # XKBCommon_INCLUDE_DIRS - Include directory for xkbcommon 12 | # XKBCommon_DEFINITIONS - Compiler flags for using xkbcommon 13 | # 14 | # Additionally, the following imported targets will be defined: 15 | # 16 | # XKB::XKBCommon 17 | # XKB::KeySyms 18 | # 19 | include(FindPackageHandleStandardArgs) 20 | 21 | # Note that this list needs to be ordered such that any component appears after its dependencies 22 | set(XKBCommon_known_components 23 | XKBCommon 24 | KeySyms 25 | ) 26 | 27 | set(XKBCommon_XKBCommon_component_deps) 28 | set(XKBCommon_XKBCommon_pkg_config "xkbcommon") 29 | set(XKBCommon_XKBCommon_lib "xkbcommon") 30 | set(XKBCommon_XKBCommon_header "xkbcommon/xkbcommon.h") 31 | 32 | set(XKBCommon_KeySyms_component_deps) 33 | set(XKBCommon_KeySyms_pkg_config "xkbcommon") 34 | set(XKBCommon_KeySyms_lib) 35 | set(XKBCommon_KeySyms_header "xkbcommon/xkbcommon-keysyms.h") 36 | 37 | # Parse components 38 | find_package_parse_components(XKBCommon 39 | RESULT_VAR XKBCommon_components 40 | KNOWN_COMPONENTS ${XKBCommon_known_components} 41 | ) 42 | 43 | # Find each component 44 | # use our private version of find_package_handle_library_components 45 | # that also handles interface library 46 | find_package_handle_library_components(XKBCommon 47 | NAMESPACE XKB 48 | COMPONENTS ${XKBCommon_components} 49 | ) 50 | 51 | set(XKBCommon_component_FOUND_VARS) 52 | foreach(comp ${XKBCommon_components}) 53 | list(APPEND XKBCommon_component_FOUND_VARS "XKBCommon_${comp}_FOUND") 54 | endforeach(comp) 55 | 56 | find_package_handle_standard_args(XKBCommon 57 | FOUND_VAR 58 | XKBCommon_FOUND 59 | REQUIRED_VARS 60 | ${XKBCommon_component_FOUND_VARS} 61 | VERSION_VAR 62 | XKBCommon_VERSION 63 | HANDLE_COMPONENTS 64 | ) 65 | 66 | unset(XKBCommon_component_FOUND_VARS) 67 | 68 | include(FeatureSummary) 69 | set_package_properties(XKBCommon PROPERTIES 70 | URL "http://xkbcommon.org" 71 | DESCRIPTION "Keyboard handling library using XKB data" 72 | ) 73 | -------------------------------------------------------------------------------- /etc/libtsm-config.cmake.in: -------------------------------------------------------------------------------- 1 | # 2 | # libtsm 3 | # ------ 4 | # 5 | # The following imported targets are created: 6 | # libtsm::libtsm 7 | # 8 | 9 | @PACKAGE_INIT@ 10 | 11 | if(NOT TARGET libtsm::tsm) 12 | include("${CMAKE_CURRENT_LIST_DIR}/libtsm-targets.cmake") 13 | endif() 14 | -------------------------------------------------------------------------------- /etc/libtsm.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@CMAKE_INSTALL_PREFIX@ 2 | exec_prefix=${prefix} 3 | libdir=@libdir_for_pc_file@ 4 | includedir=@includedir_for_pc_file@ 5 | 6 | Name: @PROJECT_NAME@ 7 | Description: @PROJECT_DESCRIPTION@ 8 | URL: @PROJECT_HOMEPAGE_URL@ 9 | Version: @PROJECT_VERSION@ 10 | Libs: -L${libdir} -ltsm 11 | Cflags: -I${includedir} 12 | -------------------------------------------------------------------------------- /etc/test.supp: -------------------------------------------------------------------------------- 1 | # 2 | # Some suppression rules for our test-suite to work correctly. 3 | # libcheck has some weird errors, so lets ignore them. 4 | # 5 | 6 | { 7 | libcheck timer_create warnings 8 | Memcheck:Param 9 | timer_create(evp) 10 | fun:timer_create 11 | obj:/usr/lib/libcheck.so.0.0.0 12 | obj:/usr/lib/libcheck.so.0.0.0 13 | fun:srunner_run 14 | fun:main 15 | } 16 | -------------------------------------------------------------------------------- /external/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # External vendorized code 3 | # build everything into a object library for ease of use internally 4 | # 5 | add_library(external OBJECT 6 | wcwidth/wcwidth.c 7 | ) 8 | 9 | target_include_directories(external 10 | PUBLIC 11 | ${CMAKE_CURRENT_SOURCE_DIR} 12 | ${CMAKE_CURRENT_SOURCE_DIR}/wcwidth 13 | ) 14 | set_target_properties(external PROPERTIES 15 | C_VISIBILITY_PRESET hidden 16 | POSITION_INDEPENDENT_CODE ON 17 | ) 18 | add_libtsm_compile_options(external) 19 | -------------------------------------------------------------------------------- /external/wcwidth/.gitignore: -------------------------------------------------------------------------------- 1 | test_wcwidth 2 | -------------------------------------------------------------------------------- /external/wcwidth/.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: c 3 | compiler: 4 | - clang 5 | - gcc 6 | os: 7 | - linux 8 | - osx 9 | script: make test 10 | -------------------------------------------------------------------------------- /external/wcwidth/LICENSE.txt: -------------------------------------------------------------------------------- 1 | This project is licensed for use as follows: 2 | 3 | """ 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2016 Fredrik Fornwall 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | """ 26 | 27 | This license applies to parts originating from the 28 | https://github.com/jquast/wcwidth repository: 29 | 30 | """ 31 | The MIT License (MIT) 32 | 33 | Copyright (c) 2014 Jeff Quast 34 | 35 | Permission is hereby granted, free of charge, to any person obtaining a copy 36 | of this software and associated documentation files (the "Software"), to deal 37 | in the Software without restriction, including without limitation the rights 38 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 39 | copies of the Software, and to permit persons to whom the Software is 40 | furnished to do so, subject to the following conditions: 41 | 42 | The above copyright notice and this permission notice shall be included in all 43 | copies or substantial portions of the Software. 44 | 45 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 46 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 47 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 48 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 49 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 50 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 51 | SOFTWARE. 52 | """ 53 | -------------------------------------------------------------------------------- /external/wcwidth/README.md: -------------------------------------------------------------------------------- 1 | A wcwidth implementation in C 2 | ============================= 3 | [![Travis build status](https://travis-ci.org/termux/wcwidth.svg?branch=master)](https://travis-ci.org/termux/wcwidth) 4 | 5 | A simple implementation of [wcwidth(3)](http://pubs.opengroup.org/onlinepubs/009695399/functions/wcwidth.html) 6 | in C as a port of [wcwidth.py](https://github.com/jquast/wcwidth/blob/master/wcwidth/wcwidth.py). 7 | 8 | Used in [Termux](https://termux.com) as part of the `libandroid-support.so` shared library. 9 | -------------------------------------------------------------------------------- /external/wcwidth/test_wcwidth.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "wcwidth.h" 6 | 7 | static int tests_run; 8 | static int test_failures; 9 | 10 | void assertWidthIs(int expected_width, wchar_t c) { 11 | tests_run++; 12 | int actual_width = wcwidth(c); 13 | if (actual_width != expected_width) { 14 | fprintf(stderr, "ERROR: wcwidth(U+%04x) returned %d, expected %d\n", c, actual_width, expected_width); 15 | test_failures++; 16 | } 17 | } 18 | 19 | int main() { 20 | assertWidthIs(1, 'a'); 21 | assertWidthIs(1, L'ö'); 22 | 23 | // Some wide: 24 | assertWidthIs(2, L'A'); 25 | assertWidthIs(2, L'B'); 26 | assertWidthIs(2, L'C'); 27 | assertWidthIs(2, L'中'); 28 | assertWidthIs(2, L'文'); 29 | assertWidthIs(2, 0x679C); 30 | assertWidthIs(2, 0x679D); 31 | assertWidthIs(2, 0x2070E); 32 | assertWidthIs(2, 0x20731); 33 | 34 | assertWidthIs(1, 0x11A3); 35 | 36 | assertWidthIs(2, 0x1F428); // Koala emoji. 37 | assertWidthIs(2, 0x231a); // Watch emoji. 38 | 39 | if (test_failures > 0) printf("%d tests FAILED, ", test_failures); 40 | printf("%d tests OK\n", tests_run); 41 | return (test_failures == 0) ? 0 : 1; 42 | } 43 | 44 | -------------------------------------------------------------------------------- /external/wcwidth/wcwidth.h: -------------------------------------------------------------------------------- 1 | #ifndef WCWIDTH_H_INCLUDED 2 | #define WCWIDTH_H_INCLUDED 3 | 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | int wcwidth(wchar_t ucs); 11 | 12 | #ifdef __cplusplus 13 | } 14 | #endif 15 | 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(shared) 2 | add_subdirectory(tsm) 3 | 4 | if(BUILD_GTKTSM) 5 | add_subdirectory(gtktsm) 6 | endif() 7 | -------------------------------------------------------------------------------- /src/config.h.in: -------------------------------------------------------------------------------- 1 | #ifndef LIBTSM_CONFIG_H 2 | #define LIBTSM_CONFIG_H 3 | 4 | /* Enable debug mode */ 5 | #cmakedefine BUILD_ENABLE_DEBUG 6 | 7 | /* Have xkbcommon library */ 8 | #cmakedefine BUILD_HAVE_XKBCOMMON 9 | 10 | #endif // LIBTSM_CONFIG_H 11 | -------------------------------------------------------------------------------- /src/gtktsm/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # GtkTsm - Example 3 | # 4 | add_executable(gtktsm 5 | gtktsm.c 6 | gtktsm-app.c 7 | gtktsm-terminal.c 8 | gtktsm-win.c 9 | ) 10 | target_link_libraries(gtktsm 11 | PRIVATE 12 | tsm 13 | 14 | m 15 | PkgConfig::GTK3 16 | PkgConfig::Cairo 17 | PkgConfig::Pango 18 | PkgConfig::PangoCairo 19 | XKB::XKBCommon 20 | ) 21 | target_link_object_libraries(gtktsm 22 | PRIVATE 23 | shl 24 | ) 25 | add_libtsm_compile_options(gtktsm) 26 | 27 | install(TARGETS gtktsm 28 | RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" 29 | ) 30 | -------------------------------------------------------------------------------- /src/gtktsm/gtktsm-app.c: -------------------------------------------------------------------------------- 1 | /* 2 | * GtkTsm - Terminal Emulator 3 | * 4 | * Copyright (c) 2011-2014 David Herrmann 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include "gtktsm-app.h" 30 | #include "gtktsm-win.h" 31 | #include "shl-macro.h" 32 | 33 | struct _GtkTsmApp { 34 | GtkApplication parent; 35 | }; 36 | 37 | struct _GtkTsmAppClass { 38 | GtkApplicationClass parent_class; 39 | }; 40 | 41 | G_DEFINE_TYPE(GtkTsmApp, gtktsm_app, GTK_TYPE_APPLICATION); 42 | 43 | static gboolean arg_version; 44 | 45 | static GOptionEntry app_options[] = { 46 | { "version", 0, 0, G_OPTION_ARG_NONE, &arg_version, "Show version information and exit", NULL }, 47 | { "font", 0, 0, G_OPTION_ARG_STRING, NULL, "Terminal font", "FONT" }, 48 | { "sb-size", 0, 0, G_OPTION_ARG_INT, NULL, "Scroll-back buffer size in lines", "COUNT" }, 49 | { "anti-aliasing", 0, 0, G_OPTION_ARG_STRING, NULL, "Anti-aliasing mode for font rendering", "{none,gray,subpixel,default}" }, 50 | { "subpixel-order", 0, 0, G_OPTION_ARG_STRING, NULL, "Subpixel order for font rendering", "{rgb,bgr,vrgb,vbgr,default}" }, 51 | { "show-dirty", 0, 0, G_OPTION_ARG_NONE, NULL, "Mark dirty cells during redraw", NULL }, 52 | { "debug", 0, 0, G_OPTION_ARG_NONE, NULL, "Enable extensive live-debugging", NULL }, 53 | { NULL } 54 | }; 55 | 56 | static gint app_handle_local_options(GApplication *gapp, 57 | GVariantDict *opts) 58 | { 59 | if (arg_version) { 60 | g_print("GtkTsm\n"); 61 | return 0; 62 | } 63 | 64 | return -1; 65 | } 66 | 67 | static gint app_command_line(GApplication *gapp, 68 | GApplicationCommandLine *cmd) 69 | { 70 | GtkTsmApp *app = GTKTSM_APP(gapp); 71 | GtkTsmWin *win; 72 | GtkTsmTerminal *term; 73 | GVariantDict *dict; 74 | const gchar *sval; 75 | GVariant *val; 76 | int r; 77 | 78 | win = gtktsm_win_new(app); 79 | term = gtktsm_win_get_terminal(win); 80 | dict = g_application_command_line_get_options_dict(cmd); 81 | 82 | val = g_variant_dict_lookup_value(dict, 83 | "font", 84 | G_VARIANT_TYPE_STRING); 85 | if (val) 86 | g_object_set(G_OBJECT(term), 87 | "font", g_variant_get_string(val, NULL), 88 | NULL); 89 | 90 | val = g_variant_dict_lookup_value(dict, 91 | "sb-size", 92 | G_VARIANT_TYPE_INT32); 93 | if (val) 94 | g_object_set(G_OBJECT(term), 95 | "sb-size", shl_max(g_variant_get_int32(val), 0), 96 | NULL); 97 | 98 | val = g_variant_dict_lookup_value(dict, 99 | "anti-aliasing", 100 | G_VARIANT_TYPE_STRING); 101 | if (val) { 102 | sval = g_variant_get_string(val, NULL); 103 | if (strcmp(sval, "none") && 104 | strcmp(sval, "gray") && 105 | strcmp(sval, "subpixel") && 106 | strcmp(sval, "default")) { 107 | g_application_command_line_printerr(cmd, 108 | "invalid anti-aliasing argument: %s\n", 109 | sval); 110 | r = 1; 111 | goto error; 112 | } 113 | 114 | g_object_set(G_OBJECT(term), 115 | "anti-aliasing", sval, 116 | NULL); 117 | } 118 | 119 | val = g_variant_dict_lookup_value(dict, 120 | "subpixel-order", 121 | G_VARIANT_TYPE_STRING); 122 | if (val) { 123 | sval = g_variant_get_string(val, NULL); 124 | if (strcmp(sval, "rgb") && 125 | strcmp(sval, "bgr") && 126 | strcmp(sval, "vrgb") && 127 | strcmp(sval, "vbgr") && 128 | strcmp(sval, "default")) { 129 | g_application_command_line_printerr(cmd, 130 | "invalid subpixel-order argument: %s\n", 131 | sval); 132 | r = 1; 133 | goto error; 134 | } 135 | 136 | g_object_set(G_OBJECT(term), 137 | "subpixel-order", sval, 138 | NULL); 139 | } 140 | 141 | val = g_variant_dict_lookup_value(dict, 142 | "show-dirty", 143 | G_VARIANT_TYPE_BOOLEAN); 144 | if (val) 145 | g_object_set(G_OBJECT(term), 146 | "show-dirty", g_variant_get_boolean(val), 147 | NULL); 148 | 149 | val = g_variant_dict_lookup_value(dict, 150 | "debug", 151 | G_VARIANT_TYPE_BOOLEAN); 152 | if (val) 153 | g_object_set(G_OBJECT(term), 154 | "debug", g_variant_get_boolean(val), 155 | NULL); 156 | 157 | gtktsm_win_run(win); 158 | gtk_window_present(GTK_WINDOW(win)); 159 | 160 | return 0; 161 | 162 | error: 163 | gtk_widget_destroy(GTK_WIDGET(win)); 164 | return r; 165 | } 166 | 167 | static void app_activate(GApplication *gapp) 168 | { 169 | GtkTsmApp *app = GTKTSM_APP(gapp); 170 | GtkTsmWin *win; 171 | 172 | win = gtktsm_win_new(app); 173 | gtktsm_win_run(win); 174 | gtk_window_present(GTK_WINDOW(win)); 175 | } 176 | 177 | static void gtktsm_app_init(GtkTsmApp *app) 178 | { 179 | g_application_add_main_option_entries(G_APPLICATION(app), 180 | app_options); 181 | } 182 | 183 | static void gtktsm_app_class_init(GtkTsmAppClass *klass) 184 | { 185 | GApplicationClass *gapp_klass; 186 | 187 | gapp_klass = G_APPLICATION_CLASS(klass); 188 | gapp_klass->handle_local_options = app_handle_local_options; 189 | gapp_klass->command_line = app_command_line; 190 | gapp_klass->activate = app_activate; 191 | } 192 | 193 | GtkTsmApp *gtktsm_app_new(void) 194 | { 195 | return g_object_new(GTKTSM_APP_TYPE, 196 | "application-id", "org.freedesktop.libtsm.gtktsm", 197 | "flags", G_APPLICATION_SEND_ENVIRONMENT | 198 | G_APPLICATION_HANDLES_COMMAND_LINE, 199 | NULL); 200 | } 201 | -------------------------------------------------------------------------------- /src/gtktsm/gtktsm-app.h: -------------------------------------------------------------------------------- 1 | /* 2 | * GtkTsm - Terminal Emulator 3 | * 4 | * Copyright (c) 2011-2014 David Herrmann 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | #ifndef GTKTSM_APP_H 27 | #define GTKTSM_APP_H 28 | 29 | #include 30 | #include 31 | 32 | typedef struct _GtkTsmApp GtkTsmApp; 33 | typedef struct _GtkTsmAppClass GtkTsmAppClass; 34 | 35 | #define GTKTSM_APP_TYPE (gtktsm_app_get_type()) 36 | #define GTKTSM_APP(_obj) (G_TYPE_CHECK_INSTANCE_CAST((_obj), \ 37 | GTKTSM_APP_TYPE, \ 38 | GtkTsmApp)) 39 | 40 | GType gtktsm_app_get_type(void); 41 | GtkTsmApp *gtktsm_app_new(void); 42 | 43 | #endif /* GTKTSM_APP_H */ 44 | -------------------------------------------------------------------------------- /src/gtktsm/gtktsm-terminal.h: -------------------------------------------------------------------------------- 1 | /* 2 | * GtkTsm - Terminal Emulator 3 | * 4 | * Copyright (c) 2011-2014 David Herrmann 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | #ifndef GTKTSM_TERMINAL_H 27 | #define GTKTSM_TERMINAL_H 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | G_BEGIN_DECLS 36 | 37 | typedef struct _GtkTsmTerminal GtkTsmTerminal; 38 | typedef struct _GtkTsmTerminalClass GtkTsmTerminalClass; 39 | 40 | struct _GtkTsmTerminal { 41 | GtkDrawingArea parent; 42 | }; 43 | 44 | struct _GtkTsmTerminalClass { 45 | GtkDrawingAreaClass parent_class; 46 | }; 47 | 48 | #define GTKTSM_TYPE_TERMINAL (gtktsm_terminal_get_type()) 49 | #define GTKTSM_TERMINAL(_obj) (G_TYPE_CHECK_INSTANCE_CAST((_obj), \ 50 | GTKTSM_TYPE_TERMINAL, \ 51 | GtkTsmTerminal)) 52 | #define GTKTSM_TERMINAL_CLASS(_klass) \ 53 | (G_TYPE_CHECK_CLASS_CAST((_klass), \ 54 | GTKTSM_TYPE_TERMINAL, \ 55 | GtkTsmTerminalClass)) 56 | #define GTKTSM_IS_TERMINAL(_obj) \ 57 | (G_TYPE_CHECK_INSTANCE_TYPE((_obj), \ 58 | GTKTSM_TYPE_TERMINAL)) 59 | #define GTKTSM_IS_TERMINAL_CLASS(_klass) \ 60 | (G_TYPE_CHECK_CLASS_TYPE((_klass), \ 61 | GTKTSM_TYPE_TERMINAL)) 62 | #define GTKTSM_TERMINAL_GET_CLASS(_obj) \ 63 | (G_TYPE_INSTANCE_GET_CLASS((_obj), \ 64 | GTKTSM_TYPE_TERMINAL, \ 65 | GtkTsmTerminalClass)) 66 | 67 | #define GTKTSM_TERMINAL_DONT_CARE (-1) 68 | 69 | GType gtktsm_terminal_get_type(void) G_GNUC_CONST; 70 | GtkTsmTerminal *gtktsm_terminal_new(void); 71 | 72 | pid_t gtktsm_terminal_fork(GtkTsmTerminal *term); 73 | void gtktsm_terminal_kill(GtkTsmTerminal *term, int sig); 74 | 75 | G_END_DECLS 76 | 77 | #endif /* GTKTSM_TERMINAL_H */ 78 | -------------------------------------------------------------------------------- /src/gtktsm/gtktsm-win.c: -------------------------------------------------------------------------------- 1 | /* 2 | * GtkTsm - Terminal Emulator 3 | * 4 | * Copyright (c) 2011-2014 David Herrmann 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include "gtktsm-app.h" 34 | #include "gtktsm-terminal.h" 35 | #include "gtktsm-win.h" 36 | #include "shl-macro.h" 37 | 38 | struct _GtkTsmWin { 39 | GtkWindow parent; 40 | }; 41 | 42 | struct _GtkTsmWinClass { 43 | GtkWindowClass parent_class; 44 | }; 45 | 46 | typedef struct _GtkTsmWinPrivate { 47 | GtkTsmTerminal *term; 48 | } GtkTsmWinPrivate; 49 | 50 | G_DEFINE_TYPE_WITH_PRIVATE(GtkTsmWin, gtktsm_win, GTK_TYPE_WINDOW); 51 | 52 | static void win_realize(GtkWidget *widget) 53 | { 54 | GtkTsmWin *win = GTKTSM_WIN(widget); 55 | GdkWindow *w; 56 | GdkRGBA col; 57 | 58 | GTK_WIDGET_CLASS(gtktsm_win_parent_class)->realize(widget); 59 | 60 | w = gtk_widget_get_window(GTK_WIDGET(win)); 61 | col.red = 0.0; 62 | col.green = 0.0; 63 | col.blue = 0.0; 64 | col.alpha = 1.0; 65 | gdk_window_set_background_rgba(w, &col); 66 | } 67 | 68 | static gboolean win_title_fn(GtkTsmTerminal *term, gchar *title, gpointer data) 69 | { 70 | GtkTsmWin *win = data; 71 | 72 | gtk_window_set_title(GTK_WINDOW(win), title); 73 | 74 | return FALSE; 75 | } 76 | 77 | static gboolean win_stopped_fn(GtkTsmTerminal *term, gpointer data) 78 | { 79 | GtkTsmWin *win = data; 80 | 81 | gtk_widget_destroy(GTK_WIDGET(win)); 82 | 83 | return FALSE; 84 | } 85 | 86 | static void gtktsm_win_init(GtkTsmWin *win) 87 | { 88 | GtkTsmWinPrivate *p = gtktsm_win_get_instance_private(win); 89 | 90 | gtk_window_set_default_size(GTK_WINDOW(win), 800, 600); 91 | 92 | p->term = gtktsm_terminal_new(); 93 | gtk_container_add(GTK_CONTAINER(win), GTK_WIDGET(p->term)); 94 | gtk_widget_show(GTK_WIDGET(p->term)); 95 | 96 | g_signal_connect(G_OBJECT(p->term), 97 | "terminal-title-changed", 98 | G_CALLBACK(win_title_fn), 99 | win); 100 | 101 | g_signal_connect(G_OBJECT(p->term), 102 | "terminal-stopped", 103 | G_CALLBACK(win_stopped_fn), 104 | win); 105 | } 106 | 107 | static void gtktsm_win_class_init(GtkTsmWinClass *klass) 108 | { 109 | GTK_WIDGET_CLASS(klass)->realize = win_realize; 110 | } 111 | 112 | GtkTsmWin *gtktsm_win_new(GtkTsmApp *app) 113 | { 114 | return g_object_new(GTKTSM_WIN_TYPE, 115 | "application", app, 116 | NULL); 117 | } 118 | 119 | void gtktsm_win_run(GtkTsmWin *win) 120 | { 121 | char **argv = (char*[]) { 122 | getenv("SHELL") ? : _PATH_BSHELL, 123 | NULL 124 | }; 125 | GtkTsmWinPrivate *p; 126 | pid_t pid; 127 | int r; 128 | 129 | if (!win) 130 | return; 131 | 132 | p = gtktsm_win_get_instance_private(win); 133 | pid = gtktsm_terminal_fork(p->term); 134 | if (pid < 0) { 135 | if (pid != -EALREADY) 136 | g_error("gtktsm_terminal_fork() failed: %d", (int)pid); 137 | 138 | return; 139 | } else if (pid != 0) { 140 | /* parent */ 141 | return; 142 | } 143 | 144 | /* child */ 145 | 146 | setenv("TERM", "xterm-256color", 1); 147 | setenv("COLORTERM", "gtktsm", 1); 148 | 149 | r = execve(argv[0], argv, environ); 150 | if (r < 0) 151 | g_error("gtktsm_win_run() execve(%s) failed: %m", 152 | argv[0]); 153 | 154 | _exit(1); 155 | } 156 | 157 | GtkTsmTerminal *gtktsm_win_get_terminal(GtkTsmWin *win) 158 | { 159 | GtkTsmWinPrivate *p; 160 | 161 | if (!win) 162 | return NULL; 163 | 164 | p = gtktsm_win_get_instance_private(win); 165 | return p->term; 166 | } 167 | -------------------------------------------------------------------------------- /src/gtktsm/gtktsm-win.h: -------------------------------------------------------------------------------- 1 | /* 2 | * GtkTsm - Terminal Emulator 3 | * 4 | * Copyright (c) 2011-2014 David Herrmann 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | #ifndef GTKTSM_WIN_H 27 | #define GTKTSM_WIN_H 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include "gtktsm-app.h" 34 | #include "gtktsm-terminal.h" 35 | 36 | typedef struct _GtkTsmWin GtkTsmWin; 37 | typedef struct _GtkTsmWinClass GtkTsmWinClass; 38 | 39 | #define GTKTSM_WIN_TYPE (gtktsm_win_get_type()) 40 | #define GTKTSM_WIN(_obj) (G_TYPE_CHECK_INSTANCE_CAST((_obj), \ 41 | GTKTSM_WIN_TYPE, \ 42 | GtkTsmWin)) 43 | 44 | GType gtktsm_win_get_type(void); 45 | GtkTsmWin *gtktsm_win_new(GtkTsmApp *app); 46 | 47 | void gtktsm_win_run(GtkTsmWin *win); 48 | GtkTsmTerminal *gtktsm_win_get_terminal(GtkTsmWin *win); 49 | 50 | #endif /* GTKTSM_WIN_H */ 51 | -------------------------------------------------------------------------------- /src/gtktsm/gtktsm.c: -------------------------------------------------------------------------------- 1 | /* 2 | * GtkTsm - Terminal Emulator 3 | * 4 | * Copyright (c) 2011-2014 David Herrmann 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include "gtktsm-app.h" 30 | 31 | int main(int argc, char **argv) 32 | { 33 | GApplication *app; 34 | int r; 35 | 36 | setlocale(LC_ALL, ""); 37 | g_set_application_name("GtkTsm"); 38 | 39 | app = G_APPLICATION(gtktsm_app_new()); 40 | r = g_application_run(app, argc, argv); 41 | 42 | g_object_unref(app); 43 | return r; 44 | } 45 | -------------------------------------------------------------------------------- /src/shared/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # SHL - Static Helper Library 3 | # The SHL subsystem contains several small code pieces used all over libtsm and 4 | # other applications. 5 | # 6 | add_library(shl OBJECT 7 | shl-htable.c 8 | ) 9 | 10 | if (BUILD_GTKTSM) 11 | target_sources(shl PRIVATE shl-pty.c shl-ring.c) 12 | endif() 13 | 14 | target_include_directories(shl 15 | PUBLIC 16 | ${CMAKE_CURRENT_SOURCE_DIR} 17 | ) 18 | set_target_properties(shl PROPERTIES 19 | C_VISIBILITY_PRESET hidden 20 | POSITION_INDEPENDENT_CODE ON 21 | ) 22 | add_libtsm_compile_options(shl) 23 | -------------------------------------------------------------------------------- /src/shared/shl-array.h: -------------------------------------------------------------------------------- 1 | /* 2 | * shl - Dynamic Array 3 | * 4 | * Copyright (c) 2011-2013 David Herrmann 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | /* 27 | * A dynamic array implementation 28 | */ 29 | 30 | #ifndef SHL_ARRAY_H 31 | #define SHL_ARRAY_H 32 | 33 | #include 34 | #include 35 | #include 36 | 37 | struct shl_array { 38 | size_t element_size; 39 | size_t length; 40 | size_t size; 41 | void *data; 42 | }; 43 | 44 | #define SHL_ARRAY_AT(_arr, _type, _pos) \ 45 | (&((_type*)shl_array_get_array(_arr))[(_pos)]) 46 | 47 | static inline int shl_array_new(struct shl_array **out, size_t element_size, 48 | size_t initial_size) 49 | { 50 | struct shl_array *arr; 51 | 52 | if (!out || !element_size) 53 | return -EINVAL; 54 | 55 | if (!initial_size) 56 | initial_size = 4; 57 | 58 | arr = malloc(sizeof(*arr)); 59 | if (!arr) 60 | return -ENOMEM; 61 | memset(arr, 0, sizeof(*arr)); 62 | arr->element_size = element_size; 63 | arr->length = 0; 64 | arr->size = initial_size; 65 | 66 | arr->data = malloc(arr->element_size * arr->size); 67 | if (!arr->data) { 68 | free(arr); 69 | return -ENOMEM; 70 | } 71 | 72 | *out = arr; 73 | return 0; 74 | } 75 | 76 | static inline void shl_array_free(struct shl_array *arr) 77 | { 78 | if (!arr) 79 | return; 80 | 81 | free(arr->data); 82 | free(arr); 83 | } 84 | 85 | /* Compute next higher power-of-2 of @v. Returns 4 in case v is 0. */ 86 | static inline size_t shl_array_pow2(size_t v) 87 | { 88 | size_t i; 89 | 90 | if (!v) 91 | return 4; 92 | 93 | --v; 94 | 95 | for (i = 1; i < 8 * sizeof(size_t); i *= 2) 96 | v |= v >> i; 97 | 98 | return ++v; 99 | } 100 | 101 | /* resize to length=size and zero out new array entries */ 102 | static inline int shl_array_zresize(struct shl_array *arr, size_t size) 103 | { 104 | void *tmp; 105 | size_t newsize; 106 | 107 | if (!arr) 108 | return -EINVAL; 109 | 110 | if (size > arr->size) { 111 | newsize = shl_array_pow2(size); 112 | tmp = realloc(arr->data, arr->element_size * newsize); 113 | if (!tmp) 114 | return -ENOMEM; 115 | 116 | arr->data = tmp; 117 | arr->size = newsize; 118 | 119 | memset(((uint8_t*)arr->data) + arr->element_size * arr->length, 120 | 0, arr->element_size * (size - arr->length)); 121 | } 122 | 123 | arr->length = size; 124 | return 0; 125 | } 126 | 127 | static inline int shl_array_push(struct shl_array *arr, const void *data) 128 | { 129 | void *tmp; 130 | size_t newsize; 131 | 132 | if (!arr || !data) 133 | return -EINVAL; 134 | 135 | if (arr->length >= arr->size) { 136 | newsize = arr->size * 2; 137 | tmp = realloc(arr->data, arr->element_size * newsize); 138 | if (!tmp) 139 | return -ENOMEM; 140 | 141 | arr->data = tmp; 142 | arr->size = newsize; 143 | } 144 | 145 | memcpy(((uint8_t*)arr->data) + arr->element_size * arr->length, 146 | data, arr->element_size); 147 | ++arr->length; 148 | 149 | return 0; 150 | } 151 | 152 | static inline void shl_array_pop(struct shl_array *arr) 153 | { 154 | if (!arr || !arr->length) 155 | return; 156 | 157 | --arr->length; 158 | } 159 | 160 | static inline void *shl_array_get_array(struct shl_array *arr) 161 | { 162 | if (!arr) 163 | return NULL; 164 | 165 | return arr->data; 166 | } 167 | 168 | static inline size_t shl_array_get_length(struct shl_array *arr) 169 | { 170 | if (!arr) 171 | return 0; 172 | 173 | return arr->length; 174 | } 175 | 176 | static inline size_t shl_array_get_bsize(struct shl_array *arr) 177 | { 178 | if (!arr) 179 | return 0; 180 | 181 | return arr->length * arr->element_size; 182 | } 183 | 184 | static inline size_t shl_array_get_element_size(struct shl_array *arr) 185 | { 186 | if (!arr) 187 | return 0; 188 | 189 | return arr->element_size; 190 | } 191 | 192 | #endif /* SHL_ARRAY_H */ 193 | -------------------------------------------------------------------------------- /src/shared/shl-htable.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SHL - Dynamic hash-table 3 | * 4 | * Written-by: Rusty Russell 5 | * Adjusted-by: David Herrmann 6 | * Licensed under LGPLv2+ - see LICENSE_htable file for details 7 | */ 8 | 9 | /* 10 | * Please see ccan/htable/_info at: 11 | * https://github.com/rustyrussell/ccan/tree/master/ccan/htable 12 | * for information on the hashtable algorithm. This file copies the code inline 13 | * and is released under the same conditions. 14 | * 15 | * At the end of the file you can find some helpers to use this htable to store 16 | * objects with "unsigned long" or "char*" keys. 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include "shl-htable.h" 27 | 28 | #define COLD __attribute__((cold)) 29 | 30 | struct htable { 31 | /* KEEP IN SYNC WITH "struct shl_htable_int" */ 32 | size_t (*rehash)(const void *elem, void *priv); 33 | void *priv; 34 | unsigned int bits; 35 | size_t elems, deleted, max, max_with_deleted; 36 | /* These are the bits which are the same in all pointers. */ 37 | uintptr_t common_mask, common_bits; 38 | uintptr_t perfect_bit; 39 | uintptr_t *table; 40 | }; 41 | 42 | #define HTABLE_INITIALIZER(name, rehash, priv) \ 43 | { rehash, priv, 0, 0, 0, 0, 0, -1, 0, 0, &name.perfect_bit } 44 | 45 | struct htable_iter { 46 | size_t off; 47 | }; 48 | 49 | /* 50 | * INLINE COPY OF ccan/htable.c 51 | */ 52 | 53 | /* We use 0x1 as deleted marker. */ 54 | #define HTABLE_DELETED (0x1) 55 | 56 | /* We clear out the bits which are always the same, and put metadata there. */ 57 | static inline uintptr_t get_extra_ptr_bits(const struct htable *ht, 58 | uintptr_t e) 59 | { 60 | return e & ht->common_mask; 61 | } 62 | 63 | static inline void *get_raw_ptr(const struct htable *ht, uintptr_t e) 64 | { 65 | return (void *)((e & ~ht->common_mask) | ht->common_bits); 66 | } 67 | 68 | static inline uintptr_t make_hval(const struct htable *ht, 69 | const void *p, uintptr_t bits) 70 | { 71 | return ((uintptr_t)p & ~ht->common_mask) | bits; 72 | } 73 | 74 | static inline bool entry_is_valid(uintptr_t e) 75 | { 76 | return e > HTABLE_DELETED; 77 | } 78 | 79 | static inline uintptr_t get_hash_ptr_bits(const struct htable *ht, 80 | size_t hash) 81 | { 82 | /* Shuffling the extra bits (as specified in mask) down the 83 | * end is quite expensive. But the lower bits are redundant, so 84 | * we fold the value first. */ 85 | return (hash ^ (hash >> ht->bits)) 86 | & ht->common_mask & ~ht->perfect_bit; 87 | } 88 | 89 | static void htable_init(struct htable *ht, 90 | size_t (*rehash)(const void *elem, void *priv), 91 | void *priv) 92 | { 93 | struct htable empty = HTABLE_INITIALIZER(empty, NULL, NULL); 94 | *ht = empty; 95 | ht->rehash = rehash; 96 | ht->priv = priv; 97 | ht->table = &ht->perfect_bit; 98 | } 99 | 100 | static void htable_clear(struct htable *ht, 101 | void (*free_cb) (void *entry, void *ctx), 102 | void *ctx) 103 | { 104 | size_t i; 105 | 106 | if (ht->table != &ht->perfect_bit) { 107 | if (free_cb) { 108 | for (i = 0; i < (size_t)1 << ht->bits; ++i) { 109 | if (entry_is_valid(ht->table[i])) 110 | free_cb(get_raw_ptr(ht, ht->table[i]), 111 | ctx); 112 | } 113 | } 114 | 115 | free((void *)ht->table); 116 | } 117 | 118 | htable_init(ht, ht->rehash, ht->priv); 119 | } 120 | 121 | static void htable_visit(struct htable *ht, 122 | void (*visit_cb) (void *elem, void *ctx), 123 | void *ctx) 124 | { 125 | size_t i; 126 | 127 | if (visit_cb && ht->table != &ht->perfect_bit) { 128 | for (i = 0; i < (size_t)1 << ht->bits; ++i) { 129 | if (entry_is_valid(ht->table[i])) 130 | visit_cb(get_raw_ptr(ht, ht->table[i]), ctx); 131 | } 132 | } 133 | } 134 | 135 | static size_t hash_bucket(const struct htable *ht, size_t h) 136 | { 137 | return h & ((1 << ht->bits)-1); 138 | } 139 | 140 | static void *htable_val(const struct htable *ht, 141 | struct htable_iter *i, size_t hash, uintptr_t perfect) 142 | { 143 | uintptr_t h2 = get_hash_ptr_bits(ht, hash) | perfect; 144 | 145 | while (ht->table[i->off]) { 146 | if (ht->table[i->off] != HTABLE_DELETED) { 147 | if (get_extra_ptr_bits(ht, ht->table[i->off]) == h2) 148 | return get_raw_ptr(ht, ht->table[i->off]); 149 | } 150 | i->off = (i->off + 1) & ((1 << ht->bits)-1); 151 | h2 &= ~perfect; 152 | } 153 | return NULL; 154 | } 155 | 156 | static void *htable_firstval(const struct htable *ht, 157 | struct htable_iter *i, size_t hash) 158 | { 159 | i->off = hash_bucket(ht, hash); 160 | return htable_val(ht, i, hash, ht->perfect_bit); 161 | } 162 | 163 | static void *htable_nextval(const struct htable *ht, 164 | struct htable_iter *i, size_t hash) 165 | { 166 | i->off = (i->off + 1) & ((1 << ht->bits)-1); 167 | return htable_val(ht, i, hash, 0); 168 | } 169 | 170 | /* This does not expand the hash table, that's up to caller. */ 171 | static void ht_add(struct htable *ht, const void *new, size_t h) 172 | { 173 | size_t i; 174 | uintptr_t perfect = ht->perfect_bit; 175 | 176 | i = hash_bucket(ht, h); 177 | 178 | while (entry_is_valid(ht->table[i])) { 179 | perfect = 0; 180 | i = (i + 1) & ((1 << ht->bits)-1); 181 | } 182 | ht->table[i] = make_hval(ht, new, get_hash_ptr_bits(ht, h)|perfect); 183 | } 184 | 185 | static COLD bool double_table(struct htable *ht) 186 | { 187 | unsigned int i; 188 | size_t oldnum = (size_t)1 << ht->bits; 189 | uintptr_t *oldtable, e; 190 | 191 | oldtable = ht->table; 192 | ht->table = calloc(1 << (ht->bits+1), sizeof(size_t)); 193 | if (!ht->table) { 194 | ht->table = oldtable; 195 | return false; 196 | } 197 | ht->bits++; 198 | ht->max = ((size_t)3 << ht->bits) / 4; 199 | ht->max_with_deleted = ((size_t)9 << ht->bits) / 10; 200 | 201 | /* If we lost our "perfect bit", get it back now. */ 202 | if (!ht->perfect_bit && ht->common_mask) { 203 | for (i = 0; i < sizeof(ht->common_mask) * CHAR_BIT; i++) { 204 | if (ht->common_mask & ((size_t)1 << i)) { 205 | ht->perfect_bit = (size_t)1 << i; 206 | break; 207 | } 208 | } 209 | } 210 | 211 | if (oldtable != &ht->perfect_bit) { 212 | for (i = 0; i < oldnum; i++) { 213 | if (entry_is_valid(e = oldtable[i])) { 214 | void *p = get_raw_ptr(ht, e); 215 | ht_add(ht, p, ht->rehash(p, ht->priv)); 216 | } 217 | } 218 | free(oldtable); 219 | } 220 | ht->deleted = 0; 221 | return true; 222 | } 223 | 224 | static COLD void rehash_table(struct htable *ht) 225 | { 226 | size_t start, i; 227 | uintptr_t e; 228 | 229 | /* Beware wrap cases: we need to start from first empty bucket. */ 230 | for (start = 0; ht->table[start]; start++); 231 | 232 | for (i = 0; i < (size_t)1 << ht->bits; i++) { 233 | size_t h = (i + start) & ((1 << ht->bits)-1); 234 | e = ht->table[h]; 235 | if (!e) 236 | continue; 237 | if (e == HTABLE_DELETED) 238 | ht->table[h] = 0; 239 | else if (!(e & ht->perfect_bit)) { 240 | void *p = get_raw_ptr(ht, e); 241 | ht->table[h] = 0; 242 | ht_add(ht, p, ht->rehash(p, ht->priv)); 243 | } 244 | } 245 | ht->deleted = 0; 246 | } 247 | 248 | /* We stole some bits, now we need to put them back... */ 249 | static COLD void update_common(struct htable *ht, const void *p) 250 | { 251 | unsigned int i; 252 | uintptr_t maskdiff, bitsdiff; 253 | 254 | if (ht->elems == 0) { 255 | /* Always reveal one bit of the pointer in the bucket, 256 | * so it's not zero or HTABLE_DELETED (1), even if 257 | * hash happens to be 0. Assumes (void *)1 is not a 258 | * valid pointer. */ 259 | for (i = sizeof(uintptr_t)*CHAR_BIT - 1; i > 0; i--) { 260 | if ((uintptr_t)p & ((uintptr_t)1 << i)) 261 | break; 262 | } 263 | 264 | ht->common_mask = ~((uintptr_t)1 << i); 265 | ht->common_bits = ((uintptr_t)p & ht->common_mask); 266 | ht->perfect_bit = 1; 267 | return; 268 | } 269 | 270 | /* Find bits which are unequal to old common set. */ 271 | maskdiff = ht->common_bits ^ ((uintptr_t)p & ht->common_mask); 272 | 273 | /* These are the bits which go there in existing entries. */ 274 | bitsdiff = ht->common_bits & maskdiff; 275 | 276 | for (i = 0; i < (size_t)1 << ht->bits; i++) { 277 | if (!entry_is_valid(ht->table[i])) 278 | continue; 279 | /* Clear the bits no longer in the mask, set them as 280 | * expected. */ 281 | ht->table[i] &= ~maskdiff; 282 | ht->table[i] |= bitsdiff; 283 | } 284 | 285 | /* Take away those bits from our mask, bits and perfect bit. */ 286 | ht->common_mask &= ~maskdiff; 287 | ht->common_bits &= ~maskdiff; 288 | ht->perfect_bit &= ~maskdiff; 289 | } 290 | 291 | static bool htable_add(struct htable *ht, size_t hash, const void *p) 292 | { 293 | if (ht->elems+1 > ht->max && !double_table(ht)) 294 | return false; 295 | if (ht->elems+1 + ht->deleted > ht->max_with_deleted) 296 | rehash_table(ht); 297 | assert(p); 298 | if (((uintptr_t)p & ht->common_mask) != ht->common_bits) 299 | update_common(ht, p); 300 | 301 | ht_add(ht, p, hash); 302 | ht->elems++; 303 | return true; 304 | } 305 | 306 | static void htable_delval(struct htable *ht, struct htable_iter *i) 307 | { 308 | assert(i->off < (size_t)1 << ht->bits); 309 | assert(entry_is_valid(ht->table[i->off])); 310 | 311 | ht->elems--; 312 | ht->table[i->off] = HTABLE_DELETED; 313 | ht->deleted++; 314 | } 315 | 316 | /* 317 | * Wrapper code to make it easier to use this hash-table as map. 318 | */ 319 | 320 | void shl_htable_init(struct shl_htable *htable, 321 | bool (*compare) (const void *a, const void *b), 322 | size_t (*rehash)(const void *elem, void *priv), 323 | void *priv) 324 | { 325 | struct htable *ht = (void*)&htable->htable; 326 | 327 | htable->compare = compare; 328 | htable_init(ht, rehash, priv); 329 | } 330 | 331 | void shl_htable_clear(struct shl_htable *htable, 332 | void (*free_cb) (void *elem, void *ctx), 333 | void *ctx) 334 | { 335 | struct htable *ht = (void*)&htable->htable; 336 | 337 | htable_clear(ht, free_cb, ctx); 338 | } 339 | 340 | void shl_htable_visit(struct shl_htable *htable, 341 | void (*visit_cb) (void *elem, void *ctx), 342 | void *ctx) 343 | { 344 | struct htable *ht = (void*)&htable->htable; 345 | 346 | htable_visit(ht, visit_cb, ctx); 347 | } 348 | 349 | bool shl_htable_lookup(struct shl_htable *htable, const void *obj, size_t hash, 350 | void **out) 351 | { 352 | struct htable *ht = (void*)&htable->htable; 353 | struct htable_iter i; 354 | void *c; 355 | 356 | for (c = htable_firstval(ht, &i, hash); 357 | c; 358 | c = htable_nextval(ht, &i, hash)) { 359 | if (htable->compare(obj, c)) { 360 | if (out) 361 | *out = c; 362 | return true; 363 | } 364 | } 365 | 366 | return false; 367 | } 368 | 369 | int shl_htable_insert(struct shl_htable *htable, const void *obj, size_t hash) 370 | { 371 | struct htable *ht = (void*)&htable->htable; 372 | bool b; 373 | 374 | b = htable_add(ht, hash, (void*)obj); 375 | return b ? 0 : -ENOMEM; 376 | } 377 | 378 | bool shl_htable_remove(struct shl_htable *htable, const void *obj, size_t hash, 379 | void **out) 380 | { 381 | struct htable *ht = (void*)&htable->htable; 382 | struct htable_iter i; 383 | void *c; 384 | 385 | for (c = htable_firstval(ht, &i, hash); 386 | c; 387 | c = htable_nextval(ht, &i, hash)) { 388 | if (htable->compare(obj, c)) { 389 | if (out) 390 | *out = c; 391 | htable_delval(ht, &i); 392 | return true; 393 | } 394 | } 395 | 396 | return false; 397 | } 398 | 399 | /* 400 | * Helpers 401 | */ 402 | 403 | bool shl_htable_compare_ulong(const void *a, const void *b) 404 | { 405 | return *(const unsigned long*)a == *(const unsigned long*)b; 406 | } 407 | 408 | size_t shl_htable_rehash_ulong(const void *elem, void *priv) 409 | { 410 | return (size_t)*(const unsigned long*)elem; 411 | } 412 | 413 | bool shl_htable_compare_str(const void *a, const void *b) 414 | { 415 | return !strcmp(*(char**)a, *(char**)b); 416 | } 417 | 418 | /* DJB's hash function */ 419 | size_t shl_htable_rehash_str(const void *elem, void *priv) 420 | { 421 | const char *str = *(char**)elem; 422 | size_t hash = 5381; 423 | 424 | for ( ; *str; ++str) 425 | hash = (hash << 5) + hash + (size_t)*str; 426 | 427 | return hash; 428 | } 429 | -------------------------------------------------------------------------------- /src/shared/shl-htable.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SHL - Dynamic hash-table 3 | * 4 | * Copyright (c) 2010-2013 David Herrmann 5 | * Licensed under LGPLv2+ - see LICENSE_htable file for details 6 | */ 7 | 8 | /* 9 | * Dynamic hash-table 10 | * Implementation of a self-resizing hashtable to store arbitrary objects. 11 | * Entries are not allocated by the table itself but are user-allocated. A 12 | * single entry can be stored multiple times in the hashtable. No 13 | * maintenance-members need to be embedded in user-allocated objects. However, 14 | * the key (and optionally the hash) must be stored in the objects. 15 | * 16 | * Uses internally the htable from CCAN. See LICENSE_htable. 17 | */ 18 | 19 | #ifndef SHL_HTABLE_H 20 | #define SHL_HTABLE_H 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | /* miscellaneous */ 29 | 30 | #define shl_htable_offsetof(pointer, type, member) ({ \ 31 | const typeof(((type*)0)->member) *__ptr = (pointer); \ 32 | (type*)(((char*)__ptr) - offsetof(type, member)); \ 33 | }) 34 | 35 | /* htable */ 36 | 37 | struct shl_htable_int { 38 | size_t (*rehash)(const void *elem, void *priv); 39 | void *priv; 40 | unsigned int bits; 41 | size_t elems, deleted, max, max_with_deleted; 42 | /* These are the bits which are the same in all pointers. */ 43 | uintptr_t common_mask, common_bits; 44 | uintptr_t perfect_bit; 45 | uintptr_t *table; 46 | }; 47 | 48 | struct shl_htable { 49 | bool (*compare) (const void *a, const void *b); 50 | struct shl_htable_int htable; 51 | }; 52 | 53 | #define SHL_HTABLE_INIT(_obj, _compare, _rehash, _priv) \ 54 | { \ 55 | .compare = (_compare), \ 56 | .htable = { \ 57 | .rehash = (_rehash), \ 58 | .priv = (_priv), \ 59 | .bits = 0, \ 60 | .elems = 0, \ 61 | .deleted = 0, \ 62 | .max = 0, \ 63 | .max_with_deleted = 0, \ 64 | .common_mask = -1, \ 65 | .common_bits = 0, \ 66 | .perfect_bit = 0, \ 67 | .table = &(_obj).htable.perfect_bit \ 68 | } \ 69 | } 70 | 71 | void shl_htable_init(struct shl_htable *htable, 72 | bool (*compare) (const void *a, const void *b), 73 | size_t (*rehash)(const void *elem, void *priv), 74 | void *priv); 75 | void shl_htable_clear(struct shl_htable *htable, 76 | void (*free_cb) (void *elem, void *ctx), 77 | void *ctx); 78 | void shl_htable_visit(struct shl_htable *htable, 79 | void (*visit_cb) (void *elem, void *ctx), 80 | void *ctx); 81 | bool shl_htable_lookup(struct shl_htable *htable, const void *obj, size_t hash, 82 | void **out); 83 | int shl_htable_insert(struct shl_htable *htable, const void *obj, size_t hash); 84 | bool shl_htable_remove(struct shl_htable *htable, const void *obj, size_t hash, 85 | void **out); 86 | 87 | /* ulong htables */ 88 | 89 | #if SIZE_MAX < ULONG_MAX 90 | # error "'size_t' is smaller than 'unsigned long'" 91 | #endif 92 | 93 | bool shl_htable_compare_ulong(const void *a, const void *b); 94 | size_t shl_htable_rehash_ulong(const void *elem, void *priv); 95 | 96 | #define SHL_HTABLE_INIT_ULONG(_obj) \ 97 | SHL_HTABLE_INIT((_obj), shl_htable_compare_ulong, \ 98 | shl_htable_rehash_ulong, \ 99 | NULL) 100 | 101 | static inline void shl_htable_init_ulong(struct shl_htable *htable) 102 | { 103 | shl_htable_init(htable, shl_htable_compare_ulong, 104 | shl_htable_rehash_ulong, NULL); 105 | } 106 | 107 | static inline void shl_htable_clear_ulong(struct shl_htable *htable, 108 | void (*cb) (unsigned long *elem, 109 | void *ctx), 110 | void *ctx) 111 | { 112 | shl_htable_clear(htable, (void (*) (void*, void*))cb, ctx); 113 | } 114 | 115 | static inline void shl_htable_visit_ulong(struct shl_htable *htable, 116 | void (*cb) (unsigned long *elem, 117 | void *ctx), 118 | void *ctx) 119 | { 120 | shl_htable_visit(htable, (void (*) (void*, void*))cb, ctx); 121 | } 122 | 123 | static inline bool shl_htable_lookup_ulong(struct shl_htable *htable, 124 | unsigned long key, 125 | unsigned long **out) 126 | { 127 | return shl_htable_lookup(htable, (const void*)&key, (size_t)key, 128 | (void**)out); 129 | } 130 | 131 | static inline int shl_htable_insert_ulong(struct shl_htable *htable, 132 | const unsigned long *key) 133 | { 134 | return shl_htable_insert(htable, (const void*)key, (size_t)*key); 135 | } 136 | 137 | static inline bool shl_htable_remove_ulong(struct shl_htable *htable, 138 | unsigned long key, 139 | unsigned long **out) 140 | { 141 | return shl_htable_remove(htable, (const void*)&key, (size_t)key, 142 | (void**)out); 143 | } 144 | 145 | /* string htables */ 146 | 147 | bool shl_htable_compare_str(const void *a, const void *b); 148 | size_t shl_htable_rehash_str(const void *elem, void *priv); 149 | 150 | #define SHL_HTABLE_INIT_STR(_obj) \ 151 | SHL_HTABLE_INIT((_obj), shl_htable_compare_str, \ 152 | shl_htable_rehash_str, \ 153 | NULL) 154 | 155 | static inline void shl_htable_init_str(struct shl_htable *htable) 156 | { 157 | shl_htable_init(htable, shl_htable_compare_str, 158 | shl_htable_rehash_str, NULL); 159 | } 160 | 161 | static inline void shl_htable_clear_str(struct shl_htable *htable, 162 | void (*cb) (char **elem, 163 | void *ctx), 164 | void *ctx) 165 | { 166 | shl_htable_clear(htable, (void (*) (void*, void*))cb, ctx); 167 | } 168 | 169 | static inline void shl_htable_visit_str(struct shl_htable *htable, 170 | void (*cb) (char **elem, 171 | void *ctx), 172 | void *ctx) 173 | { 174 | shl_htable_visit(htable, (void (*) (void*, void*))cb, ctx); 175 | } 176 | 177 | static inline size_t shl_htable_hash_str(struct shl_htable *htable, 178 | const char *str, size_t *hash) 179 | { 180 | size_t h; 181 | 182 | if (hash && *hash) { 183 | h = *hash; 184 | } else { 185 | h = htable->htable.rehash((const void*)&str, NULL); 186 | if (hash) 187 | *hash = h; 188 | } 189 | 190 | return h; 191 | } 192 | 193 | static inline bool shl_htable_lookup_str(struct shl_htable *htable, 194 | const char *str, size_t *hash, 195 | char ***out) 196 | { 197 | size_t h; 198 | 199 | h = shl_htable_hash_str(htable, str, hash); 200 | return shl_htable_lookup(htable, (const void*)&str, h, (void**)out); 201 | } 202 | 203 | static inline int shl_htable_insert_str(struct shl_htable *htable, 204 | char **str, size_t *hash) 205 | { 206 | size_t h; 207 | 208 | h = shl_htable_hash_str(htable, *str, hash); 209 | return shl_htable_insert(htable, (const void*)str, h); 210 | } 211 | 212 | static inline bool shl_htable_remove_str(struct shl_htable *htable, 213 | const char *str, size_t *hash, 214 | char ***out) 215 | { 216 | size_t h; 217 | 218 | h = shl_htable_hash_str(htable, str, hash); 219 | return shl_htable_remove(htable, (const void*)&str, h, (void **)out); 220 | } 221 | 222 | #endif /* SHL_HTABLE_H */ 223 | -------------------------------------------------------------------------------- /src/shared/shl-llog.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SHL - Library Log/Debug Interface 3 | * 4 | * Copyright (c) 2010-2013 David Herrmann 5 | * Dedicated to the Public Domain 6 | */ 7 | 8 | /* 9 | * Library Log/Debug Interface 10 | * Libraries should always avoid producing side-effects. This includes writing 11 | * log-messages of any kind. However, you often don't want to disable debugging 12 | * entirely, therefore, the core objects often contain a pointer to a function 13 | * which performs logging. If that pointer is NULL (default), logging is 14 | * disabled. 15 | * 16 | * This header should never be installed into the system! This is _no_ public 17 | * header. Instead, copy it into your application if you want and use it there. 18 | * Your public library API should include something like this: 19 | * 20 | * typedef void (*MYPREFIX_log_t) (void *data, 21 | * const char *file, 22 | * int line, 23 | * const char *func, 24 | * const char *subs, 25 | * unsigned int sev, 26 | * const char *format, 27 | * va_list args); 28 | * 29 | * And then the user can supply such a function when creating a new context 30 | * object of your library or simply supply NULL. Internally, you have a field of 31 | * type "MYPREFIX_log_t llog" in your main structure. If you pass this to the 32 | * convenience helpers like llog_dbg(), llog_warn() etc. it will automatically 33 | * use the "llog" field to print the message. If it is NULL, nothing is done. 34 | * 35 | * The arguments of the log-function are defined as: 36 | * data: User-supplied data field that is passed straight through. 37 | * file: Zero terminated string of the file-name where the log-message 38 | * occurred. Can be NULL. 39 | * line: Line number of @file where the message occurred. Set to 0 or smaller 40 | * if not available. 41 | * func: Function name where the log-message occurred. Can be NULL. 42 | * subs: Subsystem where the message occurred (zero terminated). Can be NULL. 43 | * sev: Severity of log-message. An integer between 0 and 7 as defined below. 44 | * These are identical to the linux-kernel severities so there is no need 45 | * to include these in your public API. Every app can define them 46 | * themselves, if they need it. 47 | * format: Format string. Must not be NULL. 48 | * args: Argument array 49 | * 50 | * The user should also be able to optionally provide a data field which is 51 | * always passed unmodified as first parameter to the log-function. This allows 52 | * to add context to the logger. 53 | */ 54 | 55 | #ifndef SHL_LLOG_H 56 | #define SHL_LLOG_H 57 | 58 | #include 59 | #include 60 | #include 61 | #include 62 | 63 | enum llog_severity { 64 | LLOG_FATAL = 0, 65 | LLOG_ALERT = 1, 66 | LLOG_CRITICAL = 2, 67 | LLOG_ERROR = 3, 68 | LLOG_WARNING = 4, 69 | LLOG_NOTICE = 5, 70 | LLOG_INFO = 6, 71 | LLOG_DEBUG = 7, 72 | LLOG_SEV_NUM, 73 | }; 74 | 75 | typedef void (*llog_submit_t) (void *data, 76 | const char *file, 77 | int line, 78 | const char *func, 79 | const char *subs, 80 | unsigned int sev, 81 | const char *format, 82 | va_list args); 83 | 84 | static inline __attribute__((format(printf, 8, 9))) 85 | void llog_format(llog_submit_t llog, 86 | void *data, 87 | const char *file, 88 | int line, 89 | const char *func, 90 | const char *subs, 91 | unsigned int sev, 92 | const char *format, 93 | ...) 94 | { 95 | int saved_errno = errno; 96 | va_list list; 97 | 98 | if (llog) { 99 | va_start(list, format); 100 | errno = saved_errno; 101 | llog(data, file, line, func, subs, sev, format, list); 102 | va_end(list); 103 | } 104 | } 105 | 106 | #ifndef LLOG_SUBSYSTEM 107 | static const char *LLOG_SUBSYSTEM __attribute__((__unused__)); 108 | #endif 109 | 110 | #define LLOG_DEFAULT __FILE__, __LINE__, __func__, LLOG_SUBSYSTEM 111 | 112 | #define llog_printf(obj, sev, format, ...) \ 113 | llog_format((obj)->llog, \ 114 | (obj)->llog_data, \ 115 | LLOG_DEFAULT, \ 116 | (sev), \ 117 | (format), \ 118 | ##__VA_ARGS__) 119 | #define llog_dprintf(obj, data, sev, format, ...) \ 120 | llog_format((obj), \ 121 | (data), \ 122 | LLOG_DEFAULT, \ 123 | (sev), \ 124 | (format), \ 125 | ##__VA_ARGS__) 126 | 127 | static inline __attribute__((format(printf, 4, 5))) 128 | void llog_dummyf(llog_submit_t llog, void *data, unsigned int sev, 129 | const char *format, ...) 130 | { 131 | } 132 | 133 | /* 134 | * Helpers 135 | * They pick up all the default values and submit the message to the 136 | * llog-subsystem. The llog_debug() function will discard the message unless 137 | * BUILD_ENABLE_DEBUG is defined. 138 | */ 139 | 140 | #ifdef BUILD_ENABLE_DEBUG 141 | #define llog_ddebug(obj, data, format, ...) \ 142 | llog_dprintf((obj), (data), LLOG_DEBUG, (format), ##__VA_ARGS__) 143 | #define llog_debug(obj, format, ...) \ 144 | llog_ddebug((obj)->llog, (obj)->llog_data, (format), ##__VA_ARGS__) 145 | #else 146 | #define llog_ddebug(obj, data, format, ...) \ 147 | llog_dummyf((obj), (data), LLOG_DEBUG, (format), ##__VA_ARGS__) 148 | #define llog_debug(obj, format, ...) \ 149 | llog_ddebug((obj)->llog, (obj)->llog_data, (format), ##__VA_ARGS__) 150 | #endif 151 | 152 | #define llog_info(obj, format, ...) \ 153 | llog_printf((obj), LLOG_INFO, (format), ##__VA_ARGS__) 154 | #define llog_dinfo(obj, data, format, ...) \ 155 | llog_dprintf((obj), (data), LLOG_INFO, (format), ##__VA_ARGS__) 156 | #define llog_notice(obj, format, ...) \ 157 | llog_printf((obj), LLOG_NOTICE, (format), ##__VA_ARGS__) 158 | #define llog_dnotice(obj, data, format, ...) \ 159 | llog_dprintf((obj), (data), LLOG_NOTICE, (format), ##__VA_ARGS__) 160 | #define llog_warning(obj, format, ...) \ 161 | llog_printf((obj), LLOG_WARNING, (format), ##__VA_ARGS__) 162 | #define llog_dwarning(obj, data, format, ...) \ 163 | llog_dprintf((obj), (data), LLOG_WARNING, (format), ##__VA_ARGS__) 164 | #define llog_error(obj, format, ...) \ 165 | llog_printf((obj), LLOG_ERROR, (format), ##__VA_ARGS__) 166 | #define llog_derror(obj, data, format, ...) \ 167 | llog_dprintf((obj), (data), LLOG_ERROR, (format), ##__VA_ARGS__) 168 | #define llog_critical(obj, format, ...) \ 169 | llog_printf((obj), LLOG_CRITICAL, (format), ##__VA_ARGS__) 170 | #define llog_dcritical(obj, data, format, ...) \ 171 | llog_dprintf((obj), (data), LLOG_CRITICAL, (format), ##__VA_ARGS__) 172 | #define llog_alert(obj, format, ...) \ 173 | llog_printf((obj), LLOG_ALERT, (format), ##__VA_ARGS__) 174 | #define llog_dalert(obj, data, format, ...) \ 175 | llog_dprintf((obj), (data), LLOG_ALERT, (format), ##__VA_ARGS__) 176 | #define llog_fatal(obj, format, ...) \ 177 | llog_printf((obj), LLOG_FATAL, (format), ##__VA_ARGS__) 178 | #define llog_dfatal(obj, data, format, ...) \ 179 | llog_dprintf((obj), (data), LLOG_FATAL, (format), ##__VA_ARGS__) 180 | 181 | /* 182 | * Default log messages 183 | * These macros can be used to produce default log messages. You can use them 184 | * directly in an "return" statement. The "v" variants automatically cast the 185 | * result to void so it can be used in return statements inside of void 186 | * functions. The "d" variants use the logging object directly as the parent 187 | * might not exist, yet. 188 | * 189 | * Most of the messages work only if debugging is enabled. This is, because they 190 | * are used in debug paths and would slow down normal applications. 191 | */ 192 | 193 | #define llog_dEINVAL(obj, data) \ 194 | (llog_derror((obj), (data), "invalid arguments"), -EINVAL) 195 | #define llog_EINVAL(obj) \ 196 | (llog_dEINVAL((obj)->llog, (obj)->llog_data)) 197 | #define llog_vEINVAL(obj) \ 198 | ((void)llog_EINVAL(obj)) 199 | #define llog_vdEINVAL(obj, data) \ 200 | ((void)llog_dEINVAL((obj), (data))) 201 | 202 | #define llog_dEFAULT(obj, data) \ 203 | (llog_derror((obj), (data), "internal operation failed"), -EFAULT) 204 | #define llog_EFAULT(obj) \ 205 | (llog_dEFAULT((obj)->llog, (obj)->llog_data)) 206 | #define llog_vEFAULT(obj) \ 207 | ((void)llog_EFAULT(obj)) 208 | #define llog_vdEFAULT(obj, data) \ 209 | ((void)llog_dEFAULT((obj), (data))) 210 | 211 | #define llog_dENOMEM(obj, data) \ 212 | (llog_derror((obj), (data), "out of memory"), -ENOMEM) 213 | #define llog_ENOMEM(obj) \ 214 | (llog_dENOMEM((obj)->llog, (obj)->llog_data)) 215 | #define llog_vENOMEM(obj) \ 216 | ((void)llog_ENOMEM(obj)) 217 | #define llog_vdENOMEM(obj, data) \ 218 | ((void)llog_dENOMEM((obj), (data))) 219 | 220 | #define llog_dEPIPE(obj, data) \ 221 | (llog_derror((obj), (data), "fd closed unexpectedly"), -EPIPE) 222 | #define llog_EPIPE(obj) \ 223 | (llog_dEPIPE((obj)->llog, (obj)->llog_data)) 224 | #define llog_vEPIPE(obj) \ 225 | ((void)llog_EPIPE(obj)) 226 | #define llog_vdEPIPE(obj, data) \ 227 | ((void)llog_dEPIPE((obj), (data))) 228 | 229 | #define llog_dERRNO(obj, data) \ 230 | (llog_derror((obj), (data), "syscall failed (%d): %m", errno), -errno) 231 | #define llog_ERRNO(obj) \ 232 | (llog_dERRNO((obj)->llog, (obj)->llog_data)) 233 | #define llog_vERRNO(obj) \ 234 | ((void)llog_ERRNO(obj)) 235 | #define llog_vdERRNO(obj, data) \ 236 | ((void)llog_dERRNO((obj), (data))) 237 | 238 | #define llog_dERR(obj, data, _r) \ 239 | (errno = -(_r), llog_derror((obj), (data), "syscall failed (%d): %m", (_r)), (_r)) 240 | #define llog_ERR(obj, _r) \ 241 | (llog_dERR((obj)->llog, (obj)->llog_data, (_r))) 242 | #define llog_vERR(obj, _r) \ 243 | ((void)llog_ERR((obj), (_r))) 244 | #define llog_vdERR(obj, data, _r) \ 245 | ((void)llog_dERR((obj), (data), (_r))) 246 | 247 | #endif /* SHL_LLOG_H */ 248 | -------------------------------------------------------------------------------- /src/shared/shl-macro.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SHL - Macros 3 | * 4 | * Copyright (c) 2011-2014 David Herrmann 5 | * Dedicated to the Public Domain 6 | */ 7 | 8 | /* 9 | * Macros 10 | */ 11 | 12 | #ifndef SHL_MACRO_H 13 | #define SHL_MACRO_H 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | /* macOS compatibility */ 24 | #if !defined static_assert 25 | #define static_assert _Static_assert 26 | #endif 27 | 28 | /* sanity checks required for some macros */ 29 | #if __SIZEOF_POINTER__ != 4 && __SIZEOF_POINTER__ != 8 30 | #error "Pointer size is neither 4 nor 8 bytes" 31 | #endif 32 | 33 | /* gcc attributes; look them up for more information */ 34 | #define _shl_printf_(_a, _b) __attribute__((__format__(printf, _a, _b))) 35 | #define _shl_alloc_(...) __attribute__((__alloc_size__(__VA_ARGS__))) 36 | #define _shl_sentinel_ __attribute__((__sentinel__)) 37 | #define _shl_noreturn_ __attribute__((__noreturn__)) 38 | #define _shl_unused_ __attribute__((__unused__)) 39 | #define _shl_pure_ __attribute__((__pure__)) 40 | #define _shl_const_ __attribute__((__const__)) 41 | #define _shl_deprecated_ __attribute__((__deprecated__)) 42 | #define _shl_packed_ __attribute__((__packed__)) 43 | #define _shl_malloc_ __attribute__((__malloc__)) 44 | #define _shl_weak_ __attribute__((__weak__)) 45 | #define _shl_likely_(_val) (__builtin_expect(!!(_val), 1)) 46 | #define _shl_unlikely_(_val) (__builtin_expect(!!(_val), 0)) 47 | #define _shl_public_ __attribute__((__visibility__("default"))) 48 | #define _shl_hidden_ __attribute__((__visibility__("hidden"))) 49 | #define _shl_weakref_(_val) __attribute__((__weakref__(#_val))) 50 | #define _shl_cleanup_(_val) __attribute__((__cleanup__(_val))) 51 | 52 | static inline void shl_freep(void *p) 53 | { 54 | free(*(void**)p); 55 | } 56 | 57 | #define _shl_free_ _shl_cleanup_(shl_freep) 58 | 59 | static inline void shl_closep(int *p) 60 | { 61 | if (*p >= 0) 62 | close(*p); 63 | } 64 | 65 | #define _shl_close_ _shl_cleanup_(shl_closep) 66 | 67 | static inline void shl_set_errno(int *r) 68 | { 69 | errno = *r; 70 | } 71 | 72 | #define SHL_PROTECT_ERRNO \ 73 | _shl_cleanup_(shl_set_errno) _shl_unused_ int shl__errno = errno 74 | 75 | /* 2-level stringify helper */ 76 | #define SHL__STRINGIFY(_val) #_val 77 | #define SHL_STRINGIFY(_val) SHL__STRINGIFY(_val) 78 | 79 | /* 2-level concatenate helper */ 80 | #define SHL__CONCATENATE(_a, _b) _a ## _b 81 | #define SHL_CONCATENATE(_a, _b) SHL__CONCATENATE(_a, _b) 82 | 83 | /* unique identifier with prefix */ 84 | #define SHL_UNIQUE(_prefix) SHL_CONCATENATE(_prefix, __COUNTER__) 85 | 86 | /* array element count */ 87 | #define SHL_ARRAY_LENGTH(_array) (sizeof(_array)/sizeof(*(_array))) 88 | 89 | /* get parent pointer by container-type, member and member-pointer */ 90 | #define shl_container_of(_ptr, _type, _member) \ 91 | ({ \ 92 | const typeof( ((_type *)0)->_member ) *__mptr = (_ptr); \ 93 | (_type *)( (char *)__mptr - offsetof(_type, _member) ); \ 94 | }) 95 | 96 | /* return maximum of two values and do strict type checking */ 97 | #define shl_max(_a, _b) \ 98 | ({ \ 99 | typeof(_a) __a = (_a); \ 100 | typeof(_b) __b = (_b); \ 101 | (void) (&__a == &__b); \ 102 | __a > __b ? __a : __b; \ 103 | }) 104 | 105 | /* same as shl_max() but perform explicit cast beforehand */ 106 | #define shl_max_t(_type, _a, _b) \ 107 | ({ \ 108 | _type __a = (_type)(_a); \ 109 | _type __b = (_type)(_b); \ 110 | __a > __b ? __a : __b; \ 111 | }) 112 | 113 | /* return minimum of two values and do strict type checking */ 114 | #define shl_min(_a, _b) \ 115 | ({ \ 116 | typeof(_a) __a = (_a); \ 117 | typeof(_b) __b = (_b); \ 118 | (void) (&__a == &__b); \ 119 | __a < __b ? __a : __b; \ 120 | }) 121 | 122 | /* same as shl_min() but perform explicit cast beforehand */ 123 | #define shl_min_t(_type, _a, _b) \ 124 | ({ \ 125 | _type __a = (_type)(_a); \ 126 | _type __b = (_type)(_b); \ 127 | __a < __b ? __a : __b; \ 128 | }) 129 | 130 | /* clamp value between low and high barriers */ 131 | #define shl_clamp(_val, _low, _high) \ 132 | ({ \ 133 | typeof(_val) __v = (_val); \ 134 | typeof(_low) __l = (_low); \ 135 | typeof(_high) __h = (_high); \ 136 | (void) (&__v == &__l); \ 137 | (void) (&__v == &__h); \ 138 | ((__v > __h) ? __h : ((__v < __l) ? __l : __v)); \ 139 | }) 140 | 141 | /* align to next higher power-of-2 (except for: 0 => 0, overflow => 0) */ 142 | static inline size_t SHL_ALIGN_POWER2(size_t u) 143 | { 144 | return 1ULL << ((sizeof(u) * 8ULL) - __builtin_clzll(u - 1ULL)); 145 | } 146 | 147 | /* zero memory or type */ 148 | #define shl_memzero(_ptr, _size) (memset((_ptr), 0, (_size))) 149 | #define shl_zero(_ptr) (shl_memzero(&(_ptr), sizeof(_ptr))) 150 | 151 | /* ptr <=> uint casts */ 152 | #define SHL_PTR_TO_TYPE(_type, _ptr) ((_type)((uintptr_t)(_ptr))) 153 | #define SHL_TYPE_TO_PTR(_type, _int) ((void*)((uintptr_t)(_int))) 154 | #define SHL_PTR_TO_INT(_ptr) SHL_PTR_TO_TYPE(int, (_ptr)) 155 | #define SHL_INT_TO_PTR(_ptr) SHL_TYPE_TO_PTR(int, (_ptr)) 156 | #define SHL_PTR_TO_UINT(_ptr) SHL_PTR_TO_TYPE(unsigned int, (_ptr)) 157 | #define SHL_UINT_TO_PTR(_ptr) SHL_TYPE_TO_PTR(unsigned int, (_ptr)) 158 | #define SHL_PTR_TO_LONG(_ptr) SHL_PTR_TO_TYPE(long, (_ptr)) 159 | #define SHL_LONG_TO_PTR(_ptr) SHL_TYPE_TO_PTR(long, (_ptr)) 160 | #define SHL_PTR_TO_ULONG(_ptr) SHL_PTR_TO_TYPE(unsigned long, (_ptr)) 161 | #define SHL_ULONG_TO_PTR(_ptr) SHL_TYPE_TO_PTR(unsigned long, (_ptr)) 162 | #define SHL_PTR_TO_S32(_ptr) SHL_PTR_TO_TYPE(int32_t, (_ptr)) 163 | #define SHL_S32_TO_PTR(_ptr) SHL_TYPE_TO_PTR(int32_t, (_ptr)) 164 | #define SHL_PTR_TO_U32(_ptr) SHL_PTR_TO_TYPE(uint32_t, (_ptr)) 165 | #define SHL_U32_TO_PTR(_ptr) SHL_TYPE_TO_PTR(uint32_t, (_ptr)) 166 | #define SHL_PTR_TO_S64(_ptr) SHL_PTR_TO_TYPE(int64_t, (_ptr)) 167 | #define SHL_S64_TO_PTR(_ptr) SHL_TYPE_TO_PTR(int64_t, (_ptr)) 168 | #define SHL_PTR_TO_U64(_ptr) SHL_PTR_TO_TYPE(uint64_t, (_ptr)) 169 | #define SHL_U64_TO_PTR(_ptr) SHL_TYPE_TO_PTR(uint64_t, (_ptr)) 170 | 171 | /* compile-time assertions */ 172 | #define shl_assert_cc(_expr) static_assert(_expr, #_expr) 173 | 174 | /* 175 | * Safe Multiplications 176 | * Multiplications are subject to overflows. These helpers guarantee that the 177 | * multiplication can be done safely and return -ERANGE if not. 178 | * 179 | * Note: This is horribly slow for ull/uint64_t as we need a division to test 180 | * for overflows. Take that into account when using these. For smaller integers, 181 | * we can simply use an upcast-multiplication which gcc should be smart enough 182 | * to optimize. 183 | */ 184 | 185 | #define SHL__REAL_MULT(_max, _val, _factor) \ 186 | ({ \ 187 | (_factor == 0 || *(_val) <= (_max) / (_factor)) ? \ 188 | ((*(_val) *= (_factor)), 0) : \ 189 | -ERANGE; \ 190 | }) 191 | 192 | #define SHL__UPCAST_MULT(_type, _max, _val, _factor) \ 193 | ({ \ 194 | _type v = *(_val) * (_type)(_factor); \ 195 | (v <= (_max)) ? \ 196 | ((*(_val) = v), 0) : \ 197 | -ERANGE; \ 198 | }) 199 | 200 | static inline int shl_mult_ull(unsigned long long *val, 201 | unsigned long long factor) 202 | { 203 | return SHL__REAL_MULT(ULLONG_MAX, val, factor); 204 | } 205 | 206 | static inline int shl_mult_ul(unsigned long *val, unsigned long factor) 207 | { 208 | #if ULONG_MAX < ULLONG_MAX 209 | return SHL__UPCAST_MULT(unsigned long long, ULONG_MAX, val, factor); 210 | #else 211 | shl_assert_cc(sizeof(unsigned long) == sizeof(unsigned long long)); 212 | return shl_mult_ull((unsigned long long*)val, factor); 213 | #endif 214 | } 215 | 216 | static inline int shl_mult_u(unsigned int *val, unsigned int factor) 217 | { 218 | #if UINT_MAX < ULONG_MAX 219 | return SHL__UPCAST_MULT(unsigned long, UINT_MAX, val, factor); 220 | #elif UINT_MAX < ULLONG_MAX 221 | return SHL__UPCAST_MULT(unsigned long long, UINT_MAX, val, factor); 222 | #else 223 | shl_assert_cc(sizeof(unsigned int) == sizeof(unsigned long long)); 224 | return shl_mult_ull(val, factor); 225 | #endif 226 | } 227 | 228 | static inline int shl_mult_u64(uint64_t *val, uint64_t factor) 229 | { 230 | return SHL__REAL_MULT(UINT64_MAX, val, factor); 231 | } 232 | 233 | static inline int shl_mult_u32(uint32_t *val, uint32_t factor) 234 | { 235 | return SHL__UPCAST_MULT(uint_fast64_t, UINT32_MAX, val, factor); 236 | } 237 | 238 | static inline int shl_mult_u16(uint16_t *val, uint16_t factor) 239 | { 240 | return SHL__UPCAST_MULT(uint_fast32_t, UINT16_MAX, val, factor); 241 | } 242 | 243 | static inline int shl_mult_u8(uint8_t *val, uint8_t factor) 244 | { 245 | return SHL__UPCAST_MULT(uint_fast16_t, UINT8_MAX, val, factor); 246 | } 247 | 248 | #endif /* SHL_MACRO_H */ 249 | -------------------------------------------------------------------------------- /src/shared/shl-pty.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SHL - PTY Helpers 3 | * 4 | * Copyright (c) 2011-2014 David Herrmann 5 | * Dedicated to the Public Domain 6 | */ 7 | 8 | /* 9 | * PTY Helpers 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include "shl-macro.h" 28 | #include "shl-pty.h" 29 | #include "shl-ring.h" 30 | 31 | #define SHL_PTY_BUFSIZE 16384 32 | 33 | /* 34 | * PTY 35 | * A PTY object represents a single PTY connection between a master and a 36 | * child. The child process is fork()ed so the caller controls what program 37 | * will be run. 38 | * 39 | * Programs like /bin/login tend to perform a vhangup() on their TTY 40 | * before running the login procedure. This also causes the pty master 41 | * to get a EPOLLHUP event as long as no client has the TTY opened. 42 | * This means, we cannot use the TTY connection as reliable way to track 43 | * the client. Instead, we _must_ rely on the PID of the client to track 44 | * them. 45 | * However, this has the side effect that if the client forks and the 46 | * parent exits, we loose them and restart the client. But this seems to 47 | * be the expected behavior so we implement it here. 48 | * 49 | * Unfortunately, epoll always polls for EPOLLHUP so as long as the 50 | * vhangup() is ongoing, we will _always_ get EPOLLHUP and cannot sleep. 51 | * This gets worse if the client closes the TTY but doesn't exit. 52 | * Therefore, the fd must be edge-triggered in the epoll-set so we 53 | * only get the events once they change. This has to be taken into account by 54 | * the user of shl_pty. As many event-loops don't support edge-triggered 55 | * behavior, you can use the shl_pty_bridge interface. 56 | * 57 | * Note that shl_pty does not track SIGHUP, you need to do that yourself 58 | * and call shl_pty_close() once the client exited. 59 | */ 60 | 61 | struct shl_pty { 62 | unsigned long ref; 63 | int fd; 64 | pid_t child; 65 | char in_buf[SHL_PTY_BUFSIZE]; 66 | struct shl_ring out_buf; 67 | 68 | shl_pty_input_fn fn_input; 69 | void *fn_input_data; 70 | }; 71 | 72 | enum shl_pty_msg { 73 | SHL_PTY_FAILED, 74 | SHL_PTY_SETUP, 75 | }; 76 | 77 | static char pty_recv(int fd) 78 | { 79 | int r; 80 | char d; 81 | 82 | do { 83 | r = read(fd, &d, 1); 84 | } while (r < 0 && (errno == EINTR || errno == EAGAIN)); 85 | 86 | return (r <= 0) ? SHL_PTY_FAILED : d; 87 | } 88 | 89 | static int pty_send(int fd, char d) 90 | { 91 | int r; 92 | 93 | do { 94 | r = write(fd, &d, 1); 95 | } while (r < 0 && (errno == EINTR || errno == EAGAIN)); 96 | 97 | return (r == 1) ? 0 : -EINVAL; 98 | } 99 | 100 | static int pty_setup_child(int slave, 101 | unsigned short term_width, 102 | unsigned short term_height) 103 | { 104 | struct termios attr; 105 | struct winsize ws; 106 | 107 | /* get terminal attributes */ 108 | if (tcgetattr(slave, &attr) < 0) 109 | return -errno; 110 | 111 | /* erase character should be normal backspace, PLEASEEE! */ 112 | attr.c_cc[VERASE] = 010; 113 | /* always set UTF8 flag */ 114 | attr.c_iflag |= IUTF8; 115 | 116 | /* set changed terminal attributes */ 117 | if (tcsetattr(slave, TCSANOW, &attr) < 0) 118 | return -errno; 119 | 120 | memset(&ws, 0, sizeof(ws)); 121 | ws.ws_col = term_width; 122 | ws.ws_row = term_height; 123 | 124 | if (ioctl(slave, TIOCSWINSZ, &ws) < 0) 125 | return -errno; 126 | 127 | if (dup2(slave, STDIN_FILENO) != STDIN_FILENO || 128 | dup2(slave, STDOUT_FILENO) != STDOUT_FILENO || 129 | dup2(slave, STDERR_FILENO) != STDERR_FILENO) 130 | return -errno; 131 | 132 | return 0; 133 | } 134 | 135 | static int pty_init_child(int fd) 136 | { 137 | int r; 138 | sigset_t sigset; 139 | char *slave_name; 140 | int slave, i; 141 | pid_t pid; 142 | 143 | /* unlockpt() requires unset signal-handlers */ 144 | sigemptyset(&sigset); 145 | r = sigprocmask(SIG_SETMASK, &sigset, NULL); 146 | if (r < 0) 147 | return -errno; 148 | 149 | for (i = 1; i < SIGSYS; ++i) 150 | signal(i, SIG_DFL); 151 | 152 | r = grantpt(fd); 153 | if (r < 0) 154 | return -errno; 155 | 156 | r = unlockpt(fd); 157 | if (r < 0) 158 | return -errno; 159 | 160 | slave_name = ptsname(fd); 161 | if (!slave_name) 162 | return -errno; 163 | 164 | /* open slave-TTY */ 165 | slave = open(slave_name, O_RDWR | O_CLOEXEC | O_NOCTTY); 166 | if (slave < 0) 167 | return -errno; 168 | 169 | /* open session so we loose our controlling TTY */ 170 | pid = setsid(); 171 | if (pid < 0) { 172 | close(slave); 173 | return -errno; 174 | } 175 | 176 | /* set controlling TTY */ 177 | r = ioctl(slave, TIOCSCTTY, 0); 178 | if (r < 0) { 179 | close(slave); 180 | return -errno; 181 | } 182 | 183 | return slave; 184 | } 185 | 186 | pid_t shl_pty_open(struct shl_pty **out, 187 | shl_pty_input_fn fn_input, 188 | void *fn_input_data, 189 | unsigned short term_width, 190 | unsigned short term_height) 191 | { 192 | _shl_pty_unref_ struct shl_pty *pty = NULL; 193 | _shl_close_ int fd = -1; 194 | int slave, r, comm[2]; 195 | pid_t pid; 196 | char d; 197 | 198 | if (!out) 199 | return -EINVAL; 200 | 201 | pty = calloc(1, sizeof(*pty)); 202 | if (!pty) 203 | return -ENOMEM; 204 | 205 | pty->ref = 1; 206 | pty->fd = -1; 207 | pty->fn_input = fn_input; 208 | pty->fn_input_data = fn_input_data; 209 | 210 | fd = posix_openpt(O_RDWR | O_NOCTTY | O_CLOEXEC | O_NONBLOCK); 211 | if (fd < 0) 212 | return -errno; 213 | 214 | r = pipe2(comm, O_CLOEXEC); 215 | if (r < 0) 216 | return -errno; 217 | 218 | pid = fork(); 219 | if (pid < 0) { 220 | /* error */ 221 | pid = -errno; 222 | close(comm[0]); 223 | close(comm[1]); 224 | return pid; 225 | } else if (!pid) { 226 | /* child */ 227 | slave = pty_init_child(fd); 228 | if (slave < 0) 229 | exit(1); 230 | 231 | close(comm[0]); 232 | close(fd); 233 | fd = -1; 234 | free(pty); 235 | pty = NULL; 236 | 237 | r = pty_setup_child(slave, term_width, term_height); 238 | if (r < 0) 239 | exit(1); 240 | 241 | /* close slave if it's not one of the std-fds */ 242 | if (slave > 2) 243 | close(slave); 244 | 245 | /* wake parent */ 246 | pty_send(comm[1], SHL_PTY_SETUP); 247 | close(comm[1]); 248 | 249 | *out = NULL; 250 | return pid; 251 | } 252 | 253 | /* parent */ 254 | pty->fd = fd; 255 | pty->child = pid; 256 | 257 | close(comm[1]); 258 | fd = -1; 259 | 260 | /* wait for child setup */ 261 | d = pty_recv(comm[0]); 262 | close(comm[0]); 263 | if (d != SHL_PTY_SETUP) 264 | return -EINVAL; 265 | 266 | *out = pty; 267 | pty = NULL; 268 | return pid; 269 | } 270 | 271 | void shl_pty_ref(struct shl_pty *pty) 272 | { 273 | if (!pty || !pty->ref) 274 | return; 275 | 276 | ++pty->ref; 277 | } 278 | 279 | void shl_pty_unref(struct shl_pty *pty) 280 | { 281 | if (!pty || !pty->ref || --pty->ref) 282 | return; 283 | 284 | shl_pty_close(pty); 285 | shl_ring_clear(&pty->out_buf); 286 | free(pty); 287 | } 288 | 289 | void shl_pty_close(struct shl_pty *pty) 290 | { 291 | if (!pty || pty->fd < 0) 292 | return; 293 | 294 | close(pty->fd); 295 | pty->fd = -1; 296 | } 297 | 298 | bool shl_pty_is_open(struct shl_pty *pty) 299 | { 300 | return pty && pty->fd >= 0; 301 | } 302 | 303 | int shl_pty_get_fd(struct shl_pty *pty) 304 | { 305 | if (!pty) 306 | return -EINVAL; 307 | 308 | return pty->fd >= 0 ? pty->fd : -EPIPE; 309 | } 310 | 311 | pid_t shl_pty_get_child(struct shl_pty *pty) 312 | { 313 | if (!pty) 314 | return -EINVAL; 315 | 316 | return pty->child > 0 ? pty->child : -ECHILD; 317 | } 318 | 319 | static int pty_write(struct shl_pty *pty) 320 | { 321 | struct iovec vec[2]; 322 | unsigned int i; 323 | size_t num; 324 | ssize_t r; 325 | 326 | /* 327 | * Same as pty_read(), we're edge-triggered so we need to call write() 328 | * until either all data is written or it return EAGAIN. We call it 329 | * twice and if it still writes successfully, we return EAGAIN. If we 330 | * bail out early, we also return EAGAIN if there's still data. 331 | */ 332 | 333 | for (i = 0; i < 2; ++i) { 334 | num = shl_ring_peek(&pty->out_buf, vec); 335 | if (!num) 336 | return 0; 337 | 338 | r = writev(pty->fd, vec, (int)num); 339 | if (r < 0) { 340 | if (errno == EAGAIN) 341 | return 0; 342 | if (errno == EINTR) 343 | return -EAGAIN; 344 | 345 | return -errno; 346 | } else if (!r) { 347 | return -EPIPE; 348 | } else { 349 | shl_ring_pull(&pty->out_buf, (size_t)r); 350 | } 351 | } 352 | 353 | return shl_ring_get_size(&pty->out_buf) > 0 ? -EAGAIN : 0; 354 | } 355 | 356 | static int pty_read(struct shl_pty *pty) 357 | { 358 | unsigned int i; 359 | ssize_t len; 360 | 361 | /* 362 | * We're edge-triggered, means we need to read the whole queue. This, 363 | * however, might cause us to stall if the writer is faster than we 364 | * are. Therefore, we read twice and if the second read still returned 365 | * data, we return -EAGAIN and let the caller deal with rescheduling the 366 | * dispatcher. 367 | */ 368 | 369 | for (i = 0; i < 2; ++i) { 370 | len = read(pty->fd, pty->in_buf, sizeof(pty->in_buf) - 1); 371 | if (len < 0) { 372 | if (errno == EAGAIN) 373 | return 0; 374 | if (errno == EINTR) 375 | return -EAGAIN; 376 | 377 | return -errno; 378 | } else if (!len) { 379 | return -EPIPE; 380 | } else if (len > 0 && pty->fn_input) { 381 | /* set terminating zero for debugging safety */ 382 | pty->in_buf[len] = 0; 383 | pty->fn_input(pty, 384 | pty->fn_input_data, 385 | pty->in_buf, 386 | len); 387 | } 388 | } 389 | 390 | return -EAGAIN; 391 | } 392 | 393 | int shl_pty_dispatch(struct shl_pty *pty) 394 | { 395 | int r; 396 | 397 | if (!shl_pty_is_open(pty)) 398 | return -ENODEV; 399 | 400 | r = pty_read(pty); 401 | pty_write(pty); 402 | return r; 403 | } 404 | 405 | int shl_pty_write(struct shl_pty *pty, const char *u8, size_t len) 406 | { 407 | if (!shl_pty_is_open(pty)) 408 | return -ENODEV; 409 | 410 | return shl_ring_push(&pty->out_buf, u8, len); 411 | } 412 | 413 | int shl_pty_signal(struct shl_pty *pty, int sig) 414 | { 415 | if (!shl_pty_is_open(pty)) 416 | return -ENODEV; 417 | 418 | return ioctl(pty->fd, TIOCSIG, sig) < 0 ? -errno : 0; 419 | } 420 | 421 | int shl_pty_resize(struct shl_pty *pty, 422 | unsigned short term_width, 423 | unsigned short term_height) 424 | { 425 | struct winsize ws; 426 | 427 | if (!shl_pty_is_open(pty)) 428 | return -ENODEV; 429 | 430 | memset(&ws, 0, sizeof(ws)); 431 | ws.ws_col = term_width; 432 | ws.ws_row = term_height; 433 | 434 | /* 435 | * This will send SIGWINCH to the pty slave foreground process group. 436 | * We will also get one, but we don't need it. 437 | */ 438 | return ioctl(pty->fd, TIOCSWINSZ, &ws) < 0 ? -errno : 0; 439 | } 440 | 441 | /* 442 | * PTY Bridge 443 | * The PTY bridge wraps multiple ptys in a single file-descriptor. It is 444 | * enough for the caller to listen for read-events on the fd. 445 | * 446 | * This interface is provided to allow integration of PTYs into event-loops 447 | * that do not support edge-triggered interfaces. There is no other reason 448 | * to use this bridge. 449 | */ 450 | 451 | int shl_pty_bridge_new(void) 452 | { 453 | int fd; 454 | 455 | fd = epoll_create1(EPOLL_CLOEXEC); 456 | if (fd < 0) 457 | return -errno; 458 | 459 | return fd; 460 | } 461 | 462 | void shl_pty_bridge_free(int bridge) 463 | { 464 | if (bridge < 0) 465 | return; 466 | 467 | close(bridge); 468 | } 469 | 470 | int shl_pty_bridge_dispatch_pty(int bridge, struct shl_pty *pty) 471 | { 472 | struct epoll_event up; 473 | int r; 474 | 475 | if (bridge < 0 || !pty) 476 | return -EINVAL; 477 | 478 | r = shl_pty_dispatch(pty); 479 | if (r == -EAGAIN) { 480 | /* EAGAIN means we couldn't dispatch data fast enough. Modify 481 | * the fd in the epoll-set so we get edge-triggered events 482 | * next round. */ 483 | memset(&up, 0, sizeof(up)); 484 | up.events = EPOLLHUP | EPOLLERR | EPOLLIN | EPOLLOUT | EPOLLET; 485 | up.data.ptr = pty; 486 | epoll_ctl(bridge, 487 | EPOLL_CTL_ADD, 488 | shl_pty_get_fd(pty), 489 | &up); 490 | } 491 | 492 | return 0; 493 | } 494 | 495 | int shl_pty_bridge_dispatch(int bridge, int timeout) 496 | { 497 | struct epoll_event ev; 498 | struct shl_pty *pty; 499 | int r; 500 | 501 | if (bridge < 0) 502 | return -EINVAL; 503 | 504 | r = epoll_wait(bridge, &ev, 1, timeout); 505 | if (r < 0) { 506 | if (errno == EAGAIN || errno == EINTR) 507 | return 0; 508 | 509 | return -errno; 510 | } 511 | 512 | if (!r) 513 | return 0; 514 | 515 | pty = ev.data.ptr; 516 | return shl_pty_bridge_dispatch_pty(bridge, pty); 517 | } 518 | 519 | int shl_pty_bridge_add(int bridge, struct shl_pty *pty) 520 | { 521 | struct epoll_event ev; 522 | int r; 523 | 524 | if (bridge < 0) 525 | return -EINVAL; 526 | if (!shl_pty_is_open(pty)) 527 | return -ENODEV; 528 | 529 | memset(&ev, 0, sizeof(ev)); 530 | ev.events = EPOLLHUP | EPOLLERR | EPOLLIN | EPOLLOUT | EPOLLET; 531 | ev.data.ptr = pty; 532 | 533 | r = epoll_ctl(bridge, 534 | EPOLL_CTL_ADD, 535 | shl_pty_get_fd(pty), 536 | &ev); 537 | if (r < 0) 538 | return -errno; 539 | 540 | return 0; 541 | } 542 | 543 | void shl_pty_bridge_remove(int bridge, struct shl_pty *pty) 544 | { 545 | if (bridge < 0 || !shl_pty_is_open(pty)) 546 | return; 547 | 548 | epoll_ctl(bridge, 549 | EPOLL_CTL_DEL, 550 | shl_pty_get_fd(pty), 551 | NULL); 552 | } 553 | -------------------------------------------------------------------------------- /src/shared/shl-pty.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SHL - PTY Helpers 3 | * 4 | * Copyright (c) 2011-2014 David Herrmann 5 | * Dedicated to the Public Domain 6 | */ 7 | 8 | /* 9 | * PTY Helpers 10 | */ 11 | 12 | #ifndef SHL_PTY_H 13 | #define SHL_PTY_H 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "shl-macro.h" 22 | 23 | /* pty */ 24 | 25 | struct shl_pty; 26 | 27 | typedef void (*shl_pty_input_fn) (struct shl_pty *pty, 28 | void *data, 29 | char *u8, 30 | size_t len); 31 | 32 | pid_t shl_pty_open(struct shl_pty **out, 33 | shl_pty_input_fn fn_input, 34 | void *fn_input_data, 35 | unsigned short term_width, 36 | unsigned short term_height); 37 | void shl_pty_ref(struct shl_pty *pty); 38 | void shl_pty_unref(struct shl_pty *pty); 39 | void shl_pty_close(struct shl_pty *pty); 40 | 41 | static inline void shl_pty_unref_p(struct shl_pty **pty) 42 | { 43 | shl_pty_unref(*pty); 44 | } 45 | 46 | #define _shl_pty_unref_ _shl_cleanup_(shl_pty_unref_p) 47 | 48 | bool shl_pty_is_open(struct shl_pty *pty); 49 | int shl_pty_get_fd(struct shl_pty *pty); 50 | pid_t shl_pty_get_child(struct shl_pty *pty); 51 | 52 | int shl_pty_dispatch(struct shl_pty *pty); 53 | int shl_pty_write(struct shl_pty *pty, const char *u8, size_t len); 54 | int shl_pty_signal(struct shl_pty *pty, int sig); 55 | int shl_pty_resize(struct shl_pty *pty, 56 | unsigned short term_width, 57 | unsigned short term_height); 58 | 59 | /* pty bridge */ 60 | 61 | int shl_pty_bridge_new(void); 62 | void shl_pty_bridge_free(int bridge); 63 | 64 | int shl_pty_bridge_dispatch_pty(int bridge, struct shl_pty *pty); 65 | int shl_pty_bridge_dispatch(int bridge, int timeout); 66 | int shl_pty_bridge_add(int bridge, struct shl_pty *pty); 67 | void shl_pty_bridge_remove(int bridge, struct shl_pty *pty); 68 | 69 | #endif /* SHL_PTY_H */ 70 | -------------------------------------------------------------------------------- /src/shared/shl-ring.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SHL - Ring buffer 3 | * 4 | * Copyright (c) 2011-2014 David Herrmann 5 | * Dedicated to the Public Domain 6 | */ 7 | 8 | /* 9 | * Ring buffer 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "shl-macro.h" 17 | #include "shl-ring.h" 18 | 19 | #define RING_MASK(_r, _v) ((_v) & ((_r)->size - 1)) 20 | 21 | void shl_ring_flush(struct shl_ring *r) 22 | { 23 | r->start = 0; 24 | r->used = 0; 25 | } 26 | 27 | void shl_ring_clear(struct shl_ring *r) 28 | { 29 | free(r->buf); 30 | memset(r, 0, sizeof(*r)); 31 | } 32 | 33 | /* 34 | * Get data pointers for current ring-buffer data. @vec must be an array of 2 35 | * iovec objects. They are filled according to the data available in the 36 | * ring-buffer. 0, 1 or 2 is returned according to the number of iovec objects 37 | * that were filled (0 meaning buffer is empty). 38 | * 39 | * Hint: "struct iovec" is defined in and looks like this: 40 | * struct iovec { 41 | * void *iov_base; 42 | * size_t iov_len; 43 | * }; 44 | */ 45 | size_t shl_ring_peek(struct shl_ring *r, struct iovec *vec) 46 | { 47 | if (r->used == 0) { 48 | return 0; 49 | } else if (r->start + r->used <= r->size) { 50 | if (vec) { 51 | vec[0].iov_base = &r->buf[r->start]; 52 | vec[0].iov_len = r->used; 53 | } 54 | return 1; 55 | } else { 56 | if (vec) { 57 | vec[0].iov_base = &r->buf[r->start]; 58 | vec[0].iov_len = r->size - r->start; 59 | vec[1].iov_base = r->buf; 60 | vec[1].iov_len = r->used - (r->size - r->start); 61 | } 62 | return 2; 63 | } 64 | } 65 | 66 | /* 67 | * Copy data from the ring buffer into the linear external buffer @buf. Copy 68 | * at most @size bytes. If the ring buffer size is smaller, copy less bytes and 69 | * return the number of bytes copied. 70 | */ 71 | size_t shl_ring_copy(struct shl_ring *r, void *buf, size_t size) 72 | { 73 | size_t l; 74 | 75 | if (size > r->used) 76 | size = r->used; 77 | 78 | if (size > 0) { 79 | l = r->size - r->start; 80 | if (size <= l) { 81 | memcpy(buf, &r->buf[r->start], size); 82 | } else { 83 | memcpy(buf, &r->buf[r->start], l); 84 | memcpy((uint8_t*)buf + l, r->buf, size - l); 85 | } 86 | } 87 | 88 | return size; 89 | } 90 | 91 | /* 92 | * Resize ring-buffer to size @nsize. @nsize must be a power-of-2, otherwise 93 | * ring operations will behave incorrectly. 94 | */ 95 | static int ring_resize(struct shl_ring *r, size_t nsize) 96 | { 97 | uint8_t *buf; 98 | size_t l; 99 | 100 | buf = malloc(nsize); 101 | if (!buf) 102 | return -ENOMEM; 103 | 104 | if (r->used > 0) { 105 | l = r->size - r->start; 106 | if (r->used <= l) { 107 | memcpy(buf, &r->buf[r->start], r->used); 108 | } else { 109 | memcpy(buf, &r->buf[r->start], l); 110 | memcpy(&buf[l], r->buf, r->used - l); 111 | } 112 | } 113 | 114 | free(r->buf); 115 | r->buf = buf; 116 | r->size = nsize; 117 | r->start = 0; 118 | 119 | return 0; 120 | } 121 | 122 | /* 123 | * Resize ring-buffer to provide enough room for @add bytes of new data. This 124 | * resizes the buffer if it is too small. It returns -ENOMEM on OOM and 0 on 125 | * success. 126 | */ 127 | static int ring_grow(struct shl_ring *r, size_t add) 128 | { 129 | size_t need; 130 | 131 | if (r->size - r->used >= add) 132 | return 0; 133 | 134 | need = r->used + add; 135 | if (need <= r->used) 136 | return -ENOMEM; 137 | else if (need < 4096) 138 | need = 4096; 139 | 140 | need = SHL_ALIGN_POWER2(need); 141 | if (need == 0) 142 | return -ENOMEM; 143 | 144 | return ring_resize(r, need); 145 | } 146 | 147 | /* 148 | * Push @len bytes from @u8 into the ring buffer. The buffer is resized if it 149 | * is too small. -ENOMEM is returned on OOM, 0 on success. 150 | */ 151 | int shl_ring_push(struct shl_ring *r, const void *u8, size_t size) 152 | { 153 | int err; 154 | size_t pos, l; 155 | 156 | if (size == 0) 157 | return 0; 158 | 159 | err = ring_grow(r, size); 160 | if (err < 0) 161 | return err; 162 | 163 | pos = RING_MASK(r, r->start + r->used); 164 | l = r->size - pos; 165 | if (l >= size) { 166 | memcpy(&r->buf[pos], u8, size); 167 | } else { 168 | memcpy(&r->buf[pos], u8, l); 169 | memcpy(r->buf, (const uint8_t*)u8 + l, size - l); 170 | } 171 | 172 | r->used += size; 173 | 174 | return 0; 175 | } 176 | 177 | /* 178 | * Remove @len bytes from the start of the ring-buffer. Note that we protect 179 | * against overflows so removing more bytes than available is safe. 180 | */ 181 | void shl_ring_pull(struct shl_ring *r, size_t size) 182 | { 183 | if (size > r->used) 184 | size = r->used; 185 | 186 | r->start = RING_MASK(r, r->start + size); 187 | r->used -= size; 188 | } 189 | -------------------------------------------------------------------------------- /src/shared/shl-ring.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SHL - Ring buffer 3 | * 4 | * Copyright (c) 2011-2014 David Herrmann 5 | * Dedicated to the Public Domain 6 | */ 7 | 8 | /* 9 | * Ring buffer 10 | */ 11 | 12 | #ifndef SHL_RING_H 13 | #define SHL_RING_H 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | struct shl_ring { 22 | uint8_t *buf; /* buffer or NULL */ 23 | size_t size; /* actual size of @buf */ 24 | size_t start; /* start position of ring */ 25 | size_t used; /* number of actually used bytes */ 26 | }; 27 | 28 | /* flush buffer so it is empty again */ 29 | void shl_ring_flush(struct shl_ring *r); 30 | 31 | /* flush buffer, free allocated data and reset to initial state */ 32 | void shl_ring_clear(struct shl_ring *r); 33 | 34 | /* get pointers to buffer data and their length */ 35 | size_t shl_ring_peek(struct shl_ring *r, struct iovec *vec); 36 | 37 | /* copy data into external linear buffer */ 38 | size_t shl_ring_copy(struct shl_ring *r, void *buf, size_t size); 39 | 40 | /* push data to the end of the buffer */ 41 | int shl_ring_push(struct shl_ring *r, const void *u8, size_t size); 42 | 43 | /* pull data from the front of the buffer */ 44 | void shl_ring_pull(struct shl_ring *r, size_t size); 45 | 46 | /* return size of occupied buffer in bytes */ 47 | static inline size_t shl_ring_get_size(struct shl_ring *r) 48 | { 49 | return r->used; 50 | } 51 | 52 | #endif /* SHL_RING_H */ 53 | -------------------------------------------------------------------------------- /src/tsm/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # libtsm 3 | # Main library build instructions 4 | # 5 | 6 | # 7 | # Use a separate object library that is shared between tsm and tsm_test 8 | # 9 | add_library(tsm_obj OBJECT 10 | tsm-render.c 11 | tsm-screen.c 12 | tsm-selection.c 13 | tsm-unicode.c 14 | tsm-vte.c 15 | tsm-vte-charsets.c 16 | ) 17 | set_target_properties(tsm_obj PROPERTIES 18 | POSITION_INDEPENDENT_CODE ON 19 | C_VISIBILITY_PRESET hidden 20 | ) 21 | 22 | # Fix include path for tsm_obj because it can't directly link to 23 | # external and shl. TODO: remove after we require 3.12 24 | foreach(lib IN ITEMS external shl) 25 | target_include_directories(tsm_obj PRIVATE $) 26 | endforeach() 27 | 28 | # Other non-compilation properties shared between tsm and tsm_test 29 | function(apply_properties target) 30 | # TODO: set this on tsm_obj after we require 3.12 31 | # Link to dependencies 32 | target_link_object_libraries(${target} 33 | PRIVATE 34 | external 35 | shl 36 | ) 37 | if(XKBCommon_KeySyms_FOUND) 38 | target_link_libraries(${target} 39 | PRIVATE 40 | XKB::KeySyms 41 | ) 42 | endif() 43 | target_include_directories(${target} 44 | INTERFACE 45 | $ 46 | $ 47 | ) 48 | set_target_properties(${target} PROPERTIES 49 | POSITION_INDEPENDENT_CODE ON 50 | C_VISIBILITY_PRESET hidden 51 | VERSION "${PROJECT_VERSION}" 52 | SOVERSION "${PROJECT_VERSION_MAJOR}" 53 | PUBLIC_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/libtsm.h" 54 | ) 55 | add_libtsm_compile_options(${target}) 56 | endfunction() 57 | 58 | # 59 | # Main production library 60 | # 61 | add_library(tsm) 62 | target_link_object_libraries(tsm PRIVATE tsm_obj) 63 | apply_properties(tsm) 64 | # The production library additionally use version script 65 | if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") 66 | set_property(TARGET tsm APPEND_STRING 67 | PROPERTY 68 | LINK_FLAGS " -Wl,--version-script=\"${CMAKE_CURRENT_SOURCE_DIR}/libtsm.sym\"" 69 | ) 70 | set_property(TARGET tsm APPEND 71 | PROPERTY 72 | LINK_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/libtsm.sym" 73 | ) 74 | endif() 75 | 76 | # Add an alias so that library can be used inside the build tree 77 | add_library(libtsm::tsm ALIAS tsm) 78 | 79 | # 80 | # Add an additional static library for testing 81 | # 82 | if(BUILD_TESTING) 83 | # Must be static to avoid visibility=hidden 84 | add_library(tsm_test STATIC) 85 | target_link_object_libraries(tsm_test PRIVATE tsm_obj) 86 | apply_properties(tsm_test) 87 | # Additionally expose internal private header path 88 | target_include_directories(tsm_test 89 | INTERFACE 90 | $ 91 | ) 92 | endif() 93 | 94 | # 95 | # Installation 96 | # 97 | install(TARGETS tsm EXPORT tsm-targets 98 | LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" 99 | ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" 100 | PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" 101 | ) 102 | -------------------------------------------------------------------------------- /src/tsm/libtsm-int.h: -------------------------------------------------------------------------------- 1 | /* 2 | * TSM - Main internal header 3 | * 4 | * Copyright (c) 2018 Aetf 5 | * Copyright (c) 2011-2013 David Herrmann 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining 8 | * a copy of this software and associated documentation files 9 | * (the "Software"), to deal in the Software without restriction, including 10 | * without limitation the rights to use, copy, modify, merge, publish, 11 | * distribute, sublicense, and/or sell copies of the Software, and to 12 | * permit persons to whom the Software is furnished to do so, subject to 13 | * the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included 16 | * in all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | */ 26 | 27 | #ifndef TSM_LIBTSM_INT_H 28 | #define TSM_LIBTSM_INT_H 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include "libtsm.h" 35 | #include "shl-llog.h" 36 | 37 | #define SHL_EXPORT __attribute__((visibility("default"))) 38 | 39 | /* max combined-symbol length */ 40 | #define TSM_UCS4_MAXLEN 10 41 | 42 | /* symbols */ 43 | 44 | struct tsm_symbol_table; 45 | 46 | extern const tsm_symbol_t tsm_symbol_default; 47 | 48 | int tsm_symbol_table_new(struct tsm_symbol_table **out); 49 | void tsm_symbol_table_ref(struct tsm_symbol_table *tbl); 50 | void tsm_symbol_table_unref(struct tsm_symbol_table *tbl); 51 | 52 | tsm_symbol_t tsm_symbol_make(uint32_t ucs4); 53 | tsm_symbol_t tsm_symbol_append(struct tsm_symbol_table *tbl, 54 | tsm_symbol_t sym, uint32_t ucs4); 55 | const uint32_t *tsm_symbol_get(struct tsm_symbol_table *tbl, 56 | tsm_symbol_t *sym, size_t *size); 57 | unsigned int tsm_symbol_get_width(struct tsm_symbol_table *tbl, 58 | tsm_symbol_t sym); 59 | 60 | /* utf8 state machine */ 61 | 62 | struct tsm_utf8_mach; 63 | 64 | enum tsm_utf8_mach_state { 65 | TSM_UTF8_START, 66 | TSM_UTF8_ACCEPT, 67 | TSM_UTF8_REJECT, 68 | TSM_UTF8_EXPECT1, 69 | TSM_UTF8_EXPECT2, 70 | TSM_UTF8_EXPECT3, 71 | }; 72 | 73 | int tsm_utf8_mach_new(struct tsm_utf8_mach **out); 74 | void tsm_utf8_mach_free(struct tsm_utf8_mach *mach); 75 | 76 | int tsm_utf8_mach_feed(struct tsm_utf8_mach *mach, char c); 77 | uint32_t tsm_utf8_mach_get(struct tsm_utf8_mach *mach); 78 | void tsm_utf8_mach_reset(struct tsm_utf8_mach *mach); 79 | 80 | /* TSM screen */ 81 | 82 | struct cell { 83 | tsm_symbol_t ch; /* stored character */ 84 | unsigned int width; /* character width */ 85 | struct tsm_screen_attr attr; /* cell attributes */ 86 | tsm_age_t age; /* age of the single cell */ 87 | }; 88 | 89 | struct line { 90 | struct line *next; /* next line (NULL if not sb) */ 91 | struct line *prev; /* prev line (NULL if not sb) */ 92 | 93 | unsigned int size; /* real width */ 94 | struct cell *cells; /* actuall cells */ 95 | uint64_t sb_id; /* sb ID */ 96 | tsm_age_t age; /* age of the whole line */ 97 | }; 98 | 99 | #define SELECTION_TOP -1 100 | struct selection_pos { 101 | struct line *line; 102 | unsigned int x; 103 | int y; 104 | }; 105 | 106 | struct tsm_screen { 107 | size_t ref; 108 | llog_submit_t llog; 109 | void *llog_data; 110 | unsigned int opts; 111 | unsigned int flags; 112 | struct tsm_symbol_table *sym_table; 113 | 114 | /* default attributes for new cells */ 115 | struct tsm_screen_attr def_attr; 116 | 117 | /* save default attributes of main screen here when we switch to alt screen 118 | * on resize of the alt screen we need to init the new cells of the main 119 | * screen with these attributes and not the ones of the alt screen */ 120 | struct tsm_screen_attr def_attr_main; 121 | 122 | /* ageing */ 123 | tsm_age_t age_cnt; /* current age counter */ 124 | unsigned int age_reset : 1; /* age-overflow flag */ 125 | 126 | /* current buffer */ 127 | unsigned int size_x; /* width of screen */ 128 | unsigned int size_y; /* height of screen */ 129 | unsigned int margin_top; /* top-margin index */ 130 | unsigned int margin_bottom; /* bottom-margin index */ 131 | unsigned int line_num; /* real number of allocated lines */ 132 | struct line **lines; /* active lines; copy of main/alt */ 133 | struct line **main_lines; /* real main lines */ 134 | struct line **alt_lines; /* real alternative lines */ 135 | tsm_age_t age; /* whole screen age */ 136 | 137 | /* scroll-back buffer */ 138 | unsigned int sb_count; /* number of lines in sb */ 139 | struct line *sb_first; /* first line; was moved first */ 140 | struct line *sb_last; /* last line; was moved last*/ 141 | unsigned int sb_max; /* max-limit of lines in sb */ 142 | struct line *sb_pos; /* current position in sb or NULL */ 143 | unsigned int sb_pos_num; /* current numeric position in sb */ 144 | uint64_t sb_last_id; /* last id given to sb-line */ 145 | 146 | /* cursor: positions are always in-bound, but cursor_x might be 147 | * bigger than size_x if new-line is pending */ 148 | unsigned int cursor_x; /* current cursor x-pos */ 149 | unsigned int cursor_y; /* current cursor y-pos */ 150 | 151 | /* tab ruler */ 152 | bool *tab_ruler; /* tab-flag for all cells of one row */ 153 | 154 | /* selection */ 155 | bool sel_active; 156 | struct selection_pos sel_start; 157 | struct selection_pos sel_end; 158 | }; 159 | 160 | void screen_cell_init(struct tsm_screen *con, struct cell *cell); 161 | 162 | void tsm_screen_set_opts(struct tsm_screen *scr, unsigned int opts); 163 | void tsm_screen_reset_opts(struct tsm_screen *scr, unsigned int opts); 164 | unsigned int tsm_screen_get_opts(struct tsm_screen *scr); 165 | 166 | static inline void screen_inc_age(struct tsm_screen *con) 167 | { 168 | if (!++con->age_cnt) { 169 | con->age_reset = 1; 170 | ++con->age_cnt; 171 | } 172 | } 173 | 174 | /* available character sets */ 175 | 176 | typedef tsm_symbol_t tsm_vte_charset[96]; 177 | 178 | extern tsm_vte_charset tsm_vte_unicode_lower; 179 | extern tsm_vte_charset tsm_vte_unicode_upper; 180 | extern tsm_vte_charset tsm_vte_dec_supplemental_graphics; 181 | extern tsm_vte_charset tsm_vte_dec_special_graphics; 182 | 183 | #endif /* TSM_LIBTSM_INT_H */ 184 | -------------------------------------------------------------------------------- /src/tsm/libtsm.sym: -------------------------------------------------------------------------------- 1 | /* 2 | * libtsm - Terminal-Emulator State Machine 3 | * 4 | * Copyright (c) 2012-2013 David Herrmann 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | LIBTSM_1 { 27 | global: 28 | tsm_ucs4_get_width; 29 | local: 30 | *; 31 | }; 32 | 33 | LIBTSM_2 { 34 | global: 35 | tsm_ucs4_to_utf8; 36 | tsm_ucs4_to_utf8_alloc; 37 | } LIBTSM_1; 38 | 39 | LIBTSM_3 { 40 | global: 41 | tsm_screen_new; 42 | tsm_screen_ref; 43 | tsm_screen_unref; 44 | 45 | tsm_screen_get_width; 46 | tsm_screen_get_height; 47 | tsm_screen_resize; 48 | tsm_screen_set_margins; 49 | tsm_screen_set_max_sb; 50 | tsm_screen_clear_sb; 51 | 52 | tsm_screen_sb_up; 53 | tsm_screen_sb_down; 54 | tsm_screen_sb_page_up; 55 | tsm_screen_sb_page_down; 56 | tsm_screen_sb_reset; 57 | 58 | tsm_screen_set_def_attr; 59 | tsm_screen_reset; 60 | tsm_screen_set_flags; 61 | tsm_screen_reset_flags; 62 | tsm_screen_get_flags; 63 | 64 | tsm_screen_get_cursor_x; 65 | tsm_screen_get_cursor_y; 66 | 67 | tsm_screen_set_tabstop; 68 | tsm_screen_reset_tabstop; 69 | tsm_screen_reset_all_tabstops; 70 | 71 | tsm_screen_write; 72 | tsm_screen_newline; 73 | tsm_screen_scroll_up; 74 | tsm_screen_scroll_down; 75 | tsm_screen_move_to; 76 | tsm_screen_move_up; 77 | tsm_screen_move_down; 78 | tsm_screen_move_left; 79 | tsm_screen_move_right; 80 | tsm_screen_move_line_end; 81 | tsm_screen_move_line_home; 82 | tsm_screen_tab_right; 83 | tsm_screen_tab_left; 84 | tsm_screen_insert_lines; 85 | tsm_screen_delete_lines; 86 | tsm_screen_insert_chars; 87 | tsm_screen_delete_chars; 88 | tsm_screen_erase_cursor; 89 | tsm_screen_erase_chars; 90 | tsm_screen_erase_cursor_to_end; 91 | tsm_screen_erase_home_to_cursor; 92 | tsm_screen_erase_current_line; 93 | tsm_screen_erase_screen_to_cursor; 94 | tsm_screen_erase_cursor_to_screen; 95 | tsm_screen_erase_screen; 96 | 97 | tsm_screen_selection_reset; 98 | tsm_screen_selection_start; 99 | tsm_screen_selection_target; 100 | tsm_screen_selection_copy; 101 | 102 | tsm_vte_new; 103 | tsm_vte_ref; 104 | tsm_vte_unref; 105 | 106 | tsm_vte_set_osc_cb; 107 | 108 | tsm_vte_set_palette; 109 | tsm_vte_get_def_attr; 110 | 111 | tsm_vte_reset; 112 | tsm_vte_hard_reset; 113 | tsm_vte_input; 114 | tsm_vte_handle_keyboard; 115 | } LIBTSM_2; 116 | 117 | LIBTSM_4 { 118 | global: 119 | tsm_screen_draw; 120 | 121 | tsm_vte_set_custom_palette; 122 | } LIBTSM_3; 123 | 124 | LIBTSM_4_1 { 125 | global: 126 | tsm_vte_set_backspace_sends_delete; 127 | tsm_vte_get_flags; 128 | tsm_vte_set_mouse_cb; 129 | tsm_vte_get_mouse_mode; 130 | tsm_vte_get_mouse_event; 131 | tsm_vte_handle_mouse; 132 | tsm_screen_sb_get_line_count; 133 | tsm_screen_sb_get_line_pos; 134 | } LIBTSM_4; 135 | -------------------------------------------------------------------------------- /src/tsm/tsm-render.c: -------------------------------------------------------------------------------- 1 | /* 2 | * libtsm - Rendering 3 | * 4 | * Copyright (c) 2011-2013 David Herrmann 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | /* 27 | * Rendering 28 | * TSM does not depend on any graphics system or rendering libraries. Instead, 29 | * it provides iterators and ageing support so you can implement renderers 30 | * yourself. 31 | */ 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include "libtsm.h" 39 | #include "libtsm-int.h" 40 | #include "shl-llog.h" 41 | 42 | #define LLOG_SUBSYSTEM "tsm-render" 43 | 44 | SHL_EXPORT 45 | tsm_age_t tsm_screen_draw(struct tsm_screen *con, tsm_screen_draw_cb draw_cb, 46 | void *data) 47 | { 48 | unsigned int cur_x, cur_y; 49 | unsigned int i, j, k; 50 | struct line *iter, *line = NULL; 51 | struct cell *cell, empty; 52 | struct tsm_screen_attr attr; 53 | int ret, warned = 0; 54 | const uint32_t *ch; 55 | uint64_t id; 56 | size_t len; 57 | bool in_sel = false, sel_start = false, sel_end = false; 58 | bool was_sel = false; 59 | tsm_age_t age; 60 | 61 | if (!con || !draw_cb) 62 | return 0; 63 | 64 | screen_cell_init(con, &empty); 65 | 66 | cur_x = con->cursor_x; 67 | if (con->cursor_x >= con->size_x) 68 | cur_x = con->size_x - 1; 69 | cur_y = con->cursor_y; 70 | if (con->cursor_y >= con->size_y) 71 | cur_y = con->size_y - 1; 72 | 73 | /* push each character into rendering pipeline */ 74 | 75 | iter = con->sb_pos; 76 | k = 0; 77 | 78 | if (con->sel_active) { 79 | if (!con->sel_start.line && con->sel_start.y == SELECTION_TOP) 80 | in_sel = !in_sel; 81 | if (!con->sel_end.line && con->sel_end.y == SELECTION_TOP) 82 | in_sel = !in_sel; 83 | 84 | if (con->sel_start.line && 85 | (!iter || con->sel_start.line->sb_id < iter->sb_id)) 86 | in_sel = !in_sel; 87 | if (con->sel_end.line && 88 | (!iter || con->sel_end.line->sb_id < iter->sb_id)) 89 | in_sel = !in_sel; 90 | } 91 | 92 | for (i = 0; i < con->size_y; ++i) { 93 | if (iter) { 94 | line = iter; 95 | iter = iter->next; 96 | } else { 97 | line = con->lines[k]; 98 | k++; 99 | } 100 | 101 | if (con->sel_active) { 102 | if (con->sel_start.line == line || 103 | (!con->sel_start.line && 104 | con->sel_start.y == k - 1)) 105 | sel_start = true; 106 | else 107 | sel_start = false; 108 | if (con->sel_end.line == line || 109 | (!con->sel_end.line && 110 | con->sel_end.y == k - 1)) 111 | sel_end = true; 112 | else 113 | sel_end = false; 114 | 115 | was_sel = false; 116 | } 117 | 118 | for (j = 0; j < con->size_x; ++j) { 119 | if (j < line->size) 120 | cell = &line->cells[j]; 121 | else 122 | cell = ∅ 123 | 124 | memcpy(&attr, &cell->attr, sizeof(attr)); 125 | 126 | if (con->sel_active) { 127 | if (sel_start && 128 | j == con->sel_start.x) { 129 | was_sel = in_sel; 130 | in_sel = !in_sel; 131 | } 132 | if (sel_end && 133 | j == con->sel_end.x) { 134 | was_sel = in_sel; 135 | in_sel = !in_sel; 136 | } 137 | } 138 | 139 | if (k == cur_y + 1 && j == cur_x && 140 | !(con->flags & TSM_SCREEN_HIDE_CURSOR)) 141 | attr.inverse = !attr.inverse; 142 | 143 | /* TODO: do some more sophisticated inverse here. When 144 | * INVERSE mode is set, we should instead just select 145 | * inverse colors instead of switching background and 146 | * foreground */ 147 | if (con->flags & TSM_SCREEN_INVERSE) 148 | attr.inverse = !attr.inverse; 149 | 150 | if (in_sel || was_sel) { 151 | was_sel = false; 152 | attr.inverse = !attr.inverse; 153 | } 154 | 155 | if (con->age_reset) { 156 | age = 0; 157 | } else { 158 | age = cell->age; 159 | if (line->age > age) 160 | age = line->age; 161 | if (con->age > age) 162 | age = con->age; 163 | } 164 | 165 | /* Encode attributes into the id to avoid caching problems */ 166 | id = cell->ch; 167 | if (attr.bold) 168 | id |= 1ULL << TSM_UCS4_MAX_BITS; 169 | if (attr.italic) 170 | id |= 1ULL << (TSM_UCS4_MAX_BITS + 1); 171 | if (attr.underline) 172 | id |= 1ULL << (TSM_UCS4_MAX_BITS + 2); 173 | if (attr.inverse) 174 | id |= 1ULL << (TSM_UCS4_MAX_BITS + 3); 175 | if (attr.blink) 176 | id |= 1ULL << (TSM_UCS4_MAX_BITS + 4); 177 | 178 | ch = tsm_symbol_get(con->sym_table, &cell->ch, &len); 179 | if (cell->ch == 0 || (cell->ch == ' ' && !attr.underline)) 180 | len = 0; 181 | ret = draw_cb(con, id, ch, len, cell->width, 182 | j, i, &attr, age, data); 183 | if (ret && warned++ < 3) { 184 | llog_debug(con, 185 | "cannot draw glyph at %ux%u via text-renderer", 186 | j, i); 187 | if (warned == 3) 188 | llog_debug(con, 189 | "suppressing further warnings during this rendering round"); 190 | } 191 | } 192 | } 193 | 194 | if (con->age_reset) { 195 | con->age_reset = 0; 196 | return 0; 197 | } else { 198 | return con->age_cnt; 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /src/tsm/tsm-vte-charsets.c: -------------------------------------------------------------------------------- 1 | /* 2 | * TSM - VT Emulator 3 | * 4 | * Copyright (c) 2012 David Herrmann 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | /* 27 | * VTE Character Sets 28 | * These are predefined charactersets that can be loaded into GL and GR. By 29 | * default we use unicode_lower and unicode_upper, that is, both sets have the 30 | * exact unicode mapping. unicode_lower is effectively ASCII and unicode_upper 31 | * as defined by the unicode standard. 32 | * Several other character sets are defined here. However, all of them are 33 | * limited to the 96 character space of GL or GR. Everything beyond GR (which 34 | * was not supported by the classic VTs by DEC but is available in VT emulators 35 | * that support unicode/UTF8) is always mapped to unicode and cannot be changed 36 | * by these character sets. Even mapping GL and GR is only available for 37 | * backwards compatibility as new applications can use the Unicode functionality 38 | * of the VTE. 39 | * 40 | * Moreover, mapping GR is almost unnecessary to support. In fact, Unicode UTF-8 41 | * support in VTE works by reading every incoming data as UTF-8 stream. This 42 | * maps GL/ASCII to ASCII, as UTF-8 is backwards compatible to ASCII, however, 43 | * everything that has the 8th bit set is a >=2-byte haracter in UTF-8. That is, 44 | * this is in no way backwards compatible to >=VT220 8bit support. Therefore, if 45 | * someone maps a character set into GR and wants to use them with this VTE, 46 | * then they must already send UTF-8 characters to use GR (all GR characters are 47 | * 8-bits). Hence, they can easily also send the correct UTF-8 character for the 48 | * unicode mapping. 49 | * The only advantage is that most characters in many sets are 3-byte UTF-8 50 | * characters and by mapping the set into GR/GL you can use 2 or 1 byte UTF-8 51 | * characters which saves bandwidth. 52 | * Another reason is, if you have older applications that use the VT220 8-bit 53 | * support and you put a ASCII/8bit-extension to UTF-8 converter in between, you 54 | * need these mappings to have the application behave correctly if it uses GL/GR 55 | * mappings extensively. 56 | * 57 | * Anyway, we support GL/GR mappings so here are the most commonly used maps as 58 | * defined by Unicode-standard, DEC-private maps and other famous charmaps. 59 | * 60 | * Characters 1-32 are always the control characters (part of CL) and cannot be 61 | * mapped. Characters 34-127 (94 characters) are part of GL and can be mapped. 62 | * Characters 33 and 128 are not part of GL and always mapped by VTE but are 63 | * included here in the maps for alignment reasons but always set to 0. 64 | */ 65 | 66 | #include 67 | #include 68 | #include 69 | #include "libtsm.h" 70 | #include "libtsm-int.h" 71 | 72 | /* 73 | * Lower Unicode character set. This maps the characters to the basic ASCII 74 | * characters 33-126. These are all graphics characters defined in ASCII. The 75 | * first an last entry are never used so we can safely set them to anything. 76 | */ 77 | tsm_vte_charset tsm_vte_unicode_lower = { 78 | [0] = 0, 79 | [1] = 33, 80 | [2] = 34, 81 | [3] = 35, 82 | [4] = 36, 83 | [5] = 37, 84 | [6] = 38, 85 | [7] = 39, 86 | [8] = 40, 87 | [9] = 41, 88 | [10] = 42, 89 | [11] = 43, 90 | [12] = 44, 91 | [13] = 45, 92 | [14] = 46, 93 | [15] = 47, 94 | [16] = 48, 95 | [17] = 49, 96 | [18] = 50, 97 | [19] = 51, 98 | [20] = 52, 99 | [21] = 53, 100 | [22] = 54, 101 | [23] = 55, 102 | [24] = 56, 103 | [25] = 57, 104 | [26] = 58, 105 | [27] = 59, 106 | [28] = 60, 107 | [29] = 61, 108 | [30] = 62, 109 | [31] = 63, 110 | [32] = 64, 111 | [33] = 65, 112 | [34] = 66, 113 | [35] = 67, 114 | [36] = 68, 115 | [37] = 69, 116 | [38] = 70, 117 | [39] = 71, 118 | [40] = 72, 119 | [41] = 73, 120 | [42] = 74, 121 | [43] = 75, 122 | [44] = 76, 123 | [45] = 77, 124 | [46] = 78, 125 | [47] = 79, 126 | [48] = 80, 127 | [49] = 81, 128 | [50] = 82, 129 | [51] = 83, 130 | [52] = 84, 131 | [53] = 85, 132 | [54] = 86, 133 | [55] = 87, 134 | [56] = 88, 135 | [57] = 89, 136 | [58] = 90, 137 | [59] = 91, 138 | [60] = 92, 139 | [61] = 93, 140 | [62] = 94, 141 | [63] = 95, 142 | [64] = 96, 143 | [65] = 97, 144 | [66] = 98, 145 | [67] = 99, 146 | [68] = 100, 147 | [69] = 101, 148 | [70] = 102, 149 | [71] = 103, 150 | [72] = 104, 151 | [73] = 105, 152 | [74] = 106, 153 | [75] = 107, 154 | [76] = 108, 155 | [77] = 109, 156 | [78] = 110, 157 | [79] = 111, 158 | [80] = 112, 159 | [81] = 113, 160 | [82] = 114, 161 | [83] = 115, 162 | [84] = 116, 163 | [85] = 117, 164 | [86] = 118, 165 | [87] = 119, 166 | [88] = 120, 167 | [89] = 121, 168 | [90] = 122, 169 | [91] = 123, 170 | [92] = 124, 171 | [93] = 125, 172 | [94] = 126, 173 | [95] = 0, 174 | }; 175 | 176 | /* 177 | * Upper Unicode Table 178 | * This maps all characters to the upper unicode characters 161-254. These are 179 | * not compatible to any older 8 bit character sets. See the Unicode standard 180 | * for the definitions of each symbol. Again, the first an last entry are never 181 | * used so set them to 0. 182 | */ 183 | tsm_vte_charset tsm_vte_unicode_upper = { 184 | [0] = 0, 185 | [1] = 161, 186 | [2] = 162, 187 | [3] = 163, 188 | [4] = 164, 189 | [5] = 165, 190 | [6] = 166, 191 | [7] = 167, 192 | [8] = 168, 193 | [9] = 169, 194 | [10] = 170, 195 | [11] = 171, 196 | [12] = 172, 197 | [13] = 173, 198 | [14] = 174, 199 | [15] = 175, 200 | [16] = 176, 201 | [17] = 177, 202 | [18] = 178, 203 | [19] = 179, 204 | [20] = 180, 205 | [21] = 181, 206 | [22] = 182, 207 | [23] = 183, 208 | [24] = 184, 209 | [25] = 185, 210 | [26] = 186, 211 | [27] = 187, 212 | [28] = 188, 213 | [29] = 189, 214 | [30] = 190, 215 | [31] = 191, 216 | [32] = 192, 217 | [33] = 193, 218 | [34] = 194, 219 | [35] = 195, 220 | [36] = 196, 221 | [37] = 197, 222 | [38] = 198, 223 | [39] = 199, 224 | [40] = 200, 225 | [41] = 201, 226 | [42] = 202, 227 | [43] = 203, 228 | [44] = 204, 229 | [45] = 205, 230 | [46] = 206, 231 | [47] = 207, 232 | [48] = 208, 233 | [49] = 209, 234 | [50] = 210, 235 | [51] = 211, 236 | [52] = 212, 237 | [53] = 213, 238 | [54] = 214, 239 | [55] = 215, 240 | [56] = 216, 241 | [57] = 217, 242 | [58] = 218, 243 | [59] = 219, 244 | [60] = 220, 245 | [61] = 221, 246 | [62] = 222, 247 | [63] = 223, 248 | [64] = 224, 249 | [65] = 225, 250 | [66] = 226, 251 | [67] = 227, 252 | [68] = 228, 253 | [69] = 229, 254 | [70] = 230, 255 | [71] = 231, 256 | [72] = 232, 257 | [73] = 233, 258 | [74] = 234, 259 | [75] = 235, 260 | [76] = 236, 261 | [77] = 237, 262 | [78] = 238, 263 | [79] = 239, 264 | [80] = 240, 265 | [81] = 241, 266 | [82] = 242, 267 | [83] = 243, 268 | [84] = 244, 269 | [85] = 245, 270 | [86] = 246, 271 | [87] = 247, 272 | [88] = 248, 273 | [89] = 249, 274 | [90] = 250, 275 | [91] = 251, 276 | [92] = 252, 277 | [93] = 253, 278 | [94] = 254, 279 | [95] = 0, 280 | }; 281 | 282 | /* 283 | * The DEC supplemental graphics set. For its definition see here: 284 | * http://vt100.net/docs/vt220-rm/table2-3b.html 285 | * Its basically a mixture of common European symbols that are not part of 286 | * ASCII. Most often, this is mapped into GR to extend the basci ASCII part. 287 | * 288 | * This is very similar to unicode_upper, however, few symbols differ so do not 289 | * mix them up! 290 | */ 291 | tsm_vte_charset tsm_vte_dec_supplemental_graphics = { 292 | [0] = 0, 293 | [1] = 161, 294 | [2] = 162, 295 | [3] = 163, 296 | [4] = 0, 297 | [5] = 165, 298 | [6] = 0, 299 | [7] = 167, 300 | [8] = 164, 301 | [9] = 169, 302 | [10] = 170, 303 | [11] = 171, 304 | [12] = 0, 305 | [13] = 0, 306 | [14] = 0, 307 | [15] = 0, 308 | [16] = 176, 309 | [17] = 177, 310 | [18] = 178, 311 | [19] = 179, 312 | [20] = 0, 313 | [21] = 181, 314 | [22] = 182, 315 | [23] = 183, 316 | [24] = 0, 317 | [25] = 185, 318 | [26] = 186, 319 | [27] = 187, 320 | [28] = 188, 321 | [29] = 189, 322 | [30] = 0, 323 | [31] = 191, 324 | [32] = 192, 325 | [33] = 193, 326 | [34] = 194, 327 | [35] = 195, 328 | [36] = 196, 329 | [37] = 197, 330 | [38] = 198, 331 | [39] = 199, 332 | [40] = 200, 333 | [41] = 201, 334 | [42] = 202, 335 | [43] = 203, 336 | [44] = 204, 337 | [45] = 205, 338 | [46] = 206, 339 | [47] = 207, 340 | [48] = 0, 341 | [49] = 209, 342 | [50] = 210, 343 | [51] = 211, 344 | [52] = 212, 345 | [53] = 213, 346 | [54] = 214, 347 | [55] = 338, 348 | [56] = 216, 349 | [57] = 217, 350 | [58] = 218, 351 | [59] = 219, 352 | [60] = 220, 353 | [61] = 376, 354 | [62] = 0, 355 | [63] = 223, 356 | [64] = 224, 357 | [65] = 225, 358 | [66] = 226, 359 | [67] = 227, 360 | [68] = 228, 361 | [69] = 229, 362 | [70] = 230, 363 | [71] = 231, 364 | [72] = 232, 365 | [73] = 233, 366 | [74] = 234, 367 | [75] = 235, 368 | [76] = 236, 369 | [77] = 237, 370 | [78] = 238, 371 | [79] = 239, 372 | [80] = 0, 373 | [81] = 241, 374 | [82] = 242, 375 | [83] = 243, 376 | [84] = 244, 377 | [85] = 245, 378 | [86] = 246, 379 | [87] = 339, 380 | [88] = 248, 381 | [89] = 249, 382 | [90] = 250, 383 | [91] = 251, 384 | [92] = 252, 385 | [93] = 255, 386 | [94] = 0, 387 | [95] = 0, 388 | }; 389 | 390 | /* 391 | * DEC special graphics character set. See here for its definition: 392 | * http://vt100.net/docs/vt220-rm/table2-4.html 393 | * This contains several characters to create ASCII drawings and similar. Its 394 | * commonly mapped into GR to extend the basic ASCII characters. 395 | * 396 | * Lower 62 characters map to ASCII 33-64, everything beyond is special and 397 | * commonly used for ASCII drawings. It depends on the Unicode Standard 3.2 for 398 | * the extended horizontal scan-line characters 3, 5, 7, and 9. 399 | */ 400 | tsm_vte_charset tsm_vte_dec_special_graphics = { 401 | [0] = 0, 402 | [1] = 33, 403 | [2] = 34, 404 | [3] = 35, 405 | [4] = 36, 406 | [5] = 37, 407 | [6] = 38, 408 | [7] = 39, 409 | [8] = 40, 410 | [9] = 41, 411 | [10] = 42, 412 | [11] = 43, 413 | [12] = 44, 414 | [13] = 45, 415 | [14] = 46, 416 | [15] = 47, 417 | [16] = 48, 418 | [17] = 49, 419 | [18] = 50, 420 | [19] = 51, 421 | [20] = 52, 422 | [21] = 53, 423 | [22] = 54, 424 | [23] = 55, 425 | [24] = 56, 426 | [25] = 57, 427 | [26] = 58, 428 | [27] = 59, 429 | [28] = 60, 430 | [29] = 61, 431 | [30] = 62, 432 | [31] = 63, 433 | [32] = 64, 434 | [33] = 65, 435 | [34] = 66, 436 | [35] = 67, 437 | [36] = 68, 438 | [37] = 69, 439 | [38] = 70, 440 | [39] = 71, 441 | [40] = 72, 442 | [41] = 73, 443 | [42] = 74, 444 | [43] = 75, 445 | [44] = 76, 446 | [45] = 77, 447 | [46] = 78, 448 | [47] = 79, 449 | [48] = 80, 450 | [49] = 81, 451 | [50] = 82, 452 | [51] = 83, 453 | [52] = 84, 454 | [53] = 85, 455 | [54] = 86, 456 | [55] = 87, 457 | [56] = 88, 458 | [57] = 89, 459 | [58] = 90, 460 | [59] = 91, 461 | [60] = 92, 462 | [61] = 93, 463 | [62] = 94, 464 | [63] = 0, 465 | [64] = 9830, 466 | [65] = 9618, 467 | [66] = 9225, 468 | [67] = 9228, 469 | [68] = 9229, 470 | [69] = 9226, 471 | [70] = 176, 472 | [71] = 177, 473 | [72] = 9252, 474 | [73] = 9227, 475 | [74] = 9496, 476 | [75] = 9488, 477 | [76] = 9484, 478 | [77] = 9492, 479 | [78] = 9532, 480 | [79] = 9146, 481 | [80] = 9147, 482 | [81] = 9472, 483 | [82] = 9148, 484 | [83] = 9149, 485 | [84] = 9500, 486 | [85] = 9508, 487 | [86] = 9524, 488 | [87] = 9516, 489 | [88] = 9474, 490 | [89] = 8804, 491 | [90] = 8805, 492 | [91] = 960, 493 | [92] = 8800, 494 | [93] = 163, 495 | [94] = 8901, 496 | [95] = 0, 497 | }; 498 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Tests 3 | # These tests are included in a separate "test" target that can be run manually via `make test' 4 | # But a more useful way to run the tests is using ctest, which runs valgrind on all tests. 5 | # This is also the setup using in our CI. 6 | # Note that ctest fails if _any_ leak is detected by valgrind. Thus, you 7 | # need to have valgrind installed and libcheck running properly (without leaks) 8 | # to make the tests succeed. 9 | 10 | function(libtsm_add_test name) 11 | set(options) 12 | set(oneValueArgs) 13 | set(multiValueArgs LINK_LIBRARIES) 14 | cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) 15 | 16 | add_executable(${name} ${name}) 17 | if(ARG_LINK_LIBRARIES) 18 | target_link_libraries(${name} PRIVATE ${ARG_LINK_LIBRARIES}) 19 | endif() 20 | add_test(NAME ${name} COMMAND ${name}) 21 | endfunction() 22 | 23 | libtsm_add_test(test_htable 24 | LINK_LIBRARIES 25 | check::check 26 | ) 27 | target_link_object_libraries(test_htable 28 | PRIVATE 29 | shl 30 | ) 31 | 32 | libtsm_add_test(test_symbol 33 | LINK_LIBRARIES 34 | tsm_test 35 | check::check 36 | ) 37 | 38 | libtsm_add_test(test_screen 39 | LINK_LIBRARIES 40 | tsm_test 41 | check::check 42 | ) 43 | 44 | libtsm_add_test(test_selection 45 | LINK_LIBRARIES 46 | tsm_test 47 | check::check 48 | ) 49 | 50 | libtsm_add_test(test_vte 51 | LINK_LIBRARIES 52 | tsm_test 53 | check::check 54 | ) 55 | 56 | libtsm_add_test(test_vte_mouse 57 | LINK_LIBRARIES 58 | tsm_test 59 | check::check 60 | ) 61 | 62 | # This is only a quick sanity check that verifies the 63 | # valgrind tests work properly. 64 | libtsm_add_test(test_valgrind 65 | LINK_LIBRARIES 66 | check::check 67 | ) 68 | # mark the test as will fail in memcheck 69 | set_tests_properties(test_valgrind PROPERTIES LABELS memcheck-xfail) 70 | -------------------------------------------------------------------------------- /test/test_common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * TSM - Test Helper 3 | * 4 | * Copyright (c) 2012-2013 David Herrmann 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | /* 27 | * Test Helper 28 | * This header includes all kinds of helpers for testing. It tries to include 29 | * everything required and provides simple macros to avoid duplicating code in 30 | * each test. We try to keep tests as small as possible and move everything that 31 | * might be common here. 32 | * 33 | * We avoid sticking to our usual coding conventions (including headers in 34 | * source files, etc. ..) and instead make this the most convenient we can. 35 | */ 36 | 37 | #ifndef TEST_COMMON_H 38 | #define TEST_COMMON_H 39 | 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | 47 | /* lower address-space is protected from user-allocation, so this is invalid */ 48 | #define TEST_INVALID_PTR ((void*)0x10) 49 | 50 | #define UNUSED(x) (void)(x) 51 | 52 | #define TEST_DEFINE_CASE(_name) \ 53 | static TCase *test_create_case_##_name(void) \ 54 | { \ 55 | TCase *tc; \ 56 | \ 57 | tc = tcase_create(#_name); \ 58 | 59 | #define CHECKED_FIXTURE(_setup, _teardown) tcase_add_checked_fixture(tc, _setup, _teardown); 60 | #define FIXTURE(_setup, _teardown) tcase_add_fixture(tc, _setup, _teardown); 61 | #define TEST(_name) tcase_add_test(tc, _name); 62 | 63 | #define TEST_END_CASE \ 64 | return tc; \ 65 | } \ 66 | 67 | #define TEST_END NULL 68 | 69 | #define TEST_CASE(_name) test_create_case_##_name 70 | 71 | static inline Suite *test_create_suite(const char *name, ...) 72 | { 73 | Suite *s; 74 | va_list list; 75 | TCase *(*fn)(void); 76 | 77 | s = suite_create(name); 78 | 79 | va_start(list, name); 80 | while ((fn = va_arg(list, TCase *(*)(void)))) 81 | suite_add_tcase(s, fn()); 82 | va_end(list); 83 | 84 | return s; 85 | } 86 | 87 | #define TEST_SUITE(_name, ...) test_create_suite((#_name), ##__VA_ARGS__) 88 | 89 | static inline int test_run_suite(Suite *s) 90 | { 91 | int ret; 92 | SRunner *sr; 93 | 94 | sr = srunner_create(s); 95 | srunner_run_all(sr, CK_NORMAL); 96 | ret = srunner_ntests_failed(sr); 97 | srunner_free(sr); 98 | 99 | return ret; 100 | } 101 | 102 | #define TEST_DEFINE(_suite) \ 103 | int main(int argc, char **argv) \ 104 | { \ 105 | return test_run_suite(_suite); \ 106 | } 107 | 108 | #ifndef ck_assert_mem_eq 109 | #include 110 | #define ck_assert_mem_eq(_x, _y, _len) \ 111 | ck_assert(memcmp((_x), (_y), (_len)) == 0) 112 | #define ck_assert_mem_ne(_x, _y, _len) \ 113 | ck_assert(memcmp((_x), (_y), (_len)) != 0) 114 | #define ck_assert_mem_lt(_x, _y, _len) \ 115 | ck_assert(memcmp((_x), (_y), (_len)) < 0) 116 | #define ck_assert_mem_le(_x, _y, _len) \ 117 | ck_assert(memcmp((_x), (_y), (_len)) <= 0) 118 | #define ck_assert_mem_gt(_x, _y, _len) \ 119 | ck_assert(memcmp((_x), (_y), (_len)) > 0) 120 | #define ck_assert_mem_ge(_x, _y, _len) \ 121 | ck_assert(memcmp((_x), (_y), (_len)) >= 0) 122 | #endif 123 | 124 | #endif /* TEST_COMMON_H */ 125 | -------------------------------------------------------------------------------- /test/test_htable.c: -------------------------------------------------------------------------------- 1 | /* 2 | * TSM - Hashtable Tests 3 | * 4 | * Copyright (c) 2012-2013 David Herrmann 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | 27 | #include "test_common.h" 28 | #include "shl-htable.h" 29 | 30 | static struct shl_htable ht = SHL_HTABLE_INIT_STR(ht); 31 | static struct shl_htable uht = SHL_HTABLE_INIT_ULONG(uht); 32 | 33 | struct node { 34 | char huge_padding[16384]; 35 | uint8_t v; 36 | char paaaaaadding[16384]; 37 | char *key; 38 | unsigned long ul; 39 | char more_padding[32768]; 40 | size_t hash; 41 | }; 42 | 43 | #define to_node(_key) shl_htable_offsetof((_key), struct node, key) 44 | #define ul_to_node(_key) shl_htable_offsetof((_key), struct node, ul) 45 | 46 | static struct node o[] = { 47 | { .v = 0, .key = "o0", .ul = 0 }, 48 | { .v = 1, .key = "o1", .ul = 1 }, 49 | { .v = 2, .key = "o2", .ul = 2 }, 50 | { .v = 3, .key = "o3", .ul = 3 }, 51 | { .v = 4, .key = "o4", .ul = 4 }, 52 | { .v = 5, .key = "o5", .ul = 5 }, 53 | { .v = 6, .key = "o6", .ul = 6 }, 54 | { .v = 7, .key = "o7", .ul = 7 }, 55 | }; 56 | 57 | static void test_htable_str_cb(char **k, void *ctx) 58 | { 59 | int *num = ctx; 60 | 61 | ck_assert(to_node(k)->v == to_node(k)->ul); 62 | ++*num; 63 | } 64 | 65 | START_TEST(test_htable_str) 66 | { 67 | int r, i, num; 68 | char **k; 69 | bool b; 70 | 71 | /* insert once, remove once, try removing again */ 72 | 73 | ck_assert(!o[0].hash); 74 | r = shl_htable_insert_str(&ht, &o[0].key, &o[0].hash); 75 | ck_assert(!r); 76 | ck_assert(o[0].hash == shl_htable_rehash_str(&o[0].key, NULL)); 77 | 78 | b = shl_htable_remove_str(&ht, o[0].key, &o[0].hash, &k); 79 | ck_assert(b); 80 | ck_assert(k != NULL); 81 | ck_assert(to_node(k)->v == 0); 82 | 83 | k = NULL; 84 | b = shl_htable_remove_str(&ht, o[0].key, &o[0].hash, &k); 85 | ck_assert(!b); 86 | ck_assert(k == NULL); 87 | 88 | /* insert twice, remove twice, try removing again */ 89 | 90 | r = shl_htable_insert_str(&ht, &o[0].key, &o[0].hash); 91 | ck_assert(!r); 92 | ck_assert(o[0].hash == shl_htable_rehash_str(&o[0].key, NULL)); 93 | 94 | r = shl_htable_insert_str(&ht, &o[0].key, &o[0].hash); 95 | ck_assert(!r); 96 | ck_assert(o[0].hash == shl_htable_rehash_str(&o[0].key, NULL)); 97 | 98 | b = shl_htable_remove_str(&ht, o[0].key, &o[0].hash, &k); 99 | ck_assert(b); 100 | ck_assert(k != NULL); 101 | ck_assert(to_node(k)->v == 0); 102 | 103 | b = shl_htable_remove_str(&ht, o[0].key, &o[0].hash, &k); 104 | ck_assert(b); 105 | ck_assert(k != NULL); 106 | ck_assert(to_node(k)->v == 0); 107 | 108 | k = NULL; 109 | b = shl_htable_remove_str(&ht, o[0].key, &o[0].hash, &k); 110 | ck_assert(!b); 111 | ck_assert(k == NULL); 112 | 113 | /* same as before but without hash-cache */ 114 | 115 | r = shl_htable_insert_str(&ht, &o[0].key, NULL); 116 | ck_assert(!r); 117 | 118 | r = shl_htable_insert_str(&ht, &o[0].key, NULL); 119 | ck_assert(!r); 120 | 121 | b = shl_htable_remove_str(&ht, o[0].key, NULL, &k); 122 | ck_assert(b); 123 | ck_assert(k != NULL); 124 | ck_assert(to_node(k)->v == 0); 125 | 126 | b = shl_htable_remove_str(&ht, o[0].key, NULL, &k); 127 | ck_assert(b); 128 | ck_assert(k != NULL); 129 | ck_assert(to_node(k)->v == 0); 130 | 131 | k = NULL; 132 | b = shl_htable_remove_str(&ht, o[0].key, NULL, &k); 133 | ck_assert(!b); 134 | ck_assert(k == NULL); 135 | 136 | /* insert all elements and verify empty hash-caches */ 137 | 138 | o[0].hash = 0; 139 | for (i = 0; i < 8; ++i) { 140 | ck_assert(!o[i].hash); 141 | r = shl_htable_insert_str(&ht, &o[i].key, &o[i].hash); 142 | ck_assert(!r); 143 | ck_assert(o[i].hash == shl_htable_rehash_str(&o[i].key, NULL)); 144 | } 145 | 146 | /* verify */ 147 | 148 | for (i = 0; i < 8; ++i) { 149 | k = NULL; 150 | b = shl_htable_lookup_str(&ht, o[i].key, NULL, &k); 151 | ck_assert(b); 152 | ck_assert(k != NULL); 153 | ck_assert(to_node(k)->v == i); 154 | } 155 | 156 | /* remove all elements again */ 157 | 158 | for (i = 0; i < 8; ++i) { 159 | b = shl_htable_remove_str(&ht, o[i].key, NULL, &k); 160 | ck_assert(b); 161 | ck_assert(k != NULL); 162 | ck_assert(to_node(k)->v == i); 163 | } 164 | 165 | /* verify they're gone */ 166 | 167 | for (i = 0; i < 8; ++i) { 168 | k = NULL; 169 | b = shl_htable_remove_str(&ht, o[i].key, NULL, &k); 170 | ck_assert(!b); 171 | ck_assert(k == NULL); 172 | } 173 | 174 | for (i = 0; i < 8; ++i) { 175 | k = NULL; 176 | b = shl_htable_lookup_str(&ht, o[i].key, NULL, &k); 177 | ck_assert(!b); 178 | ck_assert(k == NULL); 179 | } 180 | 181 | num = 0; 182 | shl_htable_visit_str(&ht, test_htable_str_cb, &num); 183 | ck_assert(num == 0); 184 | 185 | num = 0; 186 | shl_htable_clear_str(&ht, test_htable_str_cb, &num); 187 | ck_assert(num == 0); 188 | 189 | /* test shl_htable_clear_str() */ 190 | 191 | for (i = 0; i < 8; ++i) { 192 | r = shl_htable_insert_str(&ht, &o[i].key, &o[i].hash); 193 | ck_assert(!r); 194 | } 195 | 196 | num = 0; 197 | shl_htable_visit_str(&ht, test_htable_str_cb, &num); 198 | ck_assert(num == 8); 199 | 200 | num = 0; 201 | shl_htable_clear_str(&ht, test_htable_str_cb, &num); 202 | ck_assert(num == 8); 203 | } 204 | END_TEST 205 | 206 | static void test_htable_ulong_cb(unsigned long *k, void *ctx) 207 | { 208 | int *num = ctx; 209 | 210 | ck_assert(ul_to_node(k)->v == ul_to_node(k)->ul); 211 | ++*num; 212 | } 213 | 214 | START_TEST(test_htable_ulong) 215 | { 216 | int r, i, num; 217 | unsigned long *k; 218 | bool b; 219 | 220 | /* insert once, remove once, try removing again */ 221 | 222 | r = shl_htable_insert_ulong(&uht, &o[0].ul); 223 | ck_assert(!r); 224 | ck_assert(o[0].ul == shl_htable_rehash_ulong(&o[0].ul, NULL)); 225 | 226 | b = shl_htable_remove_ulong(&uht, o[0].ul, &k); 227 | ck_assert(b); 228 | ck_assert(k != NULL); 229 | ck_assert(ul_to_node(k)->v == 0); 230 | 231 | k = NULL; 232 | b = shl_htable_remove_ulong(&uht, o[0].ul, &k); 233 | ck_assert(!b); 234 | ck_assert(k == NULL); 235 | 236 | /* insert all */ 237 | 238 | for (i = 0; i < 8; ++i) { 239 | r = shl_htable_insert_ulong(&uht, &o[i].ul); 240 | ck_assert(!r); 241 | } 242 | 243 | /* verify */ 244 | 245 | for (i = 0; i < 8; ++i) { 246 | k = NULL; 247 | b = shl_htable_lookup_ulong(&uht, o[i].ul, &k); 248 | ck_assert(b); 249 | ck_assert(k != NULL); 250 | } 251 | 252 | /* remove all elements again */ 253 | 254 | for (i = 0; i < 8; ++i) { 255 | b = shl_htable_remove_ulong(&uht, o[i].ul, &k); 256 | ck_assert(b); 257 | ck_assert(k != NULL); 258 | ck_assert(ul_to_node(k)->v == i); 259 | } 260 | 261 | /* verify they're gone */ 262 | 263 | for (i = 0; i < 8; ++i) { 264 | k = NULL; 265 | b = shl_htable_remove_ulong(&uht, o[i].ul, &k); 266 | ck_assert(!b); 267 | ck_assert(k == NULL); 268 | } 269 | 270 | for (i = 0; i < 8; ++i) { 271 | k = NULL; 272 | b = shl_htable_lookup_ulong(&uht, o[i].ul, &k); 273 | ck_assert(!b); 274 | ck_assert(k == NULL); 275 | } 276 | 277 | num = 0; 278 | shl_htable_visit_ulong(&uht, test_htable_ulong_cb, &num); 279 | ck_assert(num == 0); 280 | 281 | num = 0; 282 | shl_htable_clear_ulong(&uht, test_htable_ulong_cb, &num); 283 | ck_assert(num == 0); 284 | 285 | /* test shl_htable_clear_ulong() */ 286 | 287 | for (i = 0; i < 8; ++i) { 288 | r = shl_htable_insert_ulong(&uht, &o[i].ul); 289 | ck_assert(!r); 290 | } 291 | 292 | num = 0; 293 | shl_htable_visit_ulong(&uht, test_htable_ulong_cb, &num); 294 | ck_assert(num == 8); 295 | 296 | num = 0; 297 | shl_htable_clear_ulong(&uht, test_htable_ulong_cb, &num); 298 | ck_assert(num == 8); 299 | } 300 | END_TEST 301 | 302 | TEST_DEFINE_CASE(misc) 303 | TEST(test_htable_str) 304 | TEST(test_htable_ulong) 305 | TEST_END_CASE 306 | 307 | TEST_DEFINE( 308 | TEST_SUITE(hashtable, 309 | TEST_CASE(misc), 310 | TEST_END 311 | ) 312 | ) 313 | -------------------------------------------------------------------------------- /test/test_screen.c: -------------------------------------------------------------------------------- 1 | /* 2 | * TSM - Screen Management Tests 3 | * 4 | * Copyright (c) 2018 Aetf 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | #include "test_common.h" 27 | #include "libtsm.h" 28 | #include "libtsm-int.h" 29 | 30 | START_TEST(test_screen_init) 31 | { 32 | struct tsm_screen *screen; 33 | int r; 34 | 35 | r = tsm_screen_new(&screen, NULL, NULL); 36 | ck_assert_int_eq(r, 0); 37 | 38 | tsm_screen_unref(screen); 39 | screen = NULL; 40 | } 41 | END_TEST 42 | 43 | START_TEST(test_screen_null) 44 | { 45 | int r; 46 | unsigned int n; 47 | 48 | r = tsm_screen_new(NULL, NULL, NULL); 49 | ck_assert_int_eq(r, -EINVAL); 50 | 51 | tsm_screen_ref(NULL); 52 | tsm_screen_unref(NULL); 53 | 54 | tsm_screen_set_opts(NULL, 0u); 55 | tsm_screen_reset_opts(NULL, 0u); 56 | 57 | n = tsm_screen_get_opts(NULL); 58 | ck_assert_int_eq(n, 0); 59 | 60 | n = tsm_screen_get_width(NULL); 61 | ck_assert_int_eq(n, 0); 62 | 63 | n = tsm_screen_get_height(NULL); 64 | ck_assert_int_eq(n, 0); 65 | 66 | r = tsm_screen_resize(NULL, 0, 0); 67 | ck_assert_int_eq(r, -EINVAL); 68 | 69 | r = tsm_screen_set_margins(NULL, 0u, 0u); 70 | ck_assert_int_eq(r, -EINVAL); 71 | 72 | tsm_screen_set_max_sb(NULL, 0u); 73 | 74 | tsm_screen_clear_sb(NULL); 75 | 76 | tsm_screen_sb_up(NULL, 0u); 77 | tsm_screen_sb_down(NULL, 0u); 78 | tsm_screen_sb_page_up(NULL, 0u); 79 | tsm_screen_sb_page_down(NULL, 0u); 80 | tsm_screen_sb_reset(NULL); 81 | 82 | tsm_screen_set_def_attr(NULL, NULL); 83 | 84 | tsm_screen_reset(NULL); 85 | 86 | tsm_screen_set_flags(NULL, 0u); 87 | tsm_screen_reset_flags(NULL, 0u); 88 | 89 | n = tsm_screen_get_flags(NULL); 90 | ck_assert_int_eq(n, 0); 91 | 92 | n = tsm_screen_get_cursor_x(NULL); 93 | ck_assert_int_eq(n, 0); 94 | 95 | n = tsm_screen_get_cursor_y(NULL); 96 | ck_assert_int_eq(n, 0); 97 | 98 | tsm_screen_set_tabstop(NULL); 99 | tsm_screen_reset_tabstop(NULL); 100 | tsm_screen_reset_all_tabstops(NULL); 101 | 102 | tsm_screen_write(NULL, 0u, NULL); 103 | 104 | tsm_screen_newline(NULL); 105 | 106 | tsm_screen_scroll_up(NULL, 0u); 107 | tsm_screen_scroll_down(NULL, 0u); 108 | 109 | tsm_screen_move_to(NULL, 0u, 0u); 110 | tsm_screen_move_up(NULL, 0u, false); 111 | tsm_screen_move_down(NULL, 0u, false); 112 | tsm_screen_move_right(NULL, 0u); 113 | tsm_screen_move_left(NULL, 0u); 114 | tsm_screen_move_line_end(NULL); 115 | tsm_screen_move_line_home(NULL); 116 | 117 | tsm_screen_tab_right(NULL, 0u); 118 | tsm_screen_tab_left(NULL, 0u); 119 | 120 | tsm_screen_insert_lines(NULL, 0u); 121 | tsm_screen_delete_lines(NULL, 0u); 122 | tsm_screen_insert_chars(NULL, 0u); 123 | tsm_screen_delete_chars(NULL, 0u); 124 | 125 | tsm_screen_erase_cursor(NULL); 126 | tsm_screen_erase_chars(NULL, 0u); 127 | tsm_screen_erase_cursor_to_end(NULL, false); 128 | tsm_screen_erase_home_to_cursor(NULL, false); 129 | tsm_screen_erase_current_line(NULL, false); 130 | tsm_screen_erase_screen_to_cursor(NULL, false); 131 | tsm_screen_erase_cursor_to_screen(NULL, false); 132 | tsm_screen_erase_screen(NULL, false); 133 | } 134 | END_TEST 135 | 136 | START_TEST(test_screen_resize_alt_colors) 137 | { 138 | struct tsm_screen *screen; 139 | struct line *line; 140 | int r, y, x; 141 | struct tsm_screen_attr *attr; 142 | struct tsm_screen_attr new_attr; 143 | 144 | r = tsm_screen_new(&screen, NULL, NULL); 145 | ck_assert_int_eq(r, 0); 146 | 147 | /* start with an initial 2x2 screen */ 148 | r = tsm_screen_resize(screen, 2, 2); 149 | ck_assert_int_eq(r, 0); 150 | 151 | /* switch to alternate screen */ 152 | tsm_screen_set_flags(screen, TSM_SCREEN_ALTERNATE); 153 | 154 | /* change background color to red */ 155 | bzero(&new_attr, sizeof(new_attr)); 156 | new_attr.br = 255; 157 | new_attr.bg = 0; 158 | new_attr.bb = 0; 159 | 160 | tsm_screen_set_def_attr(screen, &new_attr); 161 | tsm_screen_erase_screen(screen, false); 162 | 163 | /* now all cells should be red */ 164 | for (y = 0; y < screen->size_y; y++) { 165 | line = screen->lines[y]; 166 | for (x = 0; x < screen->size_x; x++) { 167 | attr = &line->cells[x].attr; 168 | ck_assert_int_eq(attr->br, 255); 169 | ck_assert_int_eq(attr->bg, 0); 170 | ck_assert_int_eq(attr->bb, 0); 171 | } 172 | } 173 | 174 | /* enlarge to 4x4 while on alternate screen */ 175 | r = tsm_screen_resize(screen, 4, 4); 176 | ck_assert_int_eq(r, 0); 177 | 178 | /* leave alternate screen */ 179 | tsm_screen_reset_flags(screen, TSM_SCREEN_ALTERNATE); 180 | 181 | /* now all cells should be black (including the new ones) */ 182 | for (y = 0; y < screen->size_y; y++) { 183 | line = screen->lines[y]; 184 | for (x = 0; x < screen->size_x; x++) { 185 | attr = &line->cells[x].attr; 186 | ck_assert_int_eq(attr->br, 0); 187 | ck_assert_int_eq(attr->bg, 0); 188 | ck_assert_int_eq(attr->bb, 0); 189 | } 190 | } 191 | 192 | tsm_screen_unref(screen); 193 | screen = NULL; 194 | } 195 | END_TEST 196 | 197 | START_TEST(test_screen_sb_get_line_pos) 198 | { 199 | struct tsm_screen *screen; 200 | int r; 201 | 202 | r = tsm_screen_new(&screen, NULL, NULL); 203 | ck_assert_int_eq(r, 0); 204 | 205 | r = tsm_screen_resize(screen, 5, 5); 206 | ck_assert_int_eq(r, 0); 207 | 208 | tsm_screen_set_max_sb(screen, 5); 209 | 210 | ck_assert_int_eq(tsm_screen_sb_get_line_count(screen), 0); 211 | ck_assert_int_eq(tsm_screen_sb_get_line_pos(screen), 0); 212 | 213 | /* fill up screen */ 214 | tsm_screen_newline(screen); 215 | tsm_screen_newline(screen); 216 | tsm_screen_newline(screen); 217 | tsm_screen_newline(screen); 218 | 219 | /* create first entry in scrollback buffer */ 220 | tsm_screen_newline(screen); 221 | 222 | ck_assert_int_eq(tsm_screen_sb_get_line_count(screen), 1); 223 | ck_assert_int_eq(tsm_screen_sb_get_line_pos(screen), 1); 224 | 225 | tsm_screen_newline(screen); 226 | 227 | ck_assert_int_eq(tsm_screen_sb_get_line_count(screen), 2); 228 | ck_assert_int_eq(tsm_screen_sb_get_line_pos(screen), 2); 229 | 230 | tsm_screen_newline(screen); 231 | 232 | ck_assert_int_eq(tsm_screen_sb_get_line_count(screen), 3); 233 | ck_assert_int_eq(tsm_screen_sb_get_line_pos(screen), 3); 234 | 235 | /* scroll up by one (third line of sb) */ 236 | tsm_screen_sb_up(screen, 1); 237 | ck_assert_int_eq(tsm_screen_sb_get_line_count(screen), 3); 238 | ck_assert_int_eq(tsm_screen_sb_get_line_pos(screen), 2); 239 | 240 | /* scroll up by one (second line of sb) */ 241 | tsm_screen_sb_up(screen, 1); 242 | ck_assert_int_eq(tsm_screen_sb_get_line_count(screen), 3); 243 | ck_assert_int_eq(tsm_screen_sb_get_line_pos(screen), 1); 244 | 245 | /* scroll up by one (first line of sb) */ 246 | tsm_screen_sb_up(screen, 1); 247 | ck_assert_int_eq(tsm_screen_sb_get_line_count(screen), 3); 248 | ck_assert_int_eq(tsm_screen_sb_get_line_pos(screen), 0); 249 | 250 | /* scroll up beyond top end of buffer */ 251 | tsm_screen_sb_up(screen, 1); 252 | ck_assert_int_eq(tsm_screen_sb_get_line_count(screen), 3); 253 | ck_assert_int_eq(tsm_screen_sb_get_line_pos(screen), 0); 254 | 255 | /* scroll out of scrollback buffer */ 256 | tsm_screen_sb_down(screen, 3); 257 | ck_assert_int_eq(tsm_screen_sb_get_line_count(screen), 3); 258 | ck_assert_int_eq(tsm_screen_sb_get_line_pos(screen), 3); 259 | 260 | tsm_screen_unref(screen); 261 | screen = NULL; 262 | } 263 | END_TEST 264 | 265 | 266 | TEST_DEFINE_CASE(misc) 267 | TEST(test_screen_init) 268 | TEST(test_screen_null) 269 | TEST(test_screen_resize_alt_colors) 270 | TEST(test_screen_sb_get_line_pos) 271 | TEST_END_CASE 272 | 273 | TEST_DEFINE( 274 | TEST_SUITE(screen, 275 | TEST_CASE(misc), 276 | TEST_END 277 | ) 278 | ) 279 | -------------------------------------------------------------------------------- /test/test_symbol.c: -------------------------------------------------------------------------------- 1 | /* 2 | * TSM - Symbol Table Tests 3 | * 4 | * Copyright (c) 2012-2013 David Herrmann 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | #include "test_common.h" 27 | #include "libtsm.h" 28 | #include "libtsm-int.h" 29 | 30 | START_TEST(test_symbol_null) 31 | { 32 | int r; 33 | tsm_symbol_t sym = 0, s; 34 | const tsm_symbol_t *sp; 35 | unsigned int n; 36 | 37 | r = tsm_symbol_table_new(NULL); 38 | ck_assert(r == -EINVAL); 39 | 40 | tsm_symbol_table_ref(NULL); 41 | tsm_symbol_table_unref(NULL); 42 | 43 | sp = tsm_symbol_get(NULL, &sym, NULL); 44 | ck_assert(sp == &sym); 45 | 46 | s = tsm_symbol_append(NULL, sym, 'a'); 47 | ck_assert(s == sym); 48 | 49 | n = tsm_symbol_get_width(NULL, sym); 50 | ck_assert(!n); 51 | } 52 | END_TEST 53 | 54 | START_TEST(test_symbol_init) 55 | { 56 | struct tsm_symbol_table *t; 57 | int r; 58 | 59 | r = tsm_symbol_table_new(&t); 60 | ck_assert(!r); 61 | 62 | tsm_symbol_table_unref(t); 63 | t = NULL; 64 | } 65 | END_TEST 66 | 67 | TEST_DEFINE_CASE(misc) 68 | TEST(test_symbol_null) 69 | TEST(test_symbol_init) 70 | TEST_END_CASE 71 | 72 | TEST_DEFINE( 73 | TEST_SUITE(symbol, 74 | TEST_CASE(misc), 75 | TEST_END 76 | ) 77 | ) 78 | -------------------------------------------------------------------------------- /test/test_valgrind.c: -------------------------------------------------------------------------------- 1 | /* 2 | * TSM - Valgrind Verification 3 | * 4 | * Copyright (c) 2012-2013 David Herrmann 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | /* Dummy which just leaks memory. Used to verify valgrind memcheck. */ 27 | 28 | #include "test_common.h" 29 | 30 | START_TEST(test_valgrind) 31 | { 32 | void *p; 33 | 34 | p = malloc(0x100); 35 | ck_assert_ptr_ne(p, NULL); 36 | } 37 | END_TEST 38 | 39 | TEST_DEFINE_CASE(misc) 40 | TEST(test_valgrind) 41 | TEST_END_CASE 42 | 43 | TEST_DEFINE( 44 | TEST_SUITE(valgrind, 45 | TEST_CASE(misc), 46 | TEST_END 47 | ) 48 | ) 49 | -------------------------------------------------------------------------------- /test/test_vte.c: -------------------------------------------------------------------------------- 1 | /* 2 | * TSM - VTE State Machine Tests 3 | * 4 | * Copyright (c) 2018 Aetf 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | #include "libtsm-int.h" 27 | #include "libtsm.h" 28 | #include "test_common.h" 29 | 30 | #include 31 | #include 32 | 33 | static void log_cb(void *data, const char *file, int line, const char *func, const char *subs, 34 | unsigned int sev, const char *format, va_list args) 35 | { 36 | UNUSED(data); 37 | UNUSED(file); 38 | UNUSED(line); 39 | UNUSED(func); 40 | UNUSED(subs); 41 | UNUSED(sev); 42 | UNUSED(format); 43 | UNUSED(args); 44 | } 45 | 46 | static void write_cb(struct tsm_vte *vte, const char *u8, size_t len, void *data) 47 | { 48 | UNUSED(len); 49 | UNUSED(data); 50 | 51 | ck_assert_ptr_ne(vte, NULL); 52 | ck_assert_ptr_ne(u8, NULL); 53 | } 54 | 55 | START_TEST(test_vte_init) 56 | { 57 | struct tsm_screen *screen; 58 | struct tsm_vte *vte; 59 | int r; 60 | 61 | r = tsm_screen_new(&screen, log_cb, NULL); 62 | ck_assert_int_eq(r, 0); 63 | 64 | r = tsm_vte_new(&vte, screen, write_cb, NULL, log_cb, NULL); 65 | ck_assert_int_eq(r, 0); 66 | 67 | tsm_vte_unref(vte); 68 | vte = NULL; 69 | 70 | tsm_screen_unref(screen); 71 | screen = NULL; 72 | } 73 | END_TEST 74 | 75 | START_TEST(test_vte_null) 76 | { 77 | int r; 78 | bool rb; 79 | 80 | r = tsm_vte_new(NULL, NULL, NULL, NULL, log_cb, NULL); 81 | ck_assert_int_eq(r, -EINVAL); 82 | 83 | tsm_vte_ref(NULL); 84 | tsm_vte_unref(NULL); 85 | 86 | tsm_vte_set_osc_cb(NULL, NULL, NULL); 87 | 88 | r = tsm_vte_set_palette(NULL, ""); 89 | ck_assert_int_eq(r, -EINVAL); 90 | 91 | r = tsm_vte_set_custom_palette(NULL, NULL); 92 | ck_assert_int_eq(r, -EINVAL); 93 | 94 | tsm_vte_get_def_attr(NULL, NULL); 95 | tsm_vte_get_flags(NULL); 96 | 97 | tsm_vte_get_mouse_mode(NULL); 98 | tsm_vte_get_mouse_event(NULL); 99 | 100 | tsm_vte_reset(NULL); 101 | tsm_vte_hard_reset(NULL); 102 | tsm_vte_input(NULL, "", 0); 103 | 104 | rb = tsm_vte_handle_keyboard(NULL, 0, 0, 0, 0); 105 | ck_assert(!rb); 106 | } 107 | END_TEST 108 | 109 | static uint8_t test_palette[TSM_COLOR_NUM][3] = { 110 | [TSM_COLOR_BLACK] = { 0, 18, 36 }, 111 | [TSM_COLOR_RED] = { 1, 19, 37 }, 112 | [TSM_COLOR_GREEN] = { 2, 20, 38 }, 113 | [TSM_COLOR_YELLOW] = { 3, 21, 39 }, 114 | [TSM_COLOR_BLUE] = { 4, 22, 40 }, 115 | [TSM_COLOR_MAGENTA] = { 5, 23, 41 }, 116 | [TSM_COLOR_CYAN] = { 6, 24, 42 }, 117 | [TSM_COLOR_LIGHT_GREY] = { 7, 25, 43 }, 118 | [TSM_COLOR_DARK_GREY] = { 8, 26, 44 }, 119 | [TSM_COLOR_LIGHT_RED] = { 9, 27, 45 }, 120 | [TSM_COLOR_LIGHT_GREEN] = { 10, 28, 46 }, 121 | [TSM_COLOR_LIGHT_YELLOW] = { 11, 29, 47 }, 122 | [TSM_COLOR_LIGHT_BLUE] = { 12, 30, 48 }, 123 | [TSM_COLOR_LIGHT_MAGENTA] = { 13, 31, 49 }, 124 | [TSM_COLOR_LIGHT_CYAN] = { 14, 32, 50 }, 125 | [TSM_COLOR_WHITE] = { 15, 33, 51 }, 126 | 127 | [TSM_COLOR_FOREGROUND] = { 16, 34, 52 }, 128 | [TSM_COLOR_BACKGROUND] = { 17, 35, 53 }, 129 | }; 130 | 131 | START_TEST(test_vte_custom_palette) 132 | { 133 | struct tsm_screen *screen; 134 | struct tsm_vte *vte; 135 | int r; 136 | 137 | r = tsm_screen_new(&screen, log_cb, NULL); 138 | ck_assert_int_eq(r, 0); 139 | 140 | r = tsm_vte_new(&vte, screen, write_cb, NULL, log_cb, NULL); 141 | ck_assert_int_eq(r, 0); 142 | 143 | r = tsm_vte_set_custom_palette(vte, test_palette); 144 | ck_assert_int_eq(r, 0); 145 | 146 | r = tsm_vte_set_palette(vte, "custom"); 147 | ck_assert_int_eq(r, 0); 148 | 149 | struct tsm_screen_attr attr; 150 | tsm_vte_get_def_attr(vte, &attr); 151 | ck_assert_uint_eq(attr.fr, test_palette[TSM_COLOR_FOREGROUND][0]); 152 | ck_assert_uint_eq(attr.fg, test_palette[TSM_COLOR_FOREGROUND][1]); 153 | ck_assert_uint_eq(attr.fb, test_palette[TSM_COLOR_FOREGROUND][2]); 154 | 155 | ck_assert_uint_eq(attr.br, test_palette[TSM_COLOR_BACKGROUND][0]); 156 | ck_assert_uint_eq(attr.bg, test_palette[TSM_COLOR_BACKGROUND][1]); 157 | ck_assert_uint_eq(attr.bb, test_palette[TSM_COLOR_BACKGROUND][2]); 158 | 159 | tsm_vte_unref(vte); 160 | vte = NULL; 161 | 162 | tsm_screen_unref(screen); 163 | screen = NULL; 164 | } 165 | END_TEST 166 | 167 | static void checking_write_cb(struct tsm_vte *vte, const char *u8, size_t len, void *data) 168 | { 169 | ck_assert_ptr_ne(vte, NULL); 170 | ck_assert_ptr_ne(u8, NULL); 171 | ck_assert_uint_gt(len, 0); 172 | 173 | ck_assert_mem_eq(u8, data, len); 174 | } 175 | 176 | START_TEST(test_vte_backspace_key) 177 | { 178 | struct tsm_screen *screen; 179 | struct tsm_vte *vte; 180 | char expected_output; 181 | int r; 182 | 183 | r = tsm_screen_new(&screen, log_cb, NULL); 184 | ck_assert_int_eq(r, 0); 185 | 186 | r = tsm_vte_new(&vte, screen, checking_write_cb, &expected_output, log_cb, NULL); 187 | ck_assert_int_eq(r, 0); 188 | 189 | expected_output = '\010'; 190 | r = tsm_vte_handle_keyboard(vte, XKB_KEY_BackSpace, 010, 0, 010); 191 | ck_assert(r); 192 | r = tsm_vte_handle_keyboard(vte, XKB_KEY_BackSpace, 0177, 0, 0177); 193 | ck_assert(r); 194 | 195 | tsm_vte_set_backspace_sends_delete(vte, true); 196 | 197 | expected_output = '\177'; 198 | r = tsm_vte_handle_keyboard(vte, XKB_KEY_BackSpace, 010, 0, 010); 199 | ck_assert(r); 200 | r = tsm_vte_handle_keyboard(vte, XKB_KEY_BackSpace, 0177, 0, 0177); 201 | ck_assert(r); 202 | 203 | tsm_vte_set_backspace_sends_delete(vte, false); 204 | 205 | expected_output = '\010'; 206 | r = tsm_vte_handle_keyboard(vte, XKB_KEY_BackSpace, 010, 0, 010); 207 | ck_assert(r); 208 | r = tsm_vte_handle_keyboard(vte, XKB_KEY_BackSpace, 0177, 0, 0177); 209 | ck_assert(r); 210 | 211 | tsm_vte_unref(vte); 212 | vte = NULL; 213 | 214 | tsm_screen_unref(screen); 215 | screen = NULL; 216 | } 217 | END_TEST 218 | 219 | START_TEST(test_vte_get_flags) 220 | { 221 | struct tsm_screen *screen; 222 | struct tsm_vte *vte; 223 | char expected_output; 224 | int r, flags; 225 | 226 | r = tsm_screen_new(&screen, log_cb, NULL); 227 | ck_assert_int_eq(r, 0); 228 | 229 | r = tsm_vte_new(&vte, screen, checking_write_cb, &expected_output, log_cb, NULL); 230 | ck_assert_int_eq(r, 0); 231 | 232 | flags = tsm_vte_get_flags(vte); 233 | ck_assert(!(flags & TSM_VTE_FLAG_CURSOR_KEY_MODE)); 234 | 235 | // enable cursor key mode 236 | tsm_vte_input(vte, "\033[?1h", 5); 237 | 238 | flags = tsm_vte_get_flags(vte); 239 | ck_assert(flags & TSM_VTE_FLAG_CURSOR_KEY_MODE); 240 | 241 | tsm_vte_unref(vte); 242 | vte = NULL; 243 | 244 | tsm_screen_unref(screen); 245 | screen = NULL; 246 | } 247 | END_TEST 248 | 249 | /* Regression test for https://github.com/Aetf/libtsm/issues/26 */ 250 | START_TEST(test_vte_decrqm_no_reset) 251 | { 252 | struct tsm_screen *screen; 253 | struct tsm_vte *vte; 254 | int r; 255 | unsigned int flags; 256 | 257 | r = tsm_screen_new(&screen, log_cb, NULL); 258 | ck_assert_int_eq(r, 0); 259 | 260 | r = tsm_vte_new(&vte, screen, write_cb, NULL, log_cb, NULL); 261 | ck_assert_int_eq(r, 0); 262 | 263 | /* switch terminal to alternate screen mode */ 264 | tsm_vte_input(vte, "\033[?1049h", 8); 265 | 266 | flags = tsm_screen_get_flags(screen); 267 | ck_assert(flags & TSM_SCREEN_ALTERNATE); 268 | 269 | /* send DECRQM SRM (12) request */ 270 | tsm_vte_input(vte, "\033[?12$p", 7); 271 | 272 | /* terminal should still be in alternate screen mode */ 273 | flags = tsm_screen_get_flags(screen); 274 | ck_assert(flags & TSM_SCREEN_ALTERNATE); 275 | 276 | tsm_vte_unref(vte); 277 | vte = NULL; 278 | 279 | tsm_screen_unref(screen); 280 | screen = NULL; 281 | } 282 | END_TEST 283 | 284 | #define assert_tsm_screen_cursor_pos(screen, x1, y1) \ 285 | do { \ 286 | ck_assert_int_eq(x1, tsm_screen_get_cursor_x(screen)); \ 287 | ck_assert_int_eq(y1, tsm_screen_get_cursor_y(screen)); \ 288 | } while(0) 289 | 290 | /* test for https://github.com/Aetf/kmscon/issues/78 */ 291 | START_TEST(test_vte_csi_cursor_up_down) 292 | { 293 | struct tsm_screen *screen; 294 | struct tsm_vte *vte; 295 | int r; 296 | int h, w; 297 | char csi_cmd[64]; 298 | 299 | r = tsm_screen_new(&screen, log_cb, NULL); 300 | ck_assert_int_eq(r, 0); 301 | 302 | r = tsm_vte_new(&vte, screen, write_cb, NULL, log_cb, NULL); 303 | ck_assert_int_eq(r, 0); 304 | 305 | tsm_vte_input(vte, "\n123", 4); 306 | assert_tsm_screen_cursor_pos(screen, 3, 1); 307 | 308 | // cursor move up, first col 309 | tsm_vte_input(vte, "\033[1F", 4); 310 | assert_tsm_screen_cursor_pos(screen, 0, 0); 311 | 312 | // cursor move down, first col 313 | tsm_vte_input(vte, "\033[1E", 4); 314 | assert_tsm_screen_cursor_pos(screen, 0, 1); 315 | 316 | h = tsm_screen_get_height(screen); 317 | w = tsm_screen_get_width(screen); 318 | 319 | // move cursor up out of screen, should at first line 320 | sprintf(csi_cmd, "\033[%dF", h + 10); 321 | tsm_vte_input(vte, csi_cmd, strlen(csi_cmd)); 322 | assert_tsm_screen_cursor_pos(screen, 0, 0); 323 | 324 | // move cursor down out of screen, should at last line 325 | sprintf(csi_cmd, "\033[%dE", h + 10); 326 | tsm_vte_input(vte, csi_cmd, strlen(csi_cmd)); 327 | assert_tsm_screen_cursor_pos(screen, 0, h - 1); 328 | 329 | tsm_vte_unref(vte); 330 | vte = NULL; 331 | 332 | tsm_screen_unref(screen); 333 | screen = NULL; 334 | } 335 | END_TEST 336 | 337 | TEST_DEFINE_CASE(misc) 338 | TEST(test_vte_init) 339 | TEST(test_vte_null) 340 | TEST(test_vte_custom_palette) 341 | TEST(test_vte_backspace_key) 342 | TEST(test_vte_get_flags) 343 | TEST(test_vte_decrqm_no_reset) 344 | TEST(test_vte_csi_cursor_up_down) 345 | TEST_END_CASE 346 | 347 | // clang-format off 348 | TEST_DEFINE( 349 | TEST_SUITE(vte, 350 | TEST_CASE(misc), 351 | TEST_END 352 | ) 353 | ) 354 | // clang-format on 355 | -------------------------------------------------------------------------------- /test/test_vte_mouse.c: -------------------------------------------------------------------------------- 1 | /* 2 | * TSM - VTE State Machine Tests 3 | * 4 | * Copyright (c) 2022 Andreas Heck 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | #include "test_common.h" 27 | #include "libtsm.h" 28 | #include "libtsm-int.h" 29 | 30 | char write_buffer[512]; 31 | 32 | bool mouse_cb_called = false; 33 | unsigned int mouse_track_mode = 0; 34 | bool mouse_track_pixels = false; 35 | 36 | static void write_cb(struct tsm_vte *vte, const char *u8, size_t len, void *data) 37 | { 38 | ck_assert_ptr_ne(vte, NULL); 39 | ck_assert_ptr_ne(u8, NULL); 40 | 41 | memcpy(&write_buffer, u8, len); 42 | write_buffer[len] = '\0'; 43 | } 44 | 45 | static void mouse_cb(struct tsm_vte *vte, enum tsm_mouse_track_mode track_mode, bool track_pixels, void *data) 46 | { 47 | mouse_cb_called = true; 48 | mouse_track_mode = track_mode; 49 | mouse_track_pixels = track_pixels; 50 | } 51 | 52 | struct tsm_screen *screen; 53 | struct tsm_vte *vte; 54 | 55 | void setup() 56 | { 57 | int r; 58 | 59 | r = tsm_screen_new(&screen, NULL, NULL); 60 | ck_assert_int_eq(r, 0); 61 | 62 | r = tsm_vte_new(&vte, screen, write_cb, NULL, NULL, NULL); 63 | ck_assert_int_eq(r, 0); 64 | 65 | tsm_vte_set_mouse_cb(vte, mouse_cb, NULL); 66 | 67 | mouse_cb_called = false; 68 | mouse_track_mode = 0; 69 | mouse_track_pixels = false; 70 | 71 | bzero(&write_buffer, sizeof(write_buffer)); 72 | } 73 | 74 | void teardown() 75 | { 76 | tsm_vte_unref(vte); 77 | vte = NULL; 78 | 79 | tsm_screen_unref(screen); 80 | screen = NULL; 81 | } 82 | 83 | START_TEST(test_mouse_cb_x10) 84 | { 85 | char *msg; 86 | 87 | /* Set X10 Mode */ 88 | msg = "\e[?9h"; 89 | tsm_vte_input(vte, msg, strlen(msg)); 90 | 91 | ck_assert(mouse_cb_called); 92 | ck_assert_int_eq(mouse_track_mode, TSM_MOUSE_TRACK_BTN); 93 | ck_assert(!mouse_track_pixels); 94 | } 95 | END_TEST 96 | 97 | START_TEST(test_mouse_x10) 98 | { 99 | char *msg; 100 | char *expected; 101 | int r; 102 | 103 | /* Set X10 Mode */ 104 | msg = "\e[?9h"; 105 | tsm_vte_input(vte, msg, strlen(msg)); 106 | 107 | /* left click on left upper cell (0, 0) should be translated to (1, 1) in output */ 108 | tsm_vte_handle_mouse(vte, 0, 0, 0, 0, 0, TSM_MOUSE_EVENT_PRESSED, 0); 109 | expected = "\e[M !!"; 110 | r = memcmp(&write_buffer, expected, strlen(expected)); 111 | ck_assert_int_eq(r, 0); 112 | 113 | /* right click on (0, 0) */ 114 | bzero(&write_buffer, sizeof(write_buffer)); 115 | tsm_vte_handle_mouse(vte, 0, 0, 0, 0, 2, TSM_MOUSE_EVENT_PRESSED, 0); 116 | expected = "\e[M\"!!"; 117 | r = memcmp(&write_buffer, expected, strlen(expected)); 118 | ck_assert_int_eq(r, 0); 119 | 120 | /* middle click on (0, 0) */ 121 | bzero(&write_buffer, sizeof(write_buffer)); 122 | tsm_vte_handle_mouse(vte, 0, 0, 0, 0, 1, TSM_MOUSE_EVENT_PRESSED, 0); 123 | expected = "\e[M!!!"; 124 | r = memcmp(&write_buffer, expected, strlen(expected)); 125 | ck_assert_int_eq(r, 0); 126 | 127 | /* left click out of range (299, 279) */ 128 | bzero(&write_buffer, sizeof(write_buffer)); 129 | tsm_vte_handle_mouse(vte, 299, 279, 0, 0, 0, TSM_MOUSE_EVENT_PRESSED, 0); 130 | expected = "\e[M \xff\xff"; 131 | r = memcmp(&write_buffer, expected, strlen(expected)); 132 | ck_assert_int_eq(r, 0); 133 | } 134 | END_TEST 135 | 136 | START_TEST(test_mouse_cb_sgr) 137 | { 138 | char *msg; 139 | 140 | /* Set SGR Mode */ 141 | msg = "\e[?1006h"; 142 | tsm_vte_input(vte, msg, strlen(msg)); 143 | 144 | /* mouse_cb should not be called if event type is not set */ 145 | ck_assert(!mouse_cb_called); 146 | 147 | /* reset for next test */ 148 | mouse_cb_called = false; 149 | mouse_track_mode = 0; 150 | mouse_track_pixels = false; 151 | 152 | /* Set Button Events */ 153 | bzero(&write_buffer, sizeof(write_buffer)); 154 | msg = "\e[?1002h"; 155 | tsm_vte_input(vte, msg, strlen(msg)); 156 | 157 | ck_assert(mouse_cb_called); 158 | ck_assert_int_eq(mouse_track_mode, TSM_MOUSE_TRACK_BTN); 159 | ck_assert(!mouse_track_pixels); 160 | } 161 | END_TEST 162 | 163 | START_TEST(test_mouse_sgr) 164 | { 165 | char *expected; 166 | char *msg; 167 | int r; 168 | 169 | /* Set SGR Mode and only notify for mouse button clicks */ 170 | msg = "\e[?1006h\e[?1002h"; 171 | tsm_vte_input(vte, msg, strlen(msg)); 172 | 173 | /* left click on left upper cell (0, 0) should be translated to (1, 1) in output */ 174 | tsm_vte_handle_mouse(vte, 0, 0, 0, 0, 0, TSM_MOUSE_EVENT_PRESSED, 0); 175 | expected = "\e[<0;1;1M"; 176 | r = memcmp(&write_buffer, expected, strlen(expected)); 177 | ck_assert_int_eq(r, 0); 178 | 179 | /* button release event for (1, 1) */ 180 | bzero(&write_buffer, sizeof(write_buffer)); 181 | tsm_vte_handle_mouse(vte, 0, 0, 0, 0, 0, TSM_MOUSE_EVENT_RELEASED, 0); 182 | expected = "\e[<0;1;1m"; 183 | r = memcmp(&write_buffer, expected, strlen(expected)); 184 | ck_assert_int_eq(r, 0); 185 | 186 | /* button 1 (middle mouse button) */ 187 | bzero(&write_buffer, sizeof(write_buffer)); 188 | tsm_vte_handle_mouse(vte, 0, 0, 0, 0, 1, TSM_MOUSE_EVENT_PRESSED, 0); 189 | expected = "\e[<1;1;1M"; 190 | r = memcmp(&write_buffer, expected, strlen(expected)); 191 | ck_assert_int_eq(r, 0); 192 | 193 | /* button 2 (right mouse button) */ 194 | bzero(&write_buffer, sizeof(write_buffer)); 195 | tsm_vte_handle_mouse(vte, 0, 0, 0, 0, 2, TSM_MOUSE_EVENT_PRESSED, 0); 196 | expected = "\e[<2;1;1M"; 197 | r = memcmp(&write_buffer, expected, strlen(expected)); 198 | ck_assert_int_eq(r, 0); 199 | 200 | /* button 4 (mouse wheel up scroll) */ 201 | bzero(&write_buffer, sizeof(write_buffer)); 202 | tsm_vte_handle_mouse(vte, 0, 0, 0, 0, 4, TSM_MOUSE_EVENT_PRESSED, 0); 203 | expected = "\e[<64;1;1M"; 204 | r = memcmp(&write_buffer, expected, strlen(expected)); 205 | ck_assert_int_eq(r, 0); 206 | 207 | /* button 5 (mouse wheel down scroll) */ 208 | bzero(&write_buffer, sizeof(write_buffer)); 209 | tsm_vte_handle_mouse(vte, 0, 0, 0, 0, 5, TSM_MOUSE_EVENT_PRESSED, 0); 210 | expected = "\e[<65;1;1M"; 211 | r = memcmp(&write_buffer, expected, strlen(expected)); 212 | ck_assert_int_eq(r, 0); 213 | 214 | /* check for (50, 120) */ 215 | bzero(&write_buffer, sizeof(write_buffer)); 216 | tsm_vte_handle_mouse(vte, 49, 119, 0, 0, 0, TSM_MOUSE_EVENT_PRESSED, 0); 217 | expected = "\e[<0;50;120M"; 218 | r = memcmp(&write_buffer, expected, strlen(expected)); 219 | ck_assert_int_eq(r, 0); 220 | } 221 | END_TEST 222 | 223 | START_TEST(test_mouse_sgr_cell_change) 224 | { 225 | char *msg; 226 | char *expected; 227 | int r; 228 | 229 | /* Set SGR Mode and only notify for mouse button clicks */ 230 | msg = "\e[?1006h\e[?1003h"; 231 | tsm_vte_input(vte, msg, strlen(msg)); 232 | 233 | /* move over (0, 0) */ 234 | tsm_vte_handle_mouse(vte, 0, 0, 0, 0, 0, TSM_MOUSE_EVENT_MOVED, 0); 235 | expected = "\e[<35;1;1M"; 236 | r = memcmp(&write_buffer, expected, strlen(expected)); 237 | ck_assert_int_eq(r, 0); 238 | 239 | /* repeated reportings of the same cell should be ignored */ 240 | bzero(&write_buffer, sizeof(write_buffer)); 241 | tsm_vte_handle_mouse(vte, 0, 0, 0, 0, 0, TSM_MOUSE_EVENT_MOVED, 0); 242 | ck_assert_int_eq(write_buffer[0], 0); 243 | 244 | /* different cells must be reported */ 245 | bzero(&write_buffer, sizeof(write_buffer)); 246 | tsm_vte_handle_mouse(vte, 1, 1, 0, 0, 0, TSM_MOUSE_EVENT_MOVED, 0); 247 | expected = "\e[<35;2;2M"; 248 | r = memcmp(&write_buffer, expected, strlen(expected)); 249 | ck_assert_int_eq(r, 0); 250 | 251 | /* a click must be reported in all cases */ 252 | bzero(&write_buffer, sizeof(write_buffer)); 253 | tsm_vte_handle_mouse(vte, 1, 1, 0, 0, 0, TSM_MOUSE_EVENT_PRESSED, 0); 254 | expected = "\e[<0;2;2M"; 255 | r = memcmp(&write_buffer, expected, strlen(expected)); 256 | ck_assert_int_eq(r, 0); 257 | } 258 | END_TEST 259 | 260 | START_TEST(test_mouse_cb_pixels) 261 | { 262 | char *msg; 263 | 264 | /* Set Pixel Mode */ 265 | msg = "\e[?1016h"; 266 | tsm_vte_input(vte, msg, strlen(msg)); 267 | 268 | /* mouse_cb should not be called if event type is not set */ 269 | ck_assert(!mouse_cb_called); 270 | 271 | /* reset for next test */ 272 | mouse_cb_called = false; 273 | mouse_track_mode = 0; 274 | mouse_track_pixels = false; 275 | 276 | /* Set Button Events */ 277 | bzero(&write_buffer, sizeof(write_buffer)); 278 | msg = "\e[?1003h"; 279 | tsm_vte_input(vte, msg, strlen(msg)); 280 | 281 | ck_assert(mouse_cb_called); 282 | ck_assert_int_eq(mouse_track_mode, TSM_MOUSE_TRACK_ANY); 283 | ck_assert(mouse_track_pixels); 284 | } 285 | END_TEST 286 | 287 | START_TEST(test_mouse_pixels) 288 | { 289 | char *msg; 290 | char *expected; 291 | int r; 292 | 293 | /* Set SGR Mode and only notify for mouse button clicks */ 294 | msg = "\e[?1016h\e[?1003h"; 295 | tsm_vte_input(vte, msg, strlen(msg)); 296 | 297 | tsm_vte_handle_mouse(vte, 0, 0, 236, 120, 0, TSM_MOUSE_EVENT_MOVED, 0); 298 | expected = "\e[<35;236;120M"; 299 | r = memcmp(&write_buffer, expected, strlen(expected)); 300 | ck_assert_int_eq(r, 0); 301 | 302 | bzero(&write_buffer, sizeof(write_buffer)); 303 | tsm_vte_handle_mouse(vte, 0, 0, 236, 120, 0, TSM_MOUSE_EVENT_PRESSED, 0); 304 | expected = "\e[<0;236;120M"; 305 | r = memcmp(&write_buffer, expected, strlen(expected)); 306 | ck_assert_int_eq(r, 0); 307 | 308 | bzero(&write_buffer, sizeof(write_buffer)); 309 | tsm_vte_handle_mouse(vte, 0, 0, 236, 120, 0, TSM_MOUSE_EVENT_RELEASED, 0); 310 | expected = "\e[<0;236;120m"; 311 | r = memcmp(&write_buffer, expected, strlen(expected)); 312 | ck_assert_int_eq(r, 0); 313 | } 314 | END_TEST 315 | 316 | TEST_DEFINE_CASE(tests_x10) 317 | CHECKED_FIXTURE(setup, teardown) 318 | TEST(test_mouse_cb_x10) 319 | TEST(test_mouse_x10) 320 | TEST_END_CASE 321 | 322 | TEST_DEFINE_CASE(tests_sgr) 323 | CHECKED_FIXTURE(setup, teardown) 324 | TEST(test_mouse_cb_sgr) 325 | TEST(test_mouse_sgr) 326 | TEST(test_mouse_sgr_cell_change) 327 | TEST_END_CASE 328 | 329 | TEST_DEFINE_CASE(tests_sgr_pixels) 330 | CHECKED_FIXTURE(setup, teardown) 331 | TEST(test_mouse_cb_pixels) 332 | TEST(test_mouse_pixels) 333 | TEST_END_CASE 334 | 335 | // clang-format off 336 | TEST_DEFINE( 337 | TEST_SUITE(vte_mouse, 338 | TEST_CASE(tests_x10), 339 | TEST_CASE(tests_sgr), 340 | TEST_CASE(tests_sgr_pixels), 341 | TEST_END 342 | ) 343 | ) 344 | // clang-format on 345 | --------------------------------------------------------------------------------