├── .clang-format ├── .gitattributes ├── .gitignore ├── .gitmodules ├── .travis.yml ├── AUTHORS.md ├── CMakeLists.txt ├── CPackConfig.cmake ├── LICENSE ├── README.md ├── README.source.md ├── cmake ├── Modules │ ├── FindLibCxx.cmake │ ├── FindXsettingsClient.cmake │ ├── Pandoc.cmake │ └── TestTarget.cmake └── package.cmake ├── data ├── default_icon.png ├── logo │ ├── tint3-128.png │ ├── tint3-16.png │ ├── tint3-256.png │ ├── tint3-32.png │ ├── tint3-48.png │ ├── tint3-64.png │ └── tint3.svg └── tint3.desktop ├── debian └── conffiles ├── doc ├── CMakeLists.txt ├── tint3.1.md └── tint3rc.5.md ├── external_includes ├── catch.hpp └── json.hpp ├── hooks └── pre-commit ├── sample ├── icon_and_text_1.tint3rc ├── icon_and_text_2.tint3rc ├── icon_and_text_3.tint3rc ├── icon_and_text_4.tint3rc ├── icon_only_1.tint3rc ├── icon_only_2.tint3rc ├── icon_only_3.tint3rc ├── icon_only_4.tint3rc ├── icon_only_6.tint3rc ├── icon_only_7.tint3rc ├── text_only_1.tint3rc ├── text_only_2.tint3rc ├── text_only_3.tint3rc ├── text_only_4.tint3rc ├── text_only_5.tint3rc ├── text_only_6.tint3rc └── tint3rc ├── src ├── CMakeLists.txt ├── battery │ ├── CMakeLists.txt │ ├── battery.cc │ ├── battery.hh │ ├── battery_interface.hh │ ├── freebsd_acpiio.cc │ ├── freebsd_acpiio.hh │ ├── linux_sysfs.cc │ └── linux_sysfs.hh ├── behavior_control.hh ├── clock │ ├── CMakeLists.txt │ ├── clock.cc │ ├── clock.hh │ └── clock_test.cc ├── config.cc ├── config.hh ├── config_test.cc ├── cxx_features.hh.in ├── dnd │ ├── CMakeLists.txt │ ├── dnd.cc │ ├── dnd.hh │ └── dnd_test.cc ├── execp │ ├── CMakeLists.txt │ ├── execp.cc │ ├── execp.hh │ └── execp_test.cc ├── launcher │ ├── CMakeLists.txt │ ├── desktop_entry.cc │ ├── desktop_entry.hh │ ├── desktop_entry_test.cc │ ├── launcher.cc │ ├── launcher.hh │ ├── launcher_test.cc │ └── testdata │ │ ├── .icons │ │ └── UnitTestTheme │ │ │ └── index.theme │ │ └── applications │ │ └── launcher_test.desktop ├── panel.cc ├── panel.hh ├── panel_test.cc ├── parser │ ├── CMakeLists.txt │ ├── lexer.cc │ ├── lexer.hh │ ├── lexer_test.cc │ ├── parser.cc │ ├── parser.hh │ └── parser_test.cc ├── server.cc ├── server.hh ├── startup_notification.cc ├── startup_notification.hh ├── startup_notification_test.cc ├── subprocess.cc ├── subprocess.hh ├── subprocess_test.cc ├── systray │ ├── CMakeLists.txt │ ├── systraybar.cc │ ├── systraybar.hh │ ├── tray_window.cc │ └── tray_window.hh ├── taskbar │ ├── CMakeLists.txt │ ├── task.cc │ ├── task.hh │ ├── taskbar.cc │ ├── taskbar.hh │ ├── taskbarbase.cc │ ├── taskbarbase.hh │ ├── taskbarname.cc │ └── taskbarname.hh ├── theme_manager.cc ├── theme_manager.hh ├── theme_manager_test.cc ├── tint.cc ├── tooltip │ ├── CMakeLists.txt │ ├── tooltip.cc │ ├── tooltip.hh │ └── tooltip_test.cc ├── unix_features.hh.in ├── util │ ├── CMakeLists.txt │ ├── area.cc │ ├── area.hh │ ├── area_test.cc │ ├── bimap.hh │ ├── bimap_test.cc │ ├── collection.hh │ ├── collection_test.cc │ ├── color.cc │ ├── color.hh │ ├── color_test.cc │ ├── common.cc │ ├── common.hh │ ├── common_test.cc │ ├── environment.cc │ ├── environment.hh │ ├── environment_test.cc │ ├── fs.cc │ ├── fs.hh │ ├── fs_test.cc │ ├── fs_test_utils.cc │ ├── fs_test_utils.hh │ ├── geometry.cc │ ├── geometry.hh │ ├── geometry_test.cc │ ├── gradient.cc │ ├── gradient.hh │ ├── gradient_test.cc │ ├── imlib2.cc │ ├── imlib2.hh │ ├── imlib2_test.cc │ ├── log.cc │ ├── log.hh │ ├── pango.cc │ ├── pango.hh │ ├── pango_test.cc │ ├── pipe.cc │ ├── pipe.hh │ ├── pipe_test.cc │ ├── testdata │ │ └── fs_test.txt │ ├── timer.cc │ ├── timer.hh │ ├── timer_test.cc │ ├── timer_test_utils.cc │ ├── timer_test_utils.hh │ ├── window.cc │ ├── window.hh │ ├── x11.cc │ ├── x11.hh │ ├── xdg.cc │ ├── xdg.hh │ └── xdg_test.cc └── version.hh.in └── test ├── CMakeLists.txt ├── Xnest.sh ├── main.cc ├── suppressions └── lsan.txt └── xvfb-run.sh /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: Google 3 | Language: Cpp 4 | Standard: Cpp11 5 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.cc text 3 | *.h text 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Don't include the build directories in the repository 2 | /build 3 | /build-* 4 | /bazel-* 5 | /package-build 6 | /scan-build 7 | /_CPack_Packages 8 | 9 | # Don't include ctags in the repository 10 | /tags 11 | 12 | # Don't include IDE- or editor-specific files in the repository 13 | *~ 14 | *.kdev4 15 | /.kdev4 16 | /.vscode 17 | /_vimrc_local.vim 18 | 19 | # Don't include built packages 20 | *.deb 21 | *.rpm 22 | 23 | # Don't include gh-pages build artifacts 24 | /Gemfile.lock 25 | /.bundle 26 | /.jekyll-metadata 27 | /_site 28 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "cmake/coveralls-cmake"] 2 | path = cmake/coveralls-cmake 3 | url = https://github.com/JoakimSoderberg/coveralls-cmake.git 4 | [submodule "abseil-cpp"] 5 | path = abseil-cpp 6 | url = https://github.com/abseil/abseil-cpp 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: bionic 3 | 4 | language: cpp 5 | 6 | compiler: 7 | - gcc 8 | 9 | os: linux 10 | addons: 11 | apt: 12 | packages: 13 | # Required libraries 14 | - libcairo2-dev 15 | - libpango1.0-dev 16 | - libglib2.0-dev 17 | - libimlib2-dev 18 | - libxinerama-dev 19 | - libx11-dev 20 | - libxdamage-dev 21 | - libxcomposite-dev 22 | - libxrender-dev 23 | - libxrandr-dev 24 | - libxsettings-client-dev 25 | - libxsettings-dev 26 | - librsvg2-dev 27 | - libstartup-notification0-dev 28 | - libcurl4-openssl-dev 29 | # For more detailed AddressSanitizer reports 30 | - binutils 31 | # For running some tests 32 | - xvfb 33 | - xauth 34 | # For Coveralls integration 35 | - lcov 36 | - curl 37 | # For documentation build 38 | - pandoc 39 | 40 | before_install: 41 | # cpp-coveralls 42 | - pip install --user cpp-coveralls 43 | 44 | before_script: 45 | - mkdir "${TRAVIS_BUILD_DIR}/build" 46 | - cd "${TRAVIS_BUILD_DIR}/build" 47 | - cmake "${TRAVIS_BUILD_DIR}" -DCMAKE_BUILD_TYPE=Debug -DCOVERALLS=ON 48 | 49 | script: 50 | # Verify tint3 builds and passes tests 51 | - cd "${TRAVIS_BUILD_DIR}/build" 52 | - make all test 53 | 54 | # Verify the installation script works, then clean up 55 | - sudo make install DESTDIR=/opt/tint3 && sudo rm -rf /opt/tint3 56 | 57 | after_success: 58 | # Upload coverage data to Coveralls 59 | - >- 60 | coveralls 61 | --root "${TRAVIS_BUILD_DIR}" 62 | --build-root "${TRAVIS_BUILD_DIR}/build" 63 | --exclude build/abseil-cpp 64 | --exclude build/generated 65 | --exclude build/CMakeFiles 66 | --exclude abseil-cpp 67 | --exclude external_includes 68 | --exclude test 69 | --exclude-pattern '.*_test\.cc$' 70 | --exclude-lines-pattern '.*_test\.cc$' 71 | --gcov-options '\-lp' 72 | 73 | notifications: 74 | irc: 75 | channels: 76 | - "chat.freenode.net#tint3" 77 | on_success: change 78 | on_failure: always 79 | use_notice: true 80 | -------------------------------------------------------------------------------- /AUTHORS.md: -------------------------------------------------------------------------------- 1 | # Current contributors 2 | 3 | - Daniele Cocca 4 | 5 | # Former contributors 6 | ## tint2 7 | 8 | The tint3 project is derived from tint2 (originally hosted at 9 | [Google Code](https://code.google.com/p/tint2/), currently hosted at 10 | [GitLab](https://gitlab.com/o9000/tint2)), developed by: 11 | 12 | - Thierry Lorthiois from Omega distribution 13 | - Andreas Fink 14 | - Euan Freeman (tintwizard) 15 | - Christian Ruppert (autotools build system) 16 | - Ovidiu M (launcher, bug fixes) 17 | 18 | With contributions from: 19 | 20 | - Kwaku Yeboah (wiki page) 21 | - Daniel Moerner (man page and Debian package) 22 | - Doug Barton (FreeBSD package) 23 | - James Buren (Frugalware package) 24 | - Pierre-Emmanuel Andre (OpenBSD port) 25 | - Redroar (Arch Linux package) 26 | - Rene Garcia (launcher SVG support) 27 | - Marcelo Vianna (taskbar sorting) 28 | - Xico Atelo (startup notifications) 29 | - Craig Oakes (WM flags, issue tracker organization) 30 | 31 | ## ttm 32 | In turn, tint2 was originally based on the [ttm](http://code.google.com/p/ttm/) 33 | source code, developed by: 34 | 35 | - Pål Staurland 36 | -------------------------------------------------------------------------------- /CPackConfig.cmake: -------------------------------------------------------------------------------- 1 | # CPack configuration file for tint3. 2 | # Based on compton's CPackConfig.cmake (https://github.com/chjj/compton). 3 | 4 | # Workaround for: https://gitlab.kitware.com/cmake/cmake/issues/16517 5 | if(${CMAKE_VERSION} VERSION_LESS "3.8.0") 6 | set(CPACK_COMPONENTS_ALL "tint3") 7 | set(CPACK_DEB_COMPONENT_INSTALL ON) 8 | set(CPACK_COMPONENTS_GROUPING ALL_COMPONENTS_IN_ONE) 9 | endif() 10 | 11 | # Environment 12 | if(NOT CPACK_SYSTEM_NAME) 13 | set(CPACK_SYSTEM_NAME "${CMAKE_SYSTEM_PROCESSOR}") 14 | if(CPACK_SYSTEM_NAME STREQUAL "x86_64") 15 | set(CPACK_SYSTEM_NAME "amd64") 16 | endif() 17 | endif() 18 | 19 | # Build and install information 20 | set(CPACK_INSTALL_CMAKE_PROJECTS "package-build;tint3;ALL;/") 21 | set(CPACK_PROJECT_CONFIG_FILE "cmake/package.cmake") 22 | set(CPACK_CMAKE_GENERATOR "Unix Makefiles") 23 | 24 | # Basic information 25 | set(CPACK_PACKAGE_NAME "tint3") 26 | set(CPACK_PACKAGE_VENDOR "Daniele Cocca") 27 | set(CPACK_PACKAGE_VERSION_MAJOR "0") 28 | set(CPACK_PACKAGE_VERSION_MINOR "3") 29 | set(CPACK_PACKAGE_VERSION_PATCH "0") 30 | set(CPACK_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}") 31 | set(CPACK_PACKAGE_DESCRIPTION "Lightweight X11 panel, taskbar and system tray. A backwards-compatible fork and C++ rewrite of tint2.") 32 | set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Lightweight X11 panel") 33 | set(CPACK_PACKAGE_CONTACT "daniele.cocca@gmail.com") 34 | 35 | # Package config 36 | set(CPACK_GENERATOR "DEB;RPM") 37 | set(CPACK_RESOURCE_FILE_LICENSE "LICENSE") 38 | set(CPACK_RESOURCE_FILE_README "README.md") 39 | set(CPACK_STRIP_FILES 1) 40 | 41 | # DEB package config 42 | set(CPACK_DEBIAN_PACKAGE_MAINTAINER "${CPACK_PACKAGE_VENDOR} <${CPACK_PACKAGE_CONTACT}>") 43 | set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "${CPACK_SYSTEM_NAME}") 44 | set(CPACK_DEBIAN_PACKAGE_SECTION "x11") 45 | set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6, libcairo2, libpango1.0-0, libglib2.0-0, libimlib2, libxinerama1, libx11-6, libxdamage1, libxcomposite1, libxrender1, libxrandr2, libxsettings-client0, libxsettings0, librsvg2-2, libstartup-notification0, libc++1") 46 | set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "debian/conffiles") 47 | set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "${CPACK_PACKAGE_DESCRIPTION_SUMMARY} 48 | tint3 is a simple panel/taskbar for X11. It provides features such as a 49 | taskbar, a battery applet, a clock applet, a system tray, drag and drop 50 | support, application launchers, and more. 51 | . 52 | The project was born as a C++ rewrite of tint2, and aims to be 53 | backwards-compatible with it.") 54 | 55 | # RPM package config 56 | set(CPACK_RPM_PACKAGE_ARCHITECTURE "${CPACK_SYSTEM_NAME}") 57 | set(CPACK_RPM_PACKAGE_RELEASE "1") 58 | set(CPACK_RPM_PACKAGE_LICENSE "GPLv2") 59 | set(CPACK_RPM_PACKAGE_REQUIRES "/bin/sh,libcairo.so.0,libpangocairo-1.0.so.0,,libpango-1.0.so.0,libgobject-2.0.so.0,libglib-2.0.so.0,libImlib2.so.1,libXinerama.so.1,libX11.so.6,libXdamage.so.1,libXcomposite.so.1,libXrender.so.1,libXrandr.so.2,libXsettings-client.so.0,libXsettings.so,librsvg-2.so.2,libstartup-notification-1.so.0,libc++.so.1") 60 | 61 | # Source package config 62 | set(CPACK_SOURCE_GENERATOR "DEB;RPM") 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![build Status](https://travis-ci.org/jmc-88/tint3.svg?branch=master)](https://travis-ci.org/jmc-88/tint3) 2 | [![CodeFactor](https://www.codefactor.io/repository/github/jmc-88/tint3/badge)](https://www.codefactor.io/repository/github/jmc-88/tint3) 3 | [![coverage status](https://coveralls.io/repos/github/jmc-88/tint3/badge.svg?branch=master)](https://coveralls.io/github/jmc-88/tint3?branch=master) 4 | [![GitHub release](https://img.shields.io/github/release/jmc-88/tint3.svg)](https://github.com/jmc-88/tint3/releases) 5 | [![license](https://img.shields.io/github/license/jmc-88/tint3.svg)](https://github.com/jmc-88/tint3/blob/master/LICENSE) 6 | 7 | # tint3 panel 8 | 9 | This project aims to continue the development of tint2, port it to C++, make it safer against crashes, and have it use XCB instead of Xlib. 10 | 11 | ## How is this different from tint2? 12 | 13 | It shouldn't be much different from tint2 from a user's point of view at this stage. The focus is mostly on having a cleaner, more robust codebase. 14 | 15 | ## How stable is it? 16 | 17 | Pretty stable. I use it daily and I haven't seen issues in a while, but if you happen to find any, please [file a bug](https://github.com/jmc-88/tint3/issues). 18 | 19 | ## Is it packaged for my Linux distribution? 20 | 21 | * AMD64 `.deb` and `.rpm` packages are available at 22 | [jmc-88.github.io/tint3](https://jmc-88.github.io/tint3/). 23 | * There's a user-contributed package for Arch Linux in AUR, 24 | [tint3-cpp-git](https://aur.archlinux.org/packages/tint3-cpp-git). 25 | * You can easily rebuild your own `.deb` or `.rpm` package through 26 | [CPack](https://cmake.org/Wiki/CMake:Packaging_With_CPack). Check the build 27 | instructions at [README.source.md](README.source.md) for more info. 28 | * Other distributions: please build and install it manually. 29 | 30 | ## How do I build it? 31 | 32 | Please refer to [README.source.md](README.source.md). 33 | -------------------------------------------------------------------------------- /README.source.md: -------------------------------------------------------------------------------- 1 | # Git hooks 2 | 3 | Git doesn't allow for automatically enabled hook scripts. 4 | Because of this, you'll need to manually do the following: 5 | 6 | - remove the default `.git/hooks`, which only contains example scripts: 7 | ```rm -rf .git/hooks``` 8 | - symlink the `hooks` directory in: 9 | ```ln -s ../hooks .git/hooks``` 10 | 11 | The `pre-commit` script uses 12 | [clang-format](http://clang.llvm.org/docs/ClangFormat.html), so make sure you 13 | have that installed. 14 | 15 | > Note: the `pre-commit` script runs `clang-format` over the entire file and 16 | > adds it back to the Git index -- this breaks a partial file staging workflow 17 | > (e.g., `git add -p`). 18 | 19 | # Dependencies 20 | 21 | - cairo (with X support) 22 | - imlib2 (with X support) 23 | - pango 24 | - glib2 25 | - libX11 26 | - libXinerama 27 | - libXrandr 28 | - libXrender 29 | - libXcomposite 30 | - libXdamage 31 | - libXsettings-client 32 | - xvfb (needed for running some tests) 33 | - xauth (needed for running some tests) 34 | - startup-notification (optional) 35 | - libc++ (optional, recommended) 36 | - libcurl (optional, recommended, needed for theme manager operations) 37 | - pandoc (optional, recommended, needed for building documentation) 38 | 39 | ## Debian/Ubuntu systems 40 | 41 | You can install all needed compile-time dependencies with the following command: 42 | 43 | ``` 44 | $ sudo apt-get install libcairo2-dev libpango1.0-dev libglib2.0-dev \ 45 | libimlib2-dev libxinerama-dev libx11-dev libxdamage-dev \ 46 | libxcomposite-dev libxrender-dev libxrandr-dev \ 47 | libxsettings-client-dev libxsettings-dev \ 48 | librsvg2-dev libstartup-notification0-dev libc++-dev \ 49 | libcurl4-openssl-dev pandoc xvfb xauth 50 | ``` 51 | 52 | Note that the above includes `libcurl4-openssl-dev`, but any of 53 | `libcurl4-gnutls-dev`, `libcurl4-nss-dev`, or `libcurl4-openssl-dev` would do. 54 | 55 | You can now build a Debian package as follows: 56 | 57 | ``` 58 | $ mkdir package-build && cd package-build 59 | $ cmake -DCMAKE_BUILD_TYPE=Release ../ 60 | $ cd ../ && cpack -G DEB 61 | ``` 62 | 63 | ## Red Hat systems 64 | 65 | You can now build a Red Hat package as follows: 66 | 67 | ``` 68 | $ mkdir package-build && cd package-build 69 | $ cmake -DCMAKE_BUILD_TYPE=Release ../ 70 | $ cd ../ && cpack -G RPM 71 | ``` 72 | -------------------------------------------------------------------------------- /cmake/Modules/FindXsettingsClient.cmake: -------------------------------------------------------------------------------- 1 | # Check that the xsettings-client package is available on the system. 2 | # 3 | # Exports: 4 | # XSETTINGS_CLIENT_FOUND - Tells if the package was found on the system. 5 | # XSETTINGS_CLIENT_LIBRARIES - If the package was found, this is a list of 6 | # compiler flags to link to the relevant libraries. 7 | 8 | include(FindPkgConfig) 9 | pkg_check_modules(XSETTINGS_CLIENT QUIET libxsettings-client) 10 | 11 | # Very basic fallback for the case where pkg-config doesn't find the package. 12 | # This could happen even if the xsettings-client library is in fact available, 13 | # as some systems (e.g. Arch Linux) don't ship with libxsettings-client.pc. 14 | # See also: https://github.com/jmc-88/tint3/issues/40 15 | if(NOT XSETTINGS_CLIENT_FOUND) 16 | mark_as_advanced(FORCE CLEAR XSETTINGS_CLIENT_FOUND) 17 | mark_as_advanced(FORCE CLEAR XSETTINGS_CLIENT_LIBRARIES) 18 | 19 | find_library( 20 | XSETTINGS_CLIENT_LIBRARIES 21 | NAMES Xsettings-client) 22 | if(NOT XSETTINGS_CLIENT_LIBRARIES) 23 | message(FATAL_ERROR "libxsettings-client couldn't be found") 24 | endif() 25 | 26 | # If we arrived here successfully, then all checks have succeeded and we 27 | # assume the library was found. 28 | set(XSETTINGS_CLIENT_FOUND TRUE) 29 | endif() -------------------------------------------------------------------------------- /cmake/Modules/Pandoc.cmake: -------------------------------------------------------------------------------- 1 | include(CMakeParseArguments) 2 | option(TINT3_ENABLE_PANDOC "Whether to use Pandoc to generate man pages" ON) 3 | option(TINT3_ENABLE_GZIP_MAN_PAGES "Whether to use Gzip to compress the generated man pages" ON) 4 | 5 | if(TINT3_ENABLE_PANDOC) 6 | if(NOT EXISTS ${PANDOC_EXECUTABLE}) 7 | find_program(PANDOC_EXECUTABLE pandoc) 8 | mark_as_advanced(PANDOC_EXECUTABLE) 9 | if(NOT EXISTS ${PANDOC_EXECUTABLE}) 10 | message(FATAL_ERROR "Pandoc not found. Install Pandoc (http://pandoc.org/) or set cache variable PANDOC_EXECUTABLE.") 11 | return() 12 | endif() 13 | endif() 14 | endif() 15 | 16 | if(TINT3_ENABLE_GZIP_MAN_PAGES) 17 | if(NOT EXISTS ${GZIP_EXECUTABLE}) 18 | find_program(GZIP_EXECUTABLE gzip) 19 | mark_as_advanced(GZIP_EXECUTABLE) 20 | if(NOT EXISTS ${GZIP_EXECUTABLE}) 21 | message(FATAL_ERROR "Gzip not found. Install Gzip (https://www.gnu.org/software/gzip/) or set cache variable GZIP_EXECUTABLE.") 22 | return() 23 | endif() 24 | endif() 25 | endif() 26 | 27 | function(add_pandoc_man_page target_name) 28 | set(oneValueArgs INPUT OUTPUT DESTINATION) 29 | cmake_parse_arguments(ADD_PANDOC_MAN_PAGE "" "${oneValueArgs}" "" ${ARGN}) 30 | 31 | if(TINT3_ENABLE_PANDOC) 32 | add_custom_command( 33 | OUTPUT 34 | "${CMAKE_CURRENT_BINARY_DIR}/${ADD_PANDOC_MAN_PAGE_OUTPUT}" 35 | COMMAND 36 | ${PANDOC_EXECUTABLE} 37 | --standalone --from=markdown --to=man 38 | --output="${ADD_PANDOC_MAN_PAGE_OUTPUT}" 39 | "${CMAKE_CURRENT_SOURCE_DIR}/${ADD_PANDOC_MAN_PAGE_INPUT}" 40 | DEPENDS 41 | "${CMAKE_CURRENT_SOURCE_DIR}/${ADD_PANDOC_MAN_PAGE_INPUT}") 42 | 43 | if(TINT3_ENABLE_GZIP_MAN_PAGES) 44 | set(MAN_PAGE_INSTALL_TARGET 45 | "${CMAKE_CURRENT_BINARY_DIR}/${ADD_PANDOC_MAN_PAGE_OUTPUT}.gz") 46 | add_custom_command( 47 | OUTPUT 48 | "${CMAKE_CURRENT_BINARY_DIR}/${ADD_PANDOC_MAN_PAGE_OUTPUT}.gz" 49 | COMMAND 50 | ${GZIP_EXECUTABLE} -k -f -n -9 "${ADD_PANDOC_MAN_PAGE_OUTPUT}" 51 | DEPENDS 52 | "${CMAKE_CURRENT_BINARY_DIR}/${ADD_PANDOC_MAN_PAGE_OUTPUT}") 53 | else() 54 | set(MAN_PAGE_INSTALL_TARGET 55 | "${CMAKE_CURRENT_BINARY_DIR}/${ADD_PANDOC_MAN_PAGE_OUTPUT}") 56 | endif() 57 | 58 | add_custom_target( 59 | ${target_name} ALL 60 | DEPENDS 61 | "${MAN_PAGE_INSTALL_TARGET}") 62 | 63 | install( 64 | FILES 65 | "${MAN_PAGE_INSTALL_TARGET}" 66 | DESTINATION 67 | ${ADD_PANDOC_MAN_PAGE_DESTINATION}) 68 | endif() 69 | endfunction(add_pandoc_man_page) 70 | -------------------------------------------------------------------------------- /cmake/Modules/TestTarget.cmake: -------------------------------------------------------------------------------- 1 | include(CMakeParseArguments) 2 | option(TINT3_ENABLE_ASAN_FOR_TESTS 3 | "Whether to use AddressSanitizer for test binaries" 4 | ON) 5 | 6 | set(LSAN_OPTIONS "report_objects=1:suppressions=${CMAKE_SOURCE_DIR}/test/suppressions/lsan.txt") 7 | set(ASAN_OPTIONS "allow_addr2line=1:fast_unwind_on_malloc=0") 8 | if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") 9 | string(APPEND ASAN_OPTIONS ":malloc_context_size=2") 10 | endif() 11 | 12 | function(test_target target_name) 13 | set(options USE_XVFB_RUN) 14 | set(multiValueArgs SOURCES DEPENDS INCLUDE_DIRS LINK_LIBRARIES) 15 | cmake_parse_arguments(TEST_TARGET "${options}" "" "${multiValueArgs}" ${ARGN}) 16 | 17 | add_executable(${target_name} ${TEST_TARGET_SOURCES}) 18 | if(TEST_TARGET_USE_XVFB_RUN) 19 | add_test( 20 | NAME 21 | ${target_name} 22 | COMMAND 23 | "${CMAKE_SOURCE_DIR}/test/xvfb-run.sh" $ 24 | WORKING_DIRECTORY 25 | "${CMAKE_BINARY_DIR}") 26 | else() 27 | add_test( 28 | NAME 29 | ${target_name} 30 | COMMAND 31 | $ 32 | WORKING_DIRECTORY 33 | "${CMAKE_BINARY_DIR}") 34 | endif() 35 | 36 | if(TEST_TARGET_INCLUDE_DIRS) 37 | target_include_directories(${target_name} ${TEST_TARGET_INCLUDE_DIRS}) 38 | endif() 39 | 40 | if(TEST_TARGET_LINK_LIBRARIES) 41 | target_link_libraries(${target_name} ${TEST_TARGET_LINK_LIBRARIES}) 42 | endif() 43 | 44 | if(TEST_TARGET_DEPENDS) 45 | add_dependencies(${target_name} ${TEST_TARGET_DEPENDS}) 46 | endif() 47 | 48 | if(${TINT3_ENABLE_ASAN_FOR_TESTS}) 49 | # When requested, turn on AddressSanitizer for test targets. 50 | set_target_properties( 51 | ${target_name} PROPERTIES 52 | LINK_FLAGS 53 | "-fsanitize=address" 54 | "-fno-omit-frame-pointer" 55 | "-fno-optimize-sibling-calls") 56 | set_tests_properties( 57 | ${target_name} PROPERTIES 58 | ENVIRONMENT 59 | "LSAN_OPTIONS=${LSAN_OPTIONS};ASAN_OPTIONS=${ASAN_OPTIONS}") 60 | endif() 61 | endfunction(test_target) 62 | -------------------------------------------------------------------------------- /cmake/package.cmake: -------------------------------------------------------------------------------- 1 | # Per-generator CPack configuration file. 2 | # https://cmake.org/cmake/help/v3.0/module/CPack.html#variable:CPACK_PROJECT_CONFIG_FILE 3 | 4 | if(CPACK_GENERATOR STREQUAL "DEB") 5 | set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CPACK_SYSTEM_NAME}") 6 | endif() 7 | 8 | if(CPACK_GENERATOR STREQUAL "RPM") 9 | set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CPACK_RPM_PACKAGE_RELEASE}.${CPACK_SYSTEM_NAME}") 10 | endif() 11 | -------------------------------------------------------------------------------- /data/default_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmc-88/tint3/c13be916f7b551972433fb0807888a5f276600be/data/default_icon.png -------------------------------------------------------------------------------- /data/logo/tint3-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmc-88/tint3/c13be916f7b551972433fb0807888a5f276600be/data/logo/tint3-128.png -------------------------------------------------------------------------------- /data/logo/tint3-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmc-88/tint3/c13be916f7b551972433fb0807888a5f276600be/data/logo/tint3-16.png -------------------------------------------------------------------------------- /data/logo/tint3-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmc-88/tint3/c13be916f7b551972433fb0807888a5f276600be/data/logo/tint3-256.png -------------------------------------------------------------------------------- /data/logo/tint3-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmc-88/tint3/c13be916f7b551972433fb0807888a5f276600be/data/logo/tint3-32.png -------------------------------------------------------------------------------- /data/logo/tint3-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmc-88/tint3/c13be916f7b551972433fb0807888a5f276600be/data/logo/tint3-48.png -------------------------------------------------------------------------------- /data/logo/tint3-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmc-88/tint3/c13be916f7b551972433fb0807888a5f276600be/data/logo/tint3-64.png -------------------------------------------------------------------------------- /data/tint3.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Version=1.0 3 | Type=Application 4 | Name=tint3 5 | Icon=tint3 6 | GenericName=Lightweight X11 panel 7 | Categories=Utility; 8 | Keywords=panel;taskbar;systray;system tray;clock;battery; 9 | TryExec=tint3 10 | Exec=tint3 11 | -------------------------------------------------------------------------------- /debian/conffiles: -------------------------------------------------------------------------------- 1 | /etc/xdg/tint3/tint3rc 2 | -------------------------------------------------------------------------------- /doc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(Pandoc) 2 | 3 | add_pandoc_man_page( 4 | tint3_1 5 | INPUT tint3.1.md 6 | OUTPUT tint3.1 7 | DESTINATION "${MANDIR}/man1") 8 | 9 | add_pandoc_man_page( 10 | tint3rc_5 11 | INPUT tint3rc.5.md 12 | OUTPUT tint3rc.5 13 | DESTINATION "${MANDIR}/man5") 14 | -------------------------------------------------------------------------------- /hooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if git rev-parse --verify HEAD >/dev/null 2>&1; then 4 | against=HEAD 5 | else 6 | # Initial commit: diff against an empty tree object 7 | against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 8 | fi 9 | 10 | # Redirect output to stderr. 11 | exec 1>&2 12 | 13 | # Reformat C++ files with ClangFormat. 14 | git diff-index --name-only --cached "${against}" -- \ 15 | | grep -E '(\.cc|\.h)$' \ 16 | | while read file; do 17 | clang-format -i -style=google "${file}" && { 18 | git add "${file}" 19 | } || exit $? 20 | done 21 | 22 | # If there are whitespace errors, print the offending file names and fail. 23 | exec git diff-index --check --cached "${against}" -- 24 | -------------------------------------------------------------------------------- /sample/icon_and_text_1.tint3rc: -------------------------------------------------------------------------------- 1 | # Tint3 config file 2 | # For information on manually configuring tint3 see https://github.com/jmc-88/tint3 3 | 4 | # Background definitions 5 | # ID 1 6 | rounded = 7 7 | border_width = 2 8 | background_color = #000000 60 9 | border_color = #FFFFFF 17 10 | 11 | # ID 2 12 | rounded = 5 13 | border_width = 0 14 | background_color = #FFFFFF 40 15 | border_color = #FFFFFF 49 16 | 17 | # ID 3 18 | rounded = 5 19 | border_width = 0 20 | background_color = #FFFFFF 17 21 | border_color = #FFFFFF 69 22 | 23 | # Panel 24 | panel_monitor = all 25 | panel_position = bottom center horizontal 26 | panel_size = 94% 30 27 | panel_margin = 0 0 28 | panel_padding = 7 0 7 29 | panel_dock = 0 30 | wm_menu = 0 31 | panel_layer = top 32 | panel_background_id = 1 33 | 34 | # Panel Autohide 35 | autohide = 0 36 | autohide_show_timeout = 0.3 37 | autohide_hide_timeout = 2 38 | autohide_height = 2 39 | strut_policy = follow_size 40 | 41 | # Taskbar 42 | taskbar_mode = single_desktop 43 | taskbar_padding = 2 3 2 44 | taskbar_background_id = 0 45 | taskbar_active_background_id = 0 46 | 47 | # Tasks 48 | urgent_nb_of_blink = 8 49 | task_icon = 1 50 | task_text = 1 51 | task_centered = 1 52 | task_maximum_size = 140 35 53 | task_padding = 6 2 54 | task_background_id = 3 55 | task_active_background_id = 2 56 | task_urgent_background_id = 2 57 | task_iconified_background_id = 3 58 | task_tooltip = 0 59 | 60 | # Task Icons 61 | task_icon_asb = 70 0 0 62 | task_active_icon_asb = 100 0 0 63 | task_urgent_icon_asb = 100 0 0 64 | task_iconified_icon_asb = 70 0 0 65 | 66 | # Fonts 67 | task_font = sans 7 68 | task_font_color = #FFFFFF 69 69 | task_active_font_color = #FFFFFF 84 70 | task_urgent_font_color = #FFFFFF 84 71 | task_iconified_font_color = #FFFFFF 69 72 | font_shadow = 0 73 | 74 | # System Tray 75 | systray = 1 76 | systray_padding = 0 4 5 77 | systray_sort = ascending 78 | systray_background_id = 0 79 | systray_icon_size = 16 80 | systray_icon_asb = 70 0 0 81 | 82 | # Clock 83 | time1_format = %H:%M 84 | time1_font = sans 8 85 | time2_format = %A %d %B 86 | time2_font = sans 6 87 | clock_font_color = #FFFFFF 75 88 | clock_padding = 1 0 89 | clock_background_id = 0 90 | clock_rclick_command = orage 91 | 92 | # Tooltips 93 | tooltip_padding = 2 2 94 | tooltip_show_timeout = 0.7 95 | tooltip_hide_timeout = 0.3 96 | tooltip_background_id = 1 97 | tooltip_font = sans 10 98 | tooltip_font_color = #000000 80 99 | 100 | # Mouse 101 | mouse_middle = none 102 | mouse_right = close 103 | mouse_scroll_up = toggle 104 | mouse_scroll_down = iconify 105 | 106 | # Battery 107 | battery = 0 108 | battery_low_status = 10 109 | battery_low_cmd = notify-send "battery low" 110 | battery_hide = 98 111 | bat1_font = sans 8 112 | bat2_font = sans 6 113 | battery_font_color = #FFFFFF 75 114 | battery_padding = 1 0 115 | battery_background_id = 0 116 | 117 | # End of config -------------------------------------------------------------------------------- /sample/icon_and_text_2.tint3rc: -------------------------------------------------------------------------------- 1 | # Tint3 config file 2 | # For information on manually configuring tint3 see https://github.com/jmc-88/tint3 3 | 4 | # Background definitions 5 | # ID 1 6 | rounded = 7 7 | border_width = 1 8 | background_color = #000000 60 9 | border_color = #FFFFFF 17 10 | 11 | # ID 2 12 | rounded = 5 13 | border_width = 0 14 | background_color = #FFFFFF 40 15 | border_color = #FFFFFF 49 16 | 17 | # ID 3 18 | rounded = 5 19 | border_width = 0 20 | background_color = #FFFFFF 17 21 | border_color = #FFFFFF 69 22 | 23 | # Panel 24 | panel_monitor = all 25 | panel_position = bottom center horizontal 26 | panel_size = 98% 28 27 | panel_margin = 0 5 28 | panel_padding = 3 0 3 29 | panel_dock = 0 30 | wm_menu = 0 31 | panel_layer = bottom 32 | panel_background_id = 0 33 | 34 | # Panel Autohide 35 | autohide = 0 36 | autohide_show_timeout = 0.7 37 | autohide_hide_timeout = 1.5 38 | autohide_height = 2 39 | strut_policy = follow_size 40 | 41 | # Taskbar 42 | taskbar_mode = single_desktop 43 | taskbar_padding = 2 2 2 44 | taskbar_background_id = 1 45 | taskbar_active_background_id = 1 46 | 47 | # Tasks 48 | urgent_nb_of_blink = 8 49 | task_icon = 1 50 | task_text = 1 51 | task_centered = 1 52 | task_maximum_size = 140 35 53 | task_padding = 2 3 54 | task_background_id = 3 55 | task_active_background_id = 2 56 | task_urgent_background_id = 2 57 | task_iconified_background_id = 3 58 | task_tooltip = 0 59 | 60 | # Task Icons 61 | task_icon_asb = 80 0 0 62 | task_active_icon_asb = 100 0 0 63 | task_urgent_icon_asb = 100 0 0 64 | task_iconified_icon_asb = 80 0 0 65 | 66 | # Fonts 67 | task_font = Sawasdee Bold 8 68 | task_font_color = #FFFFFF 89 69 | task_active_font_color = #FFFFFF 100 70 | task_urgent_font_color = #FFFFFF 100 71 | task_iconified_font_color = #FFFFFF 89 72 | font_shadow = 0 73 | 74 | # System Tray 75 | systray = 1 76 | systray_padding = 6 0 5 77 | systray_sort = left2right 78 | systray_background_id = 1 79 | systray_icon_size = 18 80 | systray_icon_asb = 100 0 -10 81 | 82 | # Clock 83 | time1_format = %H:%M 84 | time1_font = Sawasdee Bold 12 85 | clock_font_color = #FFFFFF 89 86 | clock_tooltip = %A %d %B 87 | clock_padding = 4 0 88 | clock_background_id = 1 89 | clock_rclick_command = gsimplecal 90 | 91 | # Tooltips 92 | tooltip_padding = 5 0 93 | tooltip_show_timeout = 0.7 94 | tooltip_hide_timeout = 0.3 95 | tooltip_background_id = 1 96 | tooltip_font = Sans 10 97 | tooltip_font_color = #000000 80 98 | 99 | # Mouse 100 | mouse_middle = close 101 | mouse_right = none 102 | mouse_scroll_up = toggle 103 | mouse_scroll_down = iconify 104 | 105 | # Battery 106 | battery = 0 107 | battery_low_status = 10 108 | battery_low_cmd = notify-send "battery low" 109 | battery_hide = 95 110 | bat1_font = sans 8 111 | bat2_font = sans 6 112 | battery_font_color = #FFFFFF 75 113 | battery_padding = 1 0 114 | battery_background_id = 0 115 | 116 | # End of config -------------------------------------------------------------------------------- /sample/icon_and_text_3.tint3rc: -------------------------------------------------------------------------------- 1 | # Tint3 config file 2 | # For information on manually configuring tint3 see https://github.com/jmc-88/tint3 3 | 4 | # Background definitions 5 | # ID 1 6 | rounded = 0 7 | border_width = 1 8 | background_color = #EAEAEA 100 9 | border_color = #BBBBBB 100 10 | 11 | # ID 2 12 | rounded = 2 13 | border_width = 1 14 | background_color = #E0EBE7 100 15 | border_color = #BBBBBB 100 16 | 17 | # ID 3 18 | rounded = 2 19 | border_width = 1 20 | background_color = #FCFAFB 100 21 | border_color = #BBBBBB 100 22 | 23 | # ID 4 24 | rounded = 2 25 | border_width = 2 26 | background_color = #E0EBE7 100 27 | border_color = #F15A7D 80 28 | 29 | # Panel 30 | panel_monitor = all 31 | panel_position = bottom center horizontal 32 | panel_size = 100% 30 33 | panel_margin = 0 0 34 | panel_padding = 7 3 4 35 | panel_dock = 0 36 | wm_menu = 1 37 | panel_layer = top 38 | panel_background_id = 1 39 | 40 | # Panel Autohide 41 | autohide = 0 42 | autohide_show_timeout = 0.7 43 | autohide_hide_timeout = 1.5 44 | autohide_height = 2 45 | strut_policy = follow_size 46 | 47 | # Taskbar 48 | taskbar_mode = single_desktop 49 | taskbar_padding = 0 0 2 50 | taskbar_background_id = 0 51 | #taskbar_active_background_id = 0 52 | 53 | # Tasks 54 | urgent_nb_of_blink = 20 55 | task_icon = 1 56 | task_text = 1 57 | task_centered = 1 58 | task_maximum_size = 140 40 59 | task_padding = 5 3 60 | task_background_id = 2 61 | task_active_background_id = 3 62 | task_urgent_background_id = 4 63 | task_iconified_background_id = 2 64 | task_tooltip = 1 65 | 66 | # Task Icons 67 | task_icon_asb = 80 0 0 68 | task_active_icon_asb = 100 0 0 69 | task_urgent_icon_asb = 100 0 0 70 | task_iconified_icon_asb = 80 0 0 71 | 72 | # Fonts 73 | task_font = sans 10 74 | task_font_color = #000000 100 75 | task_active_font_color = #000000 100 76 | task_urgent_font_color = #000000 100 77 | task_iconified_font_color = #000000 100 78 | font_shadow = 0 79 | 80 | # System Tray 81 | systray = 1 82 | systray_padding = 5 0 5 83 | systray_sort = ascending 84 | systray_background_id = 2 85 | systray_icon_size = 15 86 | systray_icon_asb = 100 0 -10 87 | 88 | # Clock 89 | time1_format = %F %l:%M %P 90 | time1_font = sans 10 91 | clock_font_color = #000000 100 92 | clock_tooltip = %A %d %B 93 | clock_padding = 1 1 94 | clock_background_id = 0 95 | clock_rclick_command = gsimplecal 96 | 97 | # Tooltips 98 | tooltip_padding = 5 0 99 | tooltip_show_timeout = 1.2 100 | tooltip_hide_timeout = 0.3 101 | tooltip_background_id = 1 102 | tooltip_font = Sans 9 103 | tooltip_font_color = #5E5E5E 100 104 | 105 | # Mouse 106 | mouse_middle = none 107 | mouse_right = close 108 | mouse_scroll_up = toggle 109 | mouse_scroll_down = iconify 110 | 111 | # Battery 112 | battery = 0 113 | battery_low_status = 20 114 | battery_low_cmd = notify-send "battery low" 115 | battery_hide = 95 116 | bat1_font = Sans 9 117 | bat2_font = Sans 8 118 | battery_font_color = #000000 100 119 | battery_padding = 1 1 120 | battery_background_id = 0 121 | 122 | # End of config -------------------------------------------------------------------------------- /sample/icon_and_text_4.tint3rc: -------------------------------------------------------------------------------- 1 | # Tint3 config file 2 | # For information on manually configuring tint3 see https://github.com/jmc-88/tint3 3 | 4 | # Background definitions 5 | # ID 1 6 | rounded = 5 7 | border_width = 0 8 | background_color = #000000 60 9 | border_color = #FFFFFF 20 10 | 11 | # ID 2 12 | rounded = 5 13 | border_width = 1 14 | background_color = #FFFFFF 0 15 | border_color = #FFFFFF 20 16 | 17 | # ID 3 18 | rounded = 5 19 | border_width = 0 20 | background_color = #000000 29 21 | border_color = #000000 0 22 | 23 | # ID 4 24 | rounded = 5 25 | border_width = 1 26 | background_color = #E80000 60 27 | border_color = #FFFFFF 20 28 | 29 | # Panel 30 | panel_monitor = all 31 | panel_position = bottom center horizontal 32 | panel_size = 92% 28 33 | panel_margin = 0 0 34 | panel_padding = 7 0 7 35 | panel_dock = 0 36 | wm_menu = 1 37 | panel_layer = top 38 | panel_background_id = 0 39 | 40 | # Panel Autohide 41 | autohide = 0 42 | autohide_show_timeout = 0.0 43 | autohide_hide_timeout = 0.0 44 | autohide_height = 0 45 | strut_policy = follow_size 46 | 47 | # Taskbar 48 | taskbar_mode = multi_desktop 49 | taskbar_padding = 2 3 2 50 | taskbar_background_id = 3 51 | #taskbar_active_background_id = 0 52 | 53 | # Tasks 54 | urgent_nb_of_blink = 18 55 | task_icon = 1 56 | task_text = 1 57 | task_centered = 1 58 | task_maximum_size = 140 30 59 | task_padding = 2 3 60 | task_background_id = 2 61 | task_active_background_id = 1 62 | task_urgent_background_id = 4 63 | task_iconified_background_id = 2 64 | task_tooltip = 0 65 | 66 | # Task Icons 67 | task_icon_asb = 100 0 0 68 | task_active_icon_asb = 100 0 0 69 | task_urgent_icon_asb = 100 0 0 70 | task_iconified_icon_asb = 100 0 0 71 | 72 | # Fonts 73 | task_font = sans 7 74 | task_font_color = #FFFFFF 69 75 | task_active_font_color = #FFFFFF 84 76 | task_urgent_font_color = #FFFFFF 84 77 | task_iconified_font_color = #FFFFFF 69 78 | font_shadow = 0 79 | 80 | # System Tray 81 | systray = 1 82 | systray_padding = 5 2 5 83 | systray_sort = ascending 84 | systray_background_id = 1 85 | systray_icon_size = 16 86 | systray_icon_asb = 100 -10 -5 87 | 88 | # Clock 89 | time1_format = %H:%M 90 | time1_font = sans 8 91 | time2_format = %A %d %B 92 | time2_font = sans 6 93 | clock_font_color = #FFFFFF 75 94 | clock_padding = 4 4 95 | clock_background_id = 1 96 | 97 | # Tooltips 98 | tooltip_padding = 0 0 99 | tooltip_show_timeout = 0 100 | tooltip_hide_timeout = 0 101 | tooltip_background_id = 0 102 | tooltip_font = Sans 12 103 | tooltip_font_color = #FFFFFF 100 104 | 105 | # Mouse 106 | mouse_middle = none 107 | mouse_right = close 108 | mouse_scroll_up = toggle 109 | mouse_scroll_down = iconify 110 | 111 | # Battery 112 | battery = 0 113 | battery_low_status = 7 114 | battery_low_cmd = notify-send "battery low" 115 | battery_hide = 95 116 | bat1_font = sans 8 117 | bat2_font = sans 6 118 | battery_font_color = #FFFFFF 75 119 | battery_padding = 1 0 120 | battery_background_id = 1 121 | 122 | # End of config -------------------------------------------------------------------------------- /sample/icon_only_1.tint3rc: -------------------------------------------------------------------------------- 1 | # Tint3 config file 2 | # For information on manually configuring tint3 see https://github.com/jmc-88/tint3 3 | 4 | # Background definitions 5 | # ID 1 6 | rounded = 5 7 | border_width = 1 8 | background_color = #44475D 41 9 | border_color = #44475D 100 10 | 11 | # ID 2 12 | rounded = 5 13 | border_width = 1 14 | background_color = #FFFFFF 20 15 | border_color = #444444 66 16 | 17 | # ID 3 18 | rounded = 0 19 | border_width = 1 20 | background_color = #FFFFFF 20 21 | border_color = #444444 20 22 | 23 | # ID 4 24 | rounded = 5 25 | border_width = 1 26 | background_color = #DBDBDB 49 27 | border_color = #222222 74 28 | 29 | # ID 5 30 | rounded = 3 31 | border_width = 0 32 | background_color = #44475D 20 33 | border_color = #222222 74 34 | 35 | # ID 6 36 | rounded = 3 37 | border_width = 0 38 | background_color = #DE1150 34 39 | border_color = #222222 74 40 | 41 | # Panel 42 | panel_monitor = all 43 | panel_position = bottom center horizontal 44 | panel_size = 96% 37 45 | panel_margin = 0 0 46 | panel_padding = 9 3 9 47 | panel_dock = 0 48 | wm_menu = 1 49 | panel_layer = top 50 | panel_background_id = 1 51 | 52 | # Panel Autohide 53 | autohide = 0 54 | autohide_show_timeout = 0.3 55 | autohide_hide_timeout = 1.7 56 | autohide_height = 2 57 | strut_policy = follow_size 58 | 59 | # Taskbar 60 | taskbar_mode = multi_desktop 61 | taskbar_padding = 0 0 0 62 | taskbar_background_id = 2 63 | taskbar_active_background_id = 4 64 | 65 | # Tasks 66 | urgent_nb_of_blink = 16 67 | task_icon = 1 68 | task_text = 0 69 | task_centered = 1 70 | task_maximum_size = 40 20 71 | task_padding = 0 2 72 | task_background_id = 0 73 | task_active_background_id = 5 74 | task_urgent_background_id = 6 75 | task_iconified_background_id = 0 76 | task_tooltip = 1 77 | 78 | # Task Icons 79 | task_icon_asb = 100 -25 -8 80 | task_active_icon_asb = 100 0 -5 81 | task_urgent_icon_asb = 100 0 -5 82 | task_iconified_icon_asb = 100 -25 -8 83 | 84 | # Fonts 85 | task_font = kiloji 10 86 | task_font_color = #333333 80 87 | task_active_font_color = #333333 100 88 | task_urgent_font_color = #333333 100 89 | task_iconified_font_color = #333333 80 90 | font_shadow = 0 91 | 92 | # System Tray 93 | systray = 1 94 | systray_padding = 7 0 5 95 | systray_sort = ascending 96 | systray_background_id = 2 97 | systray_icon_size = 18 98 | systray_icon_asb = 100 -20 -5 99 | 100 | # Clock 101 | time1_format = %H:%M 102 | time1_font = sans 8 bold 103 | time2_format = %A %d %B 104 | time2_font = sans 7 105 | clock_font_color = #FFFFFF 75 106 | clock_tooltip = 107 | clock_padding = 2 0 108 | clock_background_id = 0 109 | clock_rclick_command = gsimplecal 110 | 111 | # Tooltips 112 | tooltip_padding = 5 3 113 | tooltip_show_timeout = 0.8 114 | tooltip_hide_timeout = 0.3 115 | tooltip_background_id = 1 116 | tooltip_font = Sans 8 117 | tooltip_font_color = #FFFFFF 100 118 | 119 | # Mouse 120 | mouse_middle = none 121 | mouse_right = close 122 | mouse_scroll_up = toggle 123 | mouse_scroll_down = iconify 124 | 125 | # Battery 126 | battery = 0 127 | battery_low_status = 20 128 | battery_low_cmd = notify-send "battery low" 129 | battery_hide = 90 130 | bat1_font = sans 8 bold 131 | bat2_font = sans 7 132 | battery_font_color = #FFFFFF 75 133 | battery_padding = 2 0 134 | battery_background_id = 0 135 | 136 | # End of config -------------------------------------------------------------------------------- /sample/icon_only_2.tint3rc: -------------------------------------------------------------------------------- 1 | # Tint3 config file 2 | # For information on manually configuring tint3 see https://github.com/jmc-88/tint3 3 | 4 | # Background definitions 5 | # ID 1 6 | rounded = 0 7 | border_width = 0 8 | background_color = #000000 54 9 | border_color = #9B9B9B 34 10 | 11 | # ID 2 12 | rounded = 2 13 | border_width = 0 14 | background_color = #000000 69 15 | border_color = #9B9B9B 74 16 | 17 | # ID 3 18 | rounded = 1 19 | border_width = 0 20 | background_color = #000000 69 21 | border_color = #FFFFFF 20 22 | 23 | # ID 4 24 | rounded = 0 25 | border_width = 0 26 | background_color = #BBBBBB 20 27 | border_color = #BBBBBB 29 28 | 29 | # Panel 30 | panel_monitor = all 31 | panel_position = bottom center horizontal 32 | panel_size = 100% 37 33 | panel_margin = 0 0 34 | panel_padding = 5 2 3 35 | panel_dock = 0 36 | wm_menu = 1 37 | panel_layer = top 38 | panel_background_id = 1 39 | 40 | # Panel Autohide 41 | autohide = 0 42 | autohide_show_timeout = 0.7 43 | autohide_hide_timeout = 1.5 44 | autohide_height = 2 45 | strut_policy = follow_size 46 | 47 | # Taskbar 48 | taskbar_mode = multi_desktop 49 | taskbar_padding = 0 0 0 50 | taskbar_background_id = 0 51 | taskbar_active_background_id = 2 52 | 53 | # Tasks 54 | urgent_nb_of_blink = 21 55 | task_icon = 1 56 | task_text = 0 57 | task_centered = 1 58 | task_maximum_size = 50 35 59 | task_padding = 4 2 60 | task_background_id = 0 61 | task_active_background_id = 3 62 | task_urgent_background_id = 3 63 | task_iconified_background_id = 0 64 | task_tooltip = 1 65 | 66 | # Task Icons 67 | task_icon_asb = 90 -100 -20 68 | task_active_icon_asb = 100 -70 -10 69 | task_urgent_icon_asb = 100 -70 -10 70 | task_iconified_icon_asb = 90 -100 -20 71 | 72 | # Fonts 73 | task_font = kiloji 8 74 | task_font_color = #A0A0A0 100 75 | task_active_font_color = #D4D4D4 100 76 | task_urgent_font_color = #D4D4D4 100 77 | task_iconified_font_color = #A0A0A0 100 78 | font_shadow = 0 79 | 80 | # System Tray 81 | systray = 1 82 | systray_padding = 8 1 5 83 | systray_sort = ascending 84 | systray_background_id = 2 85 | systray_icon_size = 16 86 | systray_icon_asb = 100 -90 -15 87 | 88 | # Clock 89 | time1_format = %H:%M 90 | time1_font = sans 8 bold 91 | time2_format = %A %d %B 92 | time2_font = sans 7 93 | clock_font_color = #FFFFFF 75 94 | clock_tooltip = %A %d %B 95 | clock_padding = 2 0 96 | clock_background_id = 0 97 | clock_rclick_command = gsimplecal 98 | 99 | # Tooltips 100 | tooltip_padding = 2 2 101 | tooltip_show_timeout = 0.9 102 | tooltip_hide_timeout = 0.3 103 | tooltip_background_id = 4 104 | tooltip_font = sans 10 105 | tooltip_font_color = #000000 80 106 | 107 | # Mouse 108 | mouse_middle = none 109 | mouse_right = close 110 | mouse_scroll_up = toggle 111 | mouse_scroll_down = iconify 112 | 113 | # Battery 114 | battery = 1 115 | battery_low_status = 7 116 | battery_low_cmd = notify-send "battery low" 117 | battery_hide = 90 118 | bat1_font = sans 8 119 | bat2_font = sans 6 120 | battery_font_color = #FFFFFF 75 121 | battery_padding = 1 0 122 | battery_background_id = 0 123 | 124 | # End of config -------------------------------------------------------------------------------- /sample/icon_only_3.tint3rc: -------------------------------------------------------------------------------- 1 | # Tint3 config file 2 | # For information on manually configuring tint3 see https://github.com/jmc-88/tint3 3 | 4 | # Background definitions 5 | # ID 1 6 | rounded = 1 7 | border_width = 0 8 | background_color = #282828 49 9 | border_color = #000000 0 10 | 11 | # ID 2 12 | rounded = 1 13 | border_width = 1 14 | background_color = #FFFFFF 29 15 | border_color = #FFFFFF 60 16 | 17 | # ID 3 18 | rounded = 5 19 | border_width = 0 20 | background_color = #FFFFFF 17 21 | border_color = #FFFFFF 69 22 | 23 | # ID 4 24 | rounded = 1 25 | border_width = 0 26 | background_color = #FFFFFF 29 27 | border_color = #FFFFFF 60 28 | 29 | # Panel 30 | panel_monitor = all 31 | panel_position = bottom left horizontal 32 | panel_size = 100% 38 33 | panel_margin = 0 0 34 | panel_padding = 7 3 7 35 | panel_dock = 0 36 | wm_menu = 0 37 | panel_layer = bottom 38 | panel_background_id = 0 39 | 40 | # Panel Autohide 41 | autohide = 0 42 | autohide_show_timeout = 0.3 43 | autohide_hide_timeout = 2 44 | autohide_height = 4 45 | strut_policy = minimum 46 | 47 | # Taskbar 48 | taskbar_mode = multi_desktop 49 | taskbar_padding = 0 0 0 50 | taskbar_background_id = 1 51 | taskbar_active_background_id = 1 52 | 53 | # Tasks 54 | urgent_nb_of_blink = 15 55 | task_icon = 1 56 | task_text = 0 57 | task_centered = 1 58 | task_maximum_size = 45 35 59 | task_padding = 2 1 60 | task_background_id = 0 61 | task_active_background_id = 2 62 | task_urgent_background_id = 4 63 | task_iconified_background_id = 0 64 | task_tooltip = 0 65 | 66 | # Task Icons 67 | task_icon_asb = 70 0 0 68 | task_active_icon_asb = 100 0 0 69 | task_urgent_icon_asb = 70 0 0 70 | task_iconified_icon_asb = 70 0 0 71 | 72 | # Fonts 73 | task_font = sans bold 9 74 | task_font_color = #FFFFFF 60 75 | task_active_font_color = #FFFFFF 100 76 | task_urgent_font_color = #FFFFFF 100 77 | task_iconified_font_color = #FFFFFF 60 78 | font_shadow = 0 79 | 80 | # System Tray 81 | systray = 1 82 | systray_padding = 0 4 5 83 | systray_sort = left2right 84 | systray_background_id = 0 85 | systray_icon_size = 20 86 | systray_icon_asb = 100 0 0 87 | 88 | # Tooltips 89 | tooltip_padding = 2 2 90 | tooltip_show_timeout = 0.7 91 | tooltip_hide_timeout = 0.3 92 | tooltip_background_id = 1 93 | tooltip_font = sans 10 94 | tooltip_font_color = #000000 80 95 | 96 | # Mouse 97 | mouse_middle = none 98 | mouse_right = none 99 | mouse_scroll_up = toggle 100 | mouse_scroll_down = iconify 101 | 102 | # Battery 103 | battery = 0 104 | battery_low_status = 10 105 | battery_low_cmd = notify-send "battery low" 106 | battery_hide = 90 107 | bat1_font = sans 8 108 | bat2_font = sans 6 109 | battery_font_color = #FFFFFF 75 110 | battery_padding = 1 0 111 | battery_background_id = 0 112 | 113 | # End of config -------------------------------------------------------------------------------- /sample/icon_only_4.tint3rc: -------------------------------------------------------------------------------- 1 | # Tint3 config file 2 | # For information on manually configuring tint3 see https://github.com/jmc-88/tint3 3 | 4 | # Background definitions 5 | # ID 1 6 | rounded = 0 7 | border_width = 1 8 | background_color = #888888 29 9 | border_color = #000000 29 10 | 11 | # ID 2 12 | rounded = 3 13 | border_width = 1 14 | background_color = #888888 69 15 | border_color = #FFFFFF 49 16 | 17 | # ID 3 18 | rounded = 3 19 | border_width = 1 20 | background_color = #888888 29 21 | border_color = #FFFFFF 20 22 | 23 | # ID 4 24 | rounded = 3 25 | border_width = 1 26 | background_color = #888888 29 27 | border_color = #ED2323 60 28 | 29 | # Panel 30 | panel_monitor = all 31 | panel_position = bottom center horizontal 32 | panel_size = 100% 40 33 | panel_margin = 0 0 34 | panel_padding = 0 0 0 35 | panel_dock = 0 36 | wm_menu = 1 37 | panel_layer = top 38 | panel_background_id = 1 39 | 40 | # Panel Autohide 41 | autohide = 0 42 | autohide_show_timeout = 0.7 43 | autohide_hide_timeout = 1.5 44 | autohide_height = 2 45 | strut_policy = follow_size 46 | 47 | # Taskbar 48 | taskbar_mode = multi_desktop 49 | taskbar_padding = 6 1 6 50 | taskbar_background_id = 0 51 | #taskbar_active_background_id = 0 52 | 53 | # Tasks 54 | urgent_nb_of_blink = 20 55 | task_icon = 1 56 | task_text = 0 57 | task_centered = 1 58 | task_maximum_size = 40 40 59 | task_padding = 6 3 60 | task_background_id = 3 61 | task_active_background_id = 2 62 | task_urgent_background_id = 4 63 | task_iconified_background_id = 3 64 | task_tooltip = 1 65 | 66 | # Task Icons 67 | task_icon_asb = 90 0 0 68 | task_active_icon_asb = 100 0 0 69 | task_urgent_icon_asb = 100 0 0 70 | task_iconified_icon_asb = 90 0 0 71 | 72 | # Fonts 73 | task_font = kroeger 06_55 6 74 | task_font_color = #222222 100 75 | task_active_font_color = #000000 100 76 | task_urgent_font_color = #000000 100 77 | task_iconified_font_color = #222222 100 78 | font_shadow = 0 79 | 80 | # System Tray 81 | systray = 1 82 | systray_padding = 4 4 5 83 | systray_sort = ascending 84 | systray_background_id = 0 85 | systray_icon_size = 20 86 | systray_icon_asb = 100 0 -10 87 | 88 | # Tooltips 89 | tooltip_padding = 5 0 90 | tooltip_show_timeout = 0.8 91 | tooltip_hide_timeout = 0.3 92 | tooltip_background_id = 1 93 | tooltip_font = Sans 10 94 | tooltip_font_color = #FFFFFF 80 95 | 96 | # Mouse 97 | mouse_middle = none 98 | mouse_right = close 99 | mouse_scroll_up = toggle 100 | mouse_scroll_down = iconify 101 | 102 | # Battery 103 | battery = 0 104 | battery_low_status = 20 105 | battery_low_cmd = notify-send "battery low" 106 | battery_hide = 90 107 | bat1_font = Sans 12 108 | bat2_font = Sans 12 109 | battery_font_color = #FFFFFF 100 110 | battery_padding = 0 0 111 | battery_background_id = 0 112 | 113 | # End of config -------------------------------------------------------------------------------- /sample/icon_only_6.tint3rc: -------------------------------------------------------------------------------- 1 | # Tint3 config file 2 | # For information on manually configuring tint3 see https://github.com/jmc-88/tint3 3 | 4 | # Background definitions 5 | # ID 1 6 | rounded = 5 7 | border_width = 0 8 | background_color = #000000 49 9 | border_color = #FFFFFF 40 10 | 11 | # ID 2 12 | rounded = 5 13 | border_width = 0 14 | background_color = #FFFFFF 60 15 | border_color = #FFFFFF 49 16 | 17 | # ID 3 18 | rounded = 5 19 | border_width = 0 20 | background_color = #FFFFFF 24 21 | border_color = #FFFFFF 69 22 | 23 | # ID 4 24 | rounded = 5 25 | border_width = 0 26 | background_color = #BDBDBD 80 27 | border_color = #FFFFFF 69 28 | 29 | # Panel 30 | panel_monitor = all 31 | panel_position = top left vertical 32 | panel_size = 75% 50 33 | panel_margin = 0 0 34 | panel_padding = 2 2 2 35 | panel_dock = 0 36 | wm_menu = 0 37 | panel_layer = top 38 | panel_background_id = 0 39 | 40 | # Panel Autohide 41 | autohide = 0 42 | autohide_show_timeout = 0.3 43 | autohide_hide_timeout = 2 44 | autohide_height = 4 45 | strut_policy = minimum 46 | 47 | # Taskbar 48 | taskbar_mode = single_desktop 49 | taskbar_padding = 2 2 2 50 | taskbar_background_id = 1 51 | taskbar_active_background_id = 1 52 | 53 | # Tasks 54 | urgent_nb_of_blink = 20 55 | task_icon = 1 56 | task_text = 0 57 | task_centered = 1 58 | task_maximum_size = 140 35 59 | task_padding = 6 3 60 | task_background_id = 3 61 | task_active_background_id = 2 62 | task_urgent_background_id = 2 63 | task_iconified_background_id = 3 64 | task_tooltip = 1 65 | 66 | # Task Icons 67 | task_icon_asb = 100 0 0 68 | task_active_icon_asb = 100 0 0 69 | task_urgent_icon_asb = 100 0 0 70 | task_iconified_icon_asb = 90 0 0 71 | 72 | # Fonts 73 | task_font = sans 7 74 | task_font_color = #FFFFFF 69 75 | task_active_font_color = #FFFFFF 84 76 | task_urgent_font_color = #EC9B9B 84 77 | task_iconified_font_color = #FFFFFF 69 78 | font_shadow = 1 79 | 80 | # System Tray 81 | systray = 1 82 | systray_padding = 4 4 5 83 | systray_sort = left2right 84 | systray_background_id = 1 85 | systray_icon_size = 16 86 | systray_icon_asb = 100 0 0 87 | 88 | # Clock 89 | time1_format = %H:%M 90 | time1_font = sans bold 8 91 | time2_format = %h.%e 92 | time2_font = sans 6 93 | clock_font_color = #FFFFFF 75 94 | clock_tooltip = %A %d %B 95 | clock_padding = 4 2 96 | clock_background_id = 1 97 | clock_rclick_command = zenity --calendar 98 | 99 | # Tooltips 100 | tooltip_padding = 3 2 101 | tooltip_show_timeout = 0.9 102 | tooltip_hide_timeout = 0.3 103 | tooltip_background_id = 4 104 | tooltip_font = sans 8 105 | tooltip_font_color = #000000 89 106 | 107 | # Mouse 108 | mouse_middle = none 109 | mouse_right = close 110 | mouse_scroll_up = toggle 111 | mouse_scroll_down = iconify 112 | 113 | # Battery 114 | battery = 0 115 | battery_low_status = 10 116 | battery_low_cmd = notify-send "battery low" 117 | battery_hide = 98 118 | bat1_font = sans bold 8 119 | bat2_font = sans 6 120 | battery_font_color = #FFFFFF 75 121 | battery_padding = 4 2 122 | battery_background_id = 1 123 | 124 | # End of config -------------------------------------------------------------------------------- /sample/icon_only_7.tint3rc: -------------------------------------------------------------------------------- 1 | # Tint3 config file 2 | # For information on manually configuring tint3 see https://github.com/jmc-88/tint3 3 | 4 | # Background definitions 5 | # ID 1 6 | rounded = 1 7 | border_width = 0 8 | background_color = #282828 60 9 | border_color = #000000 0 10 | 11 | # ID 2 12 | rounded = 1 13 | border_width = 1 14 | background_color = #CCCCCC 0 15 | border_color = #CCCCCC 54 16 | 17 | # ID 3 18 | rounded = 1 19 | border_width = 0 20 | background_color = #CCCCCC 20 21 | border_color = #CCCCCC 40 22 | 23 | # Panel 24 | panel_monitor = all 25 | panel_position = bottom center horizontal 26 | panel_size = 95% 30 27 | panel_margin = 0 0 28 | panel_padding = 7 3 7 29 | panel_dock = 0 30 | wm_menu = 1 31 | panel_layer = top 32 | panel_background_id = 1 33 | 34 | # Panel Autohide 35 | autohide = 0 36 | autohide_show_timeout = 0.7 37 | autohide_hide_timeout = 1.5 38 | autohide_height = 2 39 | strut_policy = follow_size 40 | 41 | # Taskbar 42 | taskbar_mode = multi_desktop 43 | taskbar_padding = 0 0 0 44 | taskbar_background_id = 2 45 | taskbar_active_background_id = 2 46 | 47 | # Tasks 48 | urgent_nb_of_blink = 7 49 | task_icon = 1 50 | task_text = 0 51 | task_centered = 1 52 | task_maximum_size = 34 34 53 | task_padding = 2 3 54 | task_background_id = 0 55 | task_active_background_id = 3 56 | task_urgent_background_id = 0 57 | task_iconified_background_id = 0 58 | task_tooltip = 1 59 | 60 | # Task Icons 61 | task_icon_asb = 100 0 0 62 | task_active_icon_asb = 100 0 0 63 | task_urgent_icon_asb = 100 0 0 64 | task_iconified_icon_asb = 100 0 0 65 | 66 | # Fonts 67 | task_font = sans 8 68 | task_font_color = #FFFFFF 60 69 | task_active_font_color = #FFFFFF 100 70 | task_urgent_font_color = #FFFFFF 60 71 | task_iconified_font_color = #FFFFFF 60 72 | font_shadow = 0 73 | 74 | # System Tray 75 | systray = 1 76 | systray_padding = 0 0 5 77 | systray_sort = ascending 78 | systray_background_id = 0 79 | systray_icon_size = 16 80 | systray_icon_asb = 100 0 0 81 | 82 | # Clock 83 | time1_format = Paris %H:%M - %d/%m 84 | time1_font = sans 7 85 | time2_format = Moscow %H:%M - %d/%m 86 | time2_font = sans 7 87 | clock_font_color = #FFFFFF 100 88 | clock_tooltip = 89 | clock_padding = 1 0 90 | clock_background_id = 0 91 | clock_rclick_command = gsimplecal 92 | time1_timezone = :Europe/Paris 93 | time2_timezone = :Europe/Moscow 94 | 95 | # Tooltips 96 | tooltip_padding = 5 4 97 | tooltip_show_timeout = 0.8 98 | tooltip_hide_timeout = 0.3 99 | tooltip_background_id = 1 100 | tooltip_font = Sans 7 101 | tooltip_font_color = #FFFFFF 100 102 | 103 | # Mouse 104 | mouse_middle = none 105 | mouse_right = close 106 | mouse_scroll_up = toggle 107 | mouse_scroll_down = iconify 108 | 109 | # Battery 110 | battery = 1 111 | battery_low_status = 7 112 | battery_low_cmd = notify-send "battery low" 113 | battery_hide = 90 114 | bat1_font = sans 7 115 | bat2_font = sans 7 116 | battery_font_color = #FFFFFF 100 117 | battery_padding = 1 0 118 | battery_background_id = 0 119 | 120 | # End of config -------------------------------------------------------------------------------- /sample/text_only_1.tint3rc: -------------------------------------------------------------------------------- 1 | # Tint3 config file 2 | # For information on manually configuring tint3 see https://github.com/jmc-88/tint3 3 | 4 | # Background definitions 5 | # ID 1 6 | rounded = 0 7 | border_width = 1 8 | background_color = #076073 100 9 | border_color = #076073 100 10 | 11 | # ID 2 12 | rounded = 0 13 | border_width = 0 14 | background_color = #000000 100 15 | border_color = #FFFFFF 8 16 | 17 | # ID 3 18 | rounded = 0 19 | border_width = 0 20 | background_color = #222222 100 21 | border_color = #222222 8 22 | 23 | # ID 4 24 | rounded = 0 25 | border_width = 0 26 | background_color = #222222 100 27 | border_color = #222222 8 28 | 29 | # ID 5 30 | rounded = 0 31 | border_width = 0 32 | background_color = #FFFFFF 100 33 | border_color = #FFFFFF 8 34 | 35 | # Panel 36 | panel_monitor = LVDS 37 | panel_position = bottom left horizontal 38 | panel_size = 100% 35 39 | panel_margin = 0 0 40 | panel_padding = 0 0 0 41 | panel_dock = 0 42 | wm_menu = 1 43 | panel_layer = bottom 44 | panel_background_id = 0 45 | 46 | # Panel Autohide 47 | autohide = 0 48 | autohide_show_timeout = 0.7 49 | autohide_hide_timeout = 1.5 50 | autohide_height = 2 51 | strut_policy = follow_size 52 | 53 | # Taskbar 54 | taskbar_mode = multi_desktop 55 | taskbar_padding = 12 9 12 56 | taskbar_background_id = 2 57 | taskbar_active_background_id = 2 58 | 59 | # Tasks 60 | urgent_nb_of_blink = 7 61 | task_icon = 0 62 | task_text = 1 63 | task_centered = 1 64 | task_maximum_size = 0 32 65 | task_padding = 5 2 66 | task_background_id = 4 67 | task_active_background_id = 1 68 | task_urgent_background_id = 0 69 | task_iconified_background_id = 4 70 | task_tooltip = 0 71 | 72 | # Task Icons 73 | task_icon_asb = 100 -90 -15 74 | task_active_icon_asb = 100 -70 0 75 | task_urgent_icon_asb = 100 -90 -15 76 | task_iconified_icon_asb = 100 -90 -15 77 | 78 | # Fonts 79 | task_font = Aller 7.6 80 | task_font_color = #D3CAAA 33 81 | task_active_font_color = #FFFFFF 100 82 | task_urgent_font_color = #FFFFFF 100 83 | task_iconified_font_color = #D3CAAA 33 84 | font_shadow = 0 85 | 86 | # System Tray 87 | systray = 1 88 | systray_padding = 5 5 5 89 | systray_sort = ascending 90 | systray_background_id = 4 91 | systray_icon_size = 18 92 | systray_icon_asb = 100 -90 -15 93 | 94 | # Clock 95 | time1_format = %H:%M 96 | time1_font = Diavlo 12 97 | time2_format = %b %d 98 | time2_font = Diavlo 6 99 | clock_font_color = #FFFFFF 100 100 | clock_tooltip = %A %d %B 101 | clock_padding = 4 2 102 | clock_background_id = 1 103 | clock_rclick_command = gsimplecal 104 | 105 | # Tooltips 106 | tooltip_padding = 5 5 107 | tooltip_show_timeout = 0.7 108 | tooltip_hide_timeout = 0.3 109 | tooltip_background_id = 4 110 | tooltip_font = Aller 8 111 | tooltip_font_color = #D3CAAA 33 112 | 113 | # Mouse 114 | mouse_middle = none 115 | mouse_right = close 116 | mouse_scroll_up = toggle 117 | mouse_scroll_down = iconify 118 | 119 | # Battery 120 | battery = 1 121 | battery_low_status = 7 122 | battery_low_cmd = notify-send "battery low" 123 | battery_hide = 90 124 | bat1_font = Diavlo 10 125 | bat2_font = Aller 0 126 | battery_font_color = #D3CAAA 48 127 | battery_padding = 4 2 128 | battery_background_id = 4 129 | 130 | # End of config -------------------------------------------------------------------------------- /sample/text_only_2.tint3rc: -------------------------------------------------------------------------------- 1 | # Tint3 config file 2 | # For information on manually configuring tint3 see https://github.com/jmc-88/tint3 3 | 4 | # Background definitions 5 | # ID 1 6 | rounded = 0 7 | border_width = 1 8 | background_color = #730A07 100 9 | border_color = #730A07 100 10 | 11 | # ID 2 12 | rounded = 0 13 | border_width = 0 14 | background_color = #000000 100 15 | border_color = #FFFFFF 8 16 | 17 | # ID 3 18 | rounded = 0 19 | border_width = 0 20 | background_color = #222222 100 21 | border_color = #222222 8 22 | 23 | # ID 4 24 | rounded = 0 25 | border_width = 0 26 | background_color = #222222 100 27 | border_color = #222222 8 28 | 29 | # ID 5 30 | rounded = 0 31 | border_width = 0 32 | background_color = #FFFFFF 100 33 | border_color = #FFFFFF 8 34 | 35 | # Panel 36 | panel_monitor = all 37 | panel_position = bottom left horizontal 38 | panel_size = 100% 42 39 | panel_margin = 0 0 40 | panel_padding = 0 0 0 41 | panel_dock = 0 42 | wm_menu = 1 43 | panel_layer = bottom 44 | panel_background_id = 0 45 | 46 | # Panel Autohide 47 | autohide = 0 48 | autohide_show_timeout = 0.7 49 | autohide_hide_timeout = 1.5 50 | autohide_height = 2 51 | strut_policy = follow_size 52 | 53 | # Taskbar 54 | taskbar_mode = multi_desktop 55 | taskbar_padding = 12 7 12 56 | taskbar_background_id = 2 57 | #taskbar_active_background_id = 0 58 | 59 | # Tasks 60 | urgent_nb_of_blink = 7 61 | task_icon = 0 62 | task_text = 1 63 | task_centered = 1 64 | task_maximum_size = 0 32 65 | task_padding = 5 1 66 | task_background_id = 4 67 | task_active_background_id = 1 68 | task_urgent_background_id = 0 69 | task_iconified_background_id = 4 70 | task_tooltip = 0 71 | 72 | # Task Icons 73 | task_icon_asb = 100 -90 -15 74 | task_active_icon_asb = 100 -70 0 75 | task_urgent_icon_asb = 100 -90 -15 76 | task_iconified_icon_asb = 100 -90 -15 77 | 78 | # Fonts 79 | task_font = Aller 7.6 80 | task_font_color = #D3CAAA 33 81 | task_active_font_color = #FFFFFF 100 82 | task_urgent_font_color = #FFFFFF 100 83 | task_iconified_font_color = #D3CAAA 33 84 | font_shadow = 0 85 | 86 | # System Tray 87 | systray = 1 88 | systray_padding = 5 5 5 89 | systray_sort = ascending 90 | systray_background_id = 4 91 | systray_icon_size = 18 92 | systray_icon_asb = 100 -90 -15 93 | 94 | # Clock 95 | time1_format = %H:%M 96 | time1_font = Diavlo 12 97 | time2_format = %b %d 98 | time2_font = Diavlo 6 99 | clock_font_color = #FFFFFF 100 100 | clock_tooltip = %A %d %B 101 | clock_padding = 4 2 102 | clock_background_id = 1 103 | clock_rclick_command = gsimplecal 104 | 105 | # Tooltips 106 | tooltip_padding = 5 5 107 | tooltip_show_timeout = 0.7 108 | tooltip_hide_timeout = 0.3 109 | tooltip_background_id = 4 110 | tooltip_font = Aller 8 111 | tooltip_font_color = #D3CAAA 33 112 | 113 | # Mouse 114 | mouse_middle = none 115 | mouse_right = close 116 | mouse_scroll_up = toggle 117 | mouse_scroll_down = iconify 118 | 119 | # Battery 120 | battery = 1 121 | battery_low_status = 7 122 | battery_low_cmd = notify-send "battery low" 123 | battery_hide = 90 124 | bat1_font = Diavlo 10 125 | bat2_font = Aller 0 126 | battery_font_color = #D3CAAA 48 127 | battery_padding = 4 2 128 | battery_background_id = 4 129 | 130 | # End of config -------------------------------------------------------------------------------- /sample/text_only_3.tint3rc: -------------------------------------------------------------------------------- 1 | # Tint3 config file 2 | # For information on manually configuring tint3 see https://github.com/jmc-88/tint3 3 | 4 | # Background definitions 5 | # ID 1 6 | rounded = 1 7 | border_width = 0 8 | background_color = #282828 100 9 | border_color = #000000 0 10 | 11 | # ID 2 12 | rounded = 1 13 | border_width = 0 14 | background_color = #F6B655 85 15 | border_color = #CCCCCC 40 16 | 17 | # Panel 18 | panel_monitor = all 19 | panel_position = bottom center horizontal 20 | panel_size = 100% 22 21 | panel_margin = 0 0 22 | panel_padding = 3 0 3 23 | panel_dock = 0 24 | wm_menu = 1 25 | panel_layer = bottom 26 | panel_background_id = 1 27 | 28 | # Panel Autohide 29 | autohide = 0 30 | autohide_show_timeout = 0.0 31 | autohide_hide_timeout = 0.0 32 | autohide_height = 0 33 | strut_policy = follow_size 34 | 35 | # Taskbar 36 | taskbar_mode = single_desktop 37 | taskbar_padding = 0 0 0 38 | taskbar_background_id = 0 39 | #taskbar_active_background_id = 0 40 | 41 | # Tasks 42 | urgent_nb_of_blink = 7 43 | task_icon = 0 44 | task_text = 1 45 | task_centered = 1 46 | task_maximum_size = 200 32 47 | task_padding = 5 0 48 | task_background_id = 0 49 | task_active_background_id = 2 50 | task_urgent_background_id = 2 51 | task_iconified_background_id = 0 52 | task_tooltip = 0 53 | 54 | # Task Icons 55 | task_icon_asb = 100 0 0 56 | task_active_icon_asb = 100 0 0 57 | task_urgent_icon_asb = 100 0 0 58 | task_iconified_icon_asb = 100 0 0 59 | 60 | # Fonts 61 | task_font = sans 7.5 62 | task_font_color = #FFFFFF 60 63 | task_active_font_color = #000000 100 64 | task_urgent_font_color = #000000 100 65 | task_iconified_font_color = #FFFFFF 60 66 | font_shadow = 0 67 | 68 | # System Tray 69 | systray = 1 70 | systray_padding = 3 0 3 71 | systray_sort = ascending 72 | systray_background_id = 0 73 | systray_icon_size = 14 74 | systray_icon_asb = 100 -90 -15 75 | 76 | # Clock 77 | time1_format = %H:%M 78 | time1_font = sans 13 79 | clock_font_color = #FFFFFF 85 80 | clock_padding = 2 0 81 | clock_background_id = 0 82 | 83 | # Tooltips 84 | tooltip_padding = 2 2 85 | tooltip_show_timeout = 0.5 86 | tooltip_hide_timeout = 1.2 87 | tooltip_background_id = 1 88 | tooltip_font = Sans 9 89 | tooltip_font_color = #FFFFFF 100 90 | 91 | # Mouse 92 | mouse_middle = none 93 | mouse_right = close 94 | mouse_scroll_up = toggle 95 | mouse_scroll_down = iconify 96 | 97 | # Battery 98 | battery = 1 99 | battery_low_status = 7 100 | battery_low_cmd = notify-send "battery low" 101 | battery_hide = 90 102 | bat1_font = sans 7 103 | bat2_font = sans 6 104 | battery_font_color = #FFFFFF 100 105 | battery_padding = 2 0 106 | battery_background_id = 0 107 | 108 | # End of config 109 | -------------------------------------------------------------------------------- /sample/text_only_4.tint3rc: -------------------------------------------------------------------------------- 1 | # Tint3 config file 2 | # For information on manually configuring tint3 see https://github.com/jmc-88/tint3 3 | 4 | # Background definitions 5 | # ID 1 6 | rounded = 3 7 | border_width = 1 8 | background_color = #000000 40 9 | border_color = #D1D1D1 30 10 | 11 | # ID 2 12 | rounded = 3 13 | border_width = 1 14 | background_color = #000000 51 15 | border_color = #D1D1D1 40 16 | 17 | # Panel 18 | panel_monitor = all 19 | panel_position = bottom center horizontal 20 | panel_size = 97% 26 21 | panel_margin = 0 0 22 | panel_padding = 0 2 5 23 | panel_dock = 0 24 | wm_menu = 1 25 | panel_layer = bottom 26 | panel_background_id = 0 27 | 28 | # Panel Autohide 29 | autohide = 0 30 | autohide_show_timeout = 0.0 31 | autohide_hide_timeout = 0.0 32 | autohide_height = 0 33 | strut_policy = follow_size 34 | 35 | # Taskbar 36 | taskbar_mode = single_desktop 37 | taskbar_padding = 0 0 5 38 | taskbar_background_id = 0 39 | #taskbar_active_background_id = 0 40 | 41 | # Tasks 42 | urgent_nb_of_blink = 7 43 | task_icon = 0 44 | task_text = 1 45 | task_centered = 1 46 | task_maximum_size = 160 30 47 | task_padding = 3 1 48 | task_background_id = 1 49 | task_active_background_id = 2 50 | task_urgent_background_id = 2 51 | task_iconified_background_id = 1 52 | task_tooltip = 0 53 | 54 | # Task Icons 55 | task_icon_asb = 100 0 0 56 | task_active_icon_asb = 100 0 0 57 | task_urgent_icon_asb = 100 0 0 58 | task_iconified_icon_asb = 100 0 0 59 | 60 | # Fonts 61 | task_font = sans bold 7.5 62 | task_font_color = #FFFFFF 60 63 | task_active_font_color = #FFFFFF 86 64 | task_urgent_font_color = #FFFFFF 86 65 | task_iconified_font_color = #FFFFFF 60 66 | font_shadow = 0 67 | 68 | # System Tray 69 | systray = 1 70 | systray_padding = 6 2 6 71 | systray_sort = ascending 72 | systray_background_id = 1 73 | systray_icon_size = 16 74 | systray_icon_asb = 100 -100 -15 75 | 76 | # Tooltips 77 | tooltip_padding = 0 0 78 | tooltip_show_timeout = 0 79 | tooltip_hide_timeout = 0 80 | tooltip_background_id = 0 81 | tooltip_font = Sans 12 82 | tooltip_font_color = #FFFFFF 100 83 | 84 | # Mouse 85 | mouse_middle = none 86 | mouse_right = close 87 | mouse_scroll_up = toggle 88 | mouse_scroll_down = iconify 89 | 90 | # Battery 91 | battery = 0 92 | battery_low_status = 10 93 | battery_low_cmd = notify-send "battery low" 94 | battery_hide = 90 95 | bat1_font = sans 7 96 | bat2_font = sans 7 97 | battery_font_color = #FFFFFF 100 98 | battery_padding = 1 0 99 | battery_background_id = 0 100 | 101 | # End of config 102 | -------------------------------------------------------------------------------- /sample/text_only_5.tint3rc: -------------------------------------------------------------------------------- 1 | # Tint3 config file 2 | # For information on manually configuring tint3 see https://github.com/jmc-88/tint3 3 | 4 | # Background definitions 5 | # ID 1 6 | rounded = 0 7 | border_width = 0 8 | background_color = #FFFFFF 0 9 | border_color = #FFFFFF 60 10 | 11 | # ID 2 12 | rounded = 0 13 | border_width = 1 14 | background_color = #333333 40 15 | border_color = #FFFFFF 40 16 | 17 | # ID 3 18 | rounded = 0 19 | border_width = 0 20 | background_color = #EEEEEC 60 21 | border_color = #FFFFFF 100 22 | 23 | # Panel 24 | panel_monitor = all 25 | panel_position = bottom center horizontal 26 | panel_size = 99% 27 27 | panel_margin = 0 0 28 | panel_padding = 3 3 3 29 | panel_dock = 0 30 | wm_menu = 1 31 | panel_layer = bottom 32 | panel_background_id = 3 33 | 34 | # Panel Autohide 35 | autohide = 0 36 | autohide_show_timeout = 0.2 37 | autohide_hide_timeout = 1.6 38 | autohide_height = 1 39 | strut_policy = minimum 40 | 41 | # Taskbar 42 | taskbar_mode = multi_desktop 43 | taskbar_padding = 0 0 0 44 | taskbar_background_id = 0 45 | taskbar_active_background_id = 0 46 | 47 | # Tasks 48 | urgent_nb_of_blink = 7 49 | task_icon = 0 50 | task_text = 1 51 | task_centered = 1 52 | task_maximum_size = 200 30 53 | task_padding = 5 0 54 | task_background_id = 1 55 | task_active_background_id = 2 56 | task_urgent_background_id = 2 57 | task_iconified_background_id = 1 58 | task_tooltip = 1 59 | 60 | # Task Icons 61 | task_icon_asb = 50 0 0 62 | task_active_icon_asb = 100 0 0 63 | task_urgent_icon_asb = 100 0 0 64 | task_iconified_icon_asb = 50 0 0 65 | 66 | # Fonts 67 | task_font = AvantGardeLTMedium 8 68 | task_font_color = #151515 60 69 | task_active_font_color = #FFFFFF 60 70 | task_urgent_font_color = #7E9659 89 71 | task_iconified_font_color = #FFFFFF 69 72 | font_shadow = 0 73 | 74 | # System Tray 75 | systray = 1 76 | systray_padding = 4 2 3 77 | systray_sort = ascending 78 | systray_background_id = 0 79 | systray_icon_size = 15 80 | systray_icon_asb = 100 0 -10 81 | 82 | # Clock 83 | time1_format = %H:%M / %a %d %b 84 | time1_font = AvantGardeLTMedium 8 85 | clock_font_color = #151515 60 86 | clock_padding = 4 0 87 | clock_background_id = 0 88 | 89 | # Tooltips 90 | tooltip_padding = 3 3 91 | tooltip_show_timeout = 1.5 92 | tooltip_hide_timeout = 0.3 93 | tooltip_background_id = 1 94 | tooltip_font = AvantGardeLTMedium 8 95 | tooltip_font_color = #434141 100 96 | 97 | # Mouse 98 | mouse_middle = none 99 | mouse_right = close 100 | mouse_scroll_up = toggle 101 | mouse_scroll_down = iconify 102 | 103 | # Battery 104 | battery = 0 105 | battery_low_status = 7 106 | battery_low_cmd = notify-send "battery low" 107 | battery_hide = 90 108 | bat1_font = sans 8 109 | bat2_font = sans 6 110 | battery_font_color = #151515 60 111 | battery_padding = 1 0 112 | battery_background_id = 0 113 | 114 | # End of config -------------------------------------------------------------------------------- /sample/text_only_6.tint3rc: -------------------------------------------------------------------------------- 1 | # Tint3 config file 2 | # For information on manually configuring tint3 see https://github.com/jmc-88/tint3 3 | 4 | # Background definitions 5 | # ID 1 6 | rounded = 0 7 | border_width = 0 8 | background_color = #303030 89 9 | border_color = #FFFFFF 17 10 | 11 | # ID 2 12 | rounded = 0 13 | border_width = 0 14 | background_color = #303030 89 15 | border_color = #FFFFFF 49 16 | 17 | # ID 3 18 | rounded = 0 19 | border_width = 0 20 | background_color = #303030 49 21 | border_color = #FFFFFF 69 22 | 23 | # Panel 24 | panel_monitor = all 25 | panel_position = top center horizontal 26 | panel_size = 94% 38 27 | panel_margin = 0 1 28 | panel_padding = 10 6 6 29 | panel_dock = 0 30 | wm_menu = 1 31 | panel_layer = bottom 32 | panel_background_id = 3 33 | 34 | # Panel Autohide 35 | autohide = 0 36 | autohide_show_timeout = 0.7 37 | autohide_hide_timeout = 1.5 38 | autohide_height = 2 39 | strut_policy = follow_size 40 | 41 | # Taskbar 42 | taskbar_mode = single_desktop 43 | taskbar_padding = 0 0 6 44 | taskbar_background_id = 0 45 | #taskbar_active_background_id = 0 46 | 47 | # Tasks 48 | urgent_nb_of_blink = 7 49 | task_icon = 0 50 | task_text = 1 51 | task_centered = 1 52 | task_maximum_size = 120 32 53 | task_padding = 6 2 54 | task_background_id = 2 55 | task_active_background_id = 2 56 | task_urgent_background_id = 2 57 | task_iconified_background_id = 0 58 | task_tooltip = 0 59 | 60 | # Task Icons 61 | task_icon_asb = 100 -90 -15 62 | task_active_icon_asb = 100 -70 0 63 | task_urgent_icon_asb = 100 -90 -15 64 | task_iconified_icon_asb = 100 -90 -15 65 | 66 | # Fonts 67 | task_font = BasicDots 6 68 | task_font_color = #FFFFFF 69 69 | task_active_font_color = #7E9659 89 70 | task_urgent_font_color = #7E9659 89 71 | task_iconified_font_color = #FFFFFF 69 72 | font_shadow = 0 73 | 74 | # System Tray 75 | systray = 1 76 | systray_padding = 6 3 0 77 | systray_sort = ascending 78 | systray_background_id = 1 79 | systray_icon_size = 18 80 | systray_icon_asb = 100 -70 -15 81 | 82 | # Tooltips 83 | tooltip_padding = 5 5 84 | tooltip_show_timeout = 0.7 85 | tooltip_hide_timeout = 0.3 86 | tooltip_background_id = -1 87 | tooltip_font = Aller 8 88 | tooltip_font_color = #D3CAAA 33 89 | 90 | # Mouse 91 | mouse_middle = none 92 | mouse_right = close 93 | mouse_scroll_up = toggle 94 | mouse_scroll_down = iconify 95 | 96 | # Battery 97 | battery = 0 98 | battery_low_status = 10 99 | battery_low_cmd = notify-send "battery low" 100 | battery_hide = 90 101 | bat1_font = sans 8 102 | bat2_font = sans 6 103 | battery_font_color = #FFFFFF 75 104 | battery_padding = 1 0 105 | battery_background_id = 0 106 | 107 | # End of config -------------------------------------------------------------------------------- /sample/tint3rc: -------------------------------------------------------------------------------- 1 | # Default tint3 configuration file. 2 | # 3 | # For information on manually configuring tint3 see: 4 | # https://github.com/jmc-88/tint3 5 | 6 | # {{{ backgrounds 7 | # ID 1 8 | rounded = 0 9 | border_width = 0 10 | background_color = #000000 80 11 | border_color = #000000 0 12 | 13 | # ID 2 14 | rounded = 0 15 | border_width = 0 16 | background_color = #000000 0 17 | background_color_hover = #7f7f7f 40 18 | background_color_pressed = #7f7f7f 70 19 | border_color = #000000 0 20 | 21 | # ID 3 22 | rounded = 0 23 | border_width = 0 24 | background_color = #7f7f7f 80 25 | border_color = #000000 0 26 | 27 | # ID 4 28 | rounded = 0 29 | border_width = 0 30 | background_color = #3f3f3f 90 31 | border_color = #000000 0 32 | # }}} backgrounds 33 | 34 | # {{{ panel 35 | panel_monitor = all 36 | panel_position = bottom center horizontal 37 | panel_size = 100% 40 38 | panel_margin = 0 0 39 | panel_padding = 0 0 0 40 | panel_dock = 0 41 | wm_menu = 1 42 | panel_layer = normal 43 | panel_background_id = 1 44 | # }}} panel 45 | 46 | # {{{ taskbar 47 | taskbar_mode = single_desktop 48 | taskbar_padding = 0 0 5 49 | taskbar_background_id = 0 50 | taskbar_active_background_id = 0 51 | # }}} taskbar 52 | 53 | # {{{ tasks 54 | urgent_nb_of_blink = 20 55 | task_icon = 1 56 | task_text = 0 57 | task_centered = 1 58 | task_maximum_size = 48 30 59 | task_padding = 9 8 0 60 | task_background_id = 2 61 | task_active_background_id = 3 62 | task_urgent_background_id = 2 63 | task_iconified_background_id = 2 64 | # }}} tasks 65 | 66 | # {{{ task icons 67 | task_icon_asb = 100 0 0 68 | task_active_icon_asb = 100 0 0 69 | task_urgent_icon_asb = 100 0 0 70 | task_iconified_icon_asb = 100 0 0 71 | # }}} task icons 72 | 73 | # {{{ system tray 74 | systray = 1 75 | systray_padding = 0 2 6 76 | systray_sort = left2right 77 | systray_background_id = 0 78 | systray_icon_size = 22 79 | systray_icon_asb = 100 0 0 80 | # }}} system tray 81 | 82 | # {{{ battery 83 | battery = 1 84 | battery_hide = 99 85 | battery_low_status = 25 86 | battery_low_cmd = notify-send --app-name=tint3 --expire-time=-1 --icon=dialog-warning "Battery level is critically low. Please connect your charger as soon as possible." 87 | battery_padding = 10 6 88 | battery_background_id = 0 89 | bat1_font = Droid Sans Bold 10 90 | bat2_font = Droid Sans 10 91 | battery_font_color = #dcdcdc 100 92 | # }}} battery 93 | 94 | # {{{ clock 95 | time1_format = %H:%M 96 | time1_font = Droid Sans Bold 10 97 | clock_font_color = #dcdcdc 100 98 | clock_padding = 10 6 99 | clock_background_id = 0 100 | clock_lclick_command = gsimplecal 101 | # }}} clock 102 | 103 | # {{{ tooltips 104 | tooltip = 1 105 | tooltip_padding = 2 2 106 | tooltip_show_timeout = 0.0 107 | tooltip_hide_timeout = 0.0 108 | tooltip_background_id = 4 109 | tooltip_font = Droid Sans 10 110 | tooltip_font_color = #dcdcdc 100 111 | # }}} tooltips 112 | 113 | # {{{ mouse 114 | mouse_middle = none 115 | mouse_right = close 116 | mouse_scroll_up = toggle 117 | mouse_scroll_down = iconify 118 | mouse_effects = 1 119 | mouse_hover_icon_asb = 100 0 25 120 | mouse_pressed_icon_asb = 100 0 -25 121 | # }}} mouse 122 | -------------------------------------------------------------------------------- /src/battery/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library( 2 | battery_interface_lib INTERFACE) 3 | 4 | target_sources( 5 | battery_interface_lib 6 | INTERFACE 7 | "${PROJECT_SOURCE_DIR}/src/battery/battery_interface.hh") 8 | 9 | add_library( 10 | battery_lib STATIC 11 | battery.cc) 12 | 13 | target_link_libraries( 14 | battery_lib 15 | PRIVATE 16 | common_lib 17 | fs_lib 18 | log_lib 19 | panel_lib 20 | server_lib 21 | subprocess_lib 22 | window_lib 23 | absl::str_format 24 | ${CAIRO_LIBRARIES} 25 | ${PANGOCAIRO_LIBRARIES} 26 | PUBLIC 27 | area_lib 28 | battery_interface_lib 29 | common_lib 30 | pango_lib 31 | timer_lib) 32 | 33 | if(CMAKE_SYSTEM_NAME MATCHES "Linux") 34 | target_link_libraries( 35 | battery_lib 36 | PRIVATE 37 | linux_sysfs_lib) 38 | 39 | add_library( 40 | linux_sysfs_lib STATIC 41 | linux_sysfs.cc) 42 | 43 | target_link_libraries( 44 | linux_sysfs_lib 45 | PRIVATE 46 | common_lib 47 | fs_lib 48 | PUBLIC 49 | battery_interface_lib) 50 | endif() 51 | 52 | if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") 53 | target_link_libraries( 54 | battery_lib 55 | PRIVATE 56 | freebsd_acpiio_lib) 57 | 58 | add_library( 59 | freebsd_acpiio_lib STATIC 60 | freebsd_acpiio.cc) 61 | 62 | target_link_libraries( 63 | freebsd_acpiio_lib 64 | PRIVATE 65 | log_lib 66 | PUBLIC 67 | battery_interface_lib) 68 | endif() 69 | -------------------------------------------------------------------------------- /src/battery/battery.hh: -------------------------------------------------------------------------------- 1 | #ifndef TINT3_BATTERY_BATTERY_HH 2 | #define TINT3_BATTERY_BATTERY_HH 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #include "util/area.hh" 12 | #include "util/common.hh" 13 | #include "util/pango.hh" 14 | #include "util/timer.hh" 15 | 16 | // forward declarations 17 | class Panel; 18 | 19 | // battery drawing parameter (per panel) 20 | class Battery : public Area { 21 | public: 22 | Color font; 23 | int bat1_posy; 24 | int bat2_posy; 25 | 26 | void DrawForeground(cairo_t*) override; 27 | bool Resize() override; 28 | 29 | static std::function InitPanel(Panel* panel, Timer* timer); 30 | 31 | #ifdef _TINT3_DEBUG 32 | 33 | std::string GetFriendlyName() const override; 34 | 35 | #endif // _TINT3_DEBUG 36 | 37 | private: 38 | std::string battery_percentage_; 39 | std::string battery_time_; 40 | }; 41 | 42 | extern util::pango::FontDescriptionPtr bat1_font_desc; 43 | extern util::pango::FontDescriptionPtr bat2_font_desc; 44 | extern bool battery_enabled; 45 | extern int percentage_hide; 46 | 47 | extern int8_t battery_low_status; 48 | extern std::string battery_low_cmd; 49 | extern std::string path_energy_now; 50 | extern std::string path_energy_full; 51 | extern std::string path_current_now; 52 | extern std::string path_status; 53 | 54 | // default global data 55 | void DefaultBattery(); 56 | 57 | // free memory 58 | void CleanupBattery(Timer& timer); 59 | 60 | // initialize clock : y position, ... 61 | void UpdateBattery(); 62 | 63 | void InitBattery(); 64 | 65 | #endif // TINT3_BATTERY_BATTERY_HH 66 | -------------------------------------------------------------------------------- /src/battery/battery_interface.hh: -------------------------------------------------------------------------------- 1 | #ifndef TINT3_BATTERY_BATTERY_INTERFACE_HH 2 | #define TINT3_BATTERY_BATTERY_INTERFACE_HH 3 | 4 | #include 5 | 6 | enum class ChargeState { kUnknown, kCharging, kDischarging, kFull }; 7 | 8 | struct BatteryTimestamp { 9 | int16_t hours; 10 | int8_t minutes; 11 | int8_t seconds; 12 | }; 13 | 14 | struct BatteryState { 15 | int percentage; 16 | BatteryTimestamp time; 17 | ChargeState state; 18 | }; 19 | 20 | class BatteryInterface { 21 | public: 22 | virtual bool Found() const = 0; 23 | virtual bool Update() = 0; 24 | virtual double charge_percentage() const = 0; 25 | virtual ChargeState charge_state() const = 0; 26 | virtual unsigned int seconds_to_charge() const = 0; 27 | }; 28 | 29 | #endif // TINT3_BATTERY_BATTERY_INTERFACE_HH 30 | -------------------------------------------------------------------------------- /src/battery/freebsd_acpiio.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include // acpiio.h expects uintN_t types to be defined 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "battery/freebsd_acpiio.hh" 10 | #include "util/log.hh" 11 | 12 | namespace { 13 | 14 | constexpr char kAcpiDevice[] = "/dev/acpi"; 15 | 16 | int GetBatteries() { 17 | int fd = open(kAcpiDevice, O_RDONLY); 18 | if (fd != -1) { 19 | int units; 20 | int rc = ioctl(fd, ACPIIO_BATT_GET_UNITS, &units); 21 | close(fd); 22 | if (rc != -1) { 23 | return units; 24 | } 25 | } 26 | return -1; 27 | } 28 | 29 | ChargeState GetChargeState(int state) { 30 | if (state & ACPI_BATT_STAT_CHARGING) { 31 | return ChargeState::kCharging; 32 | } 33 | if (state & (ACPI_BATT_STAT_DISCHARG | ACPI_BATT_STAT_CRITICAL)) { 34 | return ChargeState::kDischarging; 35 | } 36 | return ChargeState::kUnknown; 37 | } 38 | 39 | } // namespace 40 | 41 | namespace freebsd_acpiio { 42 | 43 | Battery::Battery() 44 | : found_(false), 45 | charge_state_(ChargeState::kUnknown), 46 | charge_percentage_(0), 47 | seconds_to_charge_(0) { 48 | int units = GetBatteries(); 49 | if (units != -1) { 50 | found_ = true; 51 | util::log::Debug() << "freebsd_acpiio: Found " << units << " batteries.\n"; 52 | } 53 | } 54 | 55 | bool Battery::Found() const { return found_; } 56 | 57 | bool Battery::Update() { 58 | int fd = open(kAcpiDevice, O_RDONLY); 59 | if (fd == -1) { 60 | return false; 61 | } 62 | 63 | union acpi_battery_ioctl_arg bi; 64 | bi.unit = ACPI_BATTERY_ALL_UNITS; 65 | if (ioctl(fd, ACPIIO_BATT_GET_BATTINFO, &bi) == -1) { 66 | close(fd); 67 | return false; 68 | } 69 | 70 | charge_percentage_ = bi.battinfo.cap; 71 | charge_state_ = GetChargeState(bi.battinfo.state); 72 | // FIXME: we cap this at 0 since charging states return a negative value for 73 | // min. We can obviously do better, but this is a starting point. 74 | seconds_to_charge_ = std::max(0, bi.battinfo.min * 60); 75 | 76 | close(fd); 77 | return true; 78 | } 79 | 80 | double Battery::charge_percentage() const { return charge_percentage_; } 81 | 82 | ChargeState Battery::charge_state() const { return charge_state_; } 83 | 84 | unsigned int Battery::seconds_to_charge() const { return seconds_to_charge_; } 85 | 86 | } // namespace freebsd_acpiio 87 | -------------------------------------------------------------------------------- /src/battery/freebsd_acpiio.hh: -------------------------------------------------------------------------------- 1 | #ifndef TINT3_BATTERY_FREEBSD_ACPIIO_HH 2 | #define TINT3_BATTERY_FREEBSD_ACPIIO_HH 3 | 4 | #include "battery/battery_interface.hh" 5 | 6 | namespace freebsd_acpiio { 7 | 8 | class Battery : public BatteryInterface { 9 | public: 10 | Battery(); 11 | 12 | bool Found() const; 13 | bool Update(); 14 | double charge_percentage() const; 15 | ChargeState charge_state() const; 16 | unsigned int seconds_to_charge() const; 17 | 18 | private: 19 | bool found_; 20 | ChargeState charge_state_; 21 | double charge_percentage_; 22 | unsigned int seconds_to_charge_; 23 | }; 24 | 25 | } // namespace freebsd_acpiio 26 | 27 | #endif // TINT3_BATTERY_FREEBSD_ACPIIO_HH 28 | -------------------------------------------------------------------------------- /src/battery/linux_sysfs.hh: -------------------------------------------------------------------------------- 1 | #ifndef TINT3_BATTERY_LINUX_SYSFS_HH 2 | #define TINT3_BATTERY_LINUX_SYSFS_HH 3 | 4 | #include 5 | #include 6 | 7 | #include "battery/battery_interface.hh" 8 | 9 | namespace linux_sysfs { 10 | 11 | std::vector GetBatteryDirectories(); 12 | 13 | class Battery : public BatteryInterface { 14 | public: 15 | Battery(std::string const& base_path); 16 | 17 | bool Found() const; 18 | bool Update(); 19 | double charge_percentage() const; 20 | ChargeState charge_state() const; 21 | unsigned int seconds_to_charge() const; 22 | 23 | private: 24 | std::string path_current_now_; 25 | std::string path_energy_now_; 26 | std::string path_energy_full_; 27 | std::string path_status_; 28 | bool found_; 29 | ChargeState charge_state_; 30 | double charge_percentage_; 31 | unsigned int seconds_to_charge_; 32 | }; 33 | 34 | } // namespace linux_sysfs 35 | 36 | #endif // TINT3_BATTERY_LINUX_SYSFS_HH 37 | -------------------------------------------------------------------------------- /src/behavior_control.hh: -------------------------------------------------------------------------------- 1 | #ifndef TINT3_BEHAVIOR_CONTROL_HH 2 | #define TINT3_BEHAVIOR_CONTROL_HH 3 | 4 | #ifdef __clang__ 5 | #define FORCE_STD_MOVE_START \ 6 | _Pragma("clang diagnostic push"); \ 7 | _Pragma("clang diagnostic ignored \"-Wpessimizing-move\""); 8 | #else // __clang__ 9 | #define FORCE_STD_MOVE_START 10 | #endif // __clang__ 11 | 12 | #ifdef __clang__ 13 | #define FORCE_STD_MOVE_END _Pragma("clang diagnostic pop"); 14 | #else // __clang__ 15 | #define FORCE_STD_MOVE_END 16 | #endif // __clang__ 17 | 18 | #define FORCE_STD_MOVE(code) \ 19 | FORCE_STD_MOVE_START; \ 20 | code; \ 21 | FORCE_STD_MOVE_END; 22 | 23 | #endif // TINT3_BEHAVIOR_CONTROL_HH -------------------------------------------------------------------------------- /src/clock/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library( 2 | clock_lib STATIC 3 | clock.cc) 4 | 5 | target_include_directories( 6 | clock_lib 7 | PRIVATE 8 | ${CAIRO_INCLUDE_DIRS}) 9 | 10 | target_link_libraries( 11 | clock_lib 12 | PRIVATE 13 | common_lib 14 | panel_lib 15 | server_lib 16 | subprocess_lib 17 | timer_lib 18 | window_lib 19 | absl::time 20 | ${CAIRO_LIBRARIES} 21 | PUBLIC 22 | area_lib 23 | common_lib 24 | pango_lib 25 | timer_lib 26 | ${PANGOCAIRO_LIBRARIES}) 27 | 28 | test_target( 29 | clock_test 30 | SOURCES 31 | clock_test.cc 32 | INCLUDE_DIRS 33 | ${X11_X11_INCLUDE_DIRS} 34 | LINK_LIBRARIES 35 | clock_lib 36 | testmain 37 | ${X11_X11_LIB}) 38 | -------------------------------------------------------------------------------- /src/clock/clock.hh: -------------------------------------------------------------------------------- 1 | #ifndef TINT3_CLOCK_CLOCK_HH 2 | #define TINT3_CLOCK_CLOCK_HH 3 | 4 | #include 5 | 6 | #include "util/area.hh" 7 | #include "util/common.hh" 8 | #include "util/pango.hh" 9 | #include "util/timer.hh" 10 | 11 | class Clock : public Area { 12 | public: 13 | Color font_; 14 | int time1_posy_; 15 | int time2_posy_; 16 | 17 | void DrawForeground(cairo_t*) override; 18 | std::string GetTooltipText() override; 19 | bool Resize() override; 20 | 21 | bool HandlesClick(XEvent* event) override; 22 | bool OnClick(XEvent* event) override; 23 | 24 | static void InitPanel(Panel* panel); 25 | 26 | #ifdef _TINT3_DEBUG 27 | 28 | std::string GetFriendlyName() const override; 29 | 30 | #endif // _TINT3_DEBUG 31 | 32 | private: 33 | std::string time1_; 34 | std::string time2_; 35 | }; 36 | 37 | extern std::string time1_format; 38 | extern std::string time1_timezone; 39 | extern std::string time2_format; 40 | extern std::string time2_timezone; 41 | extern std::string time_tooltip_format; 42 | extern std::string time_tooltip_timezone; 43 | extern std::string clock_lclick_command; 44 | extern std::string clock_rclick_command; 45 | extern util::pango::FontDescriptionPtr time1_font_desc; 46 | extern util::pango::FontDescriptionPtr time2_font_desc; 47 | extern bool clock_enabled; 48 | 49 | // default global data 50 | void DefaultClock(); 51 | 52 | // freed memory 53 | void CleanupClock(Timer& timer); 54 | 55 | // initialize clock : y position, precision, ... 56 | void InitClock(Timer& timer); 57 | 58 | #endif // TINT3_CLOCK_CLOCK_HH 59 | -------------------------------------------------------------------------------- /src/clock/clock_test.cc: -------------------------------------------------------------------------------- 1 | #include "catch.hpp" 2 | 3 | #include 4 | 5 | #include "clock/clock.hh" 6 | 7 | TEST_CASE("HandlesClick") { 8 | Clock clock; 9 | clock.panel_x_ = 0; 10 | clock.width_ = 200; 11 | clock.panel_y_ = 0; 12 | clock.height_ = 100; 13 | clock.on_screen_ = true; 14 | 15 | SECTION("Click is outside an on-screen Clock") { 16 | XEvent e; 17 | e.xbutton.x = -50; 18 | e.xbutton.y = -50; 19 | REQUIRE_FALSE(clock.HandlesClick(&e)); 20 | } 21 | 22 | SECTION("Click is inside an off-screen Clock") { 23 | XEvent e; 24 | e.xbutton.x = 50; 25 | e.xbutton.y = 50; 26 | 27 | clock.on_screen_ = false; 28 | REQUIRE_FALSE(clock.HandlesClick(&e)); 29 | } 30 | 31 | SECTION("Left click inside, left click action undefined") { 32 | XEvent e; 33 | e.xbutton.x = 50; 34 | e.xbutton.y = 50; 35 | e.xbutton.button = 1; 36 | 37 | clock_lclick_command.clear(); 38 | REQUIRE_FALSE(clock.HandlesClick(&e)); 39 | } 40 | 41 | SECTION("Left click inside, left click action defined") { 42 | XEvent e; 43 | e.xbutton.x = 50; 44 | e.xbutton.y = 50; 45 | e.xbutton.button = 1; 46 | 47 | clock_lclick_command = "bogus"; 48 | REQUIRE(clock.HandlesClick(&e)); 49 | } 50 | 51 | SECTION("Right click inside, right click action undefined") { 52 | XEvent e; 53 | e.xbutton.x = 50; 54 | e.xbutton.y = 50; 55 | e.xbutton.button = 3; 56 | 57 | clock_rclick_command.clear(); 58 | REQUIRE_FALSE(clock.HandlesClick(&e)); 59 | } 60 | 61 | SECTION("Right click inside, right click action defined") { 62 | XEvent e; 63 | e.xbutton.x = 50; 64 | e.xbutton.y = 50; 65 | e.xbutton.button = 3; 66 | 67 | clock_rclick_command = "bogus"; 68 | REQUIRE(clock.HandlesClick(&e)); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/config.hh: -------------------------------------------------------------------------------- 1 | #ifndef TINT3_CONFIG_HH 2 | #define TINT3_CONFIG_HH 3 | 4 | #include 5 | 6 | #include "parser/parser.hh" 7 | #include "server.hh" 8 | #include "util/fs.hh" 9 | 10 | namespace test { 11 | 12 | class ConfigReader; 13 | 14 | } // namespace test 15 | 16 | namespace config { 17 | 18 | enum Tokens { 19 | kNewLine = (parser::kEOF + 1), 20 | kPoundSign, 21 | kEqualsSign, 22 | kImport, 23 | kIdentifier, 24 | kWhitespace, 25 | kAny, 26 | }; 27 | 28 | extern const parser::Lexer kLexer; 29 | 30 | void ExtractValues(const std::string& value, std::string* v1, std::string* v2, 31 | std::string* v3); 32 | 33 | class Parser; 34 | class Reader { 35 | public: 36 | Reader(Server* server); 37 | 38 | static void GetDefaultPaths(util::fs::Path* user_config_dir, 39 | util::fs::Path* config_path); 40 | 41 | virtual bool LoadFromFile(std::string const& path); 42 | bool LoadFromDefaults(); 43 | 44 | private: 45 | friend class Parser; 46 | friend class test::ConfigReader; 47 | 48 | Server* server_; 49 | bool new_config_file_; 50 | 51 | void AddEntry(std::string const& key, std::string const& value); 52 | bool AddEntry_BackgroundBorder(std::string const& key, 53 | std::string const& value); 54 | bool AddEntry_Gradient(std::string const& key, std::string const& value); 55 | bool AddEntry_Panel(std::string const& key, std::string const& value); 56 | bool AddEntry_Battery(std::string const& key, std::string const& value); 57 | bool AddEntry_Clock(std::string const& key, std::string const& value); 58 | bool AddEntry_Taskbar(std::string const& key, std::string const& value); 59 | bool AddEntry_Task(std::string const& key, std::string const& value); 60 | bool AddEntry_Systray(std::string const& key, std::string const& value); 61 | bool AddEntry_Launcher(std::string const& key, std::string const& value); 62 | bool AddEntry_Tooltip(std::string const& key, std::string const& value); 63 | bool AddEntry_Executor(std::string const& key, std::string const& value); 64 | bool AddEntry_Mouse(std::string const& key, std::string const& value); 65 | bool AddEntry_Autohide(std::string const& key, std::string const& value); 66 | bool AddEntry_Legacy(std::string const& key, std::string const& value); 67 | unsigned int GetMonitor(std::string const& monitor_name) const; 68 | }; 69 | 70 | class Parser : public parser::ParseCallback { 71 | // config_entry ::= '\n' config_entry 72 | // | comment '\n' config_entry 73 | // | import 74 | // | assignment; 75 | // comment ::= '#' value '\n'; 76 | // assignment ::= \s* identifier \s+ '=' \s+ value \s* '\n'; 77 | // import ::= '@import "' value '"'"; 78 | // identifier ::= [A-Za-z][A-Za-z0-9-]*; 79 | // value ::= [^\n]+; 80 | 81 | public: 82 | Parser(Reader* reader, std::string const& path); 83 | 84 | bool operator()(parser::TokenList* tokens); 85 | 86 | private: 87 | Reader* reader_; 88 | util::fs::Path current_config_path_; 89 | 90 | bool ConfigEntryParser(parser::TokenList* tokens); 91 | bool Comment(parser::TokenList* tokens); 92 | bool Import(parser::TokenList* tokens); 93 | bool Assignment(parser::TokenList* tokens); 94 | bool AddKeyValue(std::string key, std::string value); 95 | }; 96 | 97 | } // namespace config 98 | 99 | #endif // TINT3_CONFIG_HH 100 | -------------------------------------------------------------------------------- /src/cxx_features.hh.in: -------------------------------------------------------------------------------- 1 | #ifndef TINT3_CXX_FEATURES_HH 2 | #define TINT3_CXX_FEATURES_HH 3 | 4 | #cmakedefine TINT3_HAVE_STD_NEARBYINT 5 | #cmakedefine TINT3_HAVE_STD_ROUND 6 | 7 | #include 8 | 9 | #ifndef TINT3_HAVE_STD_NEARBYINT 10 | namespace std { 11 | template 12 | T nearbyint(T x) { 13 | #ifdef TINT3_HAVE_STD_ROUND 14 | return round(x); 15 | #else // TINT3_HAVE_STD_ROUND 16 | return ::round(x); 17 | #endif // TINT3_HAVE_STD_ROUND 18 | } 19 | }; 20 | #endif // TINT3_HAVE_STD_NEARBYINT 21 | 22 | #endif // TINT3_CXX_FEATURES_HH 23 | -------------------------------------------------------------------------------- /src/dnd/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library( 2 | dnd_lib STATIC 3 | dnd.cc) 4 | 5 | target_include_directories( 6 | dnd_lib 7 | PUBLIC 8 | ${X11_X11_INCLUDE_DIRS}) 9 | 10 | target_link_libraries( 11 | dnd_lib 12 | PRIVATE 13 | common_lib 14 | server_lib 15 | PUBLIC 16 | ${X11_X11_LIB}) 17 | 18 | test_target( 19 | dnd_test 20 | SOURCES 21 | dnd_test.cc 22 | LINK_LIBRARIES 23 | dnd_lib 24 | testmain) 25 | -------------------------------------------------------------------------------- /src/dnd/dnd.hh: -------------------------------------------------------------------------------- 1 | #ifndef TINT3_DND_DND_HH 2 | #define TINT3_DND_DND_HH 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | namespace dnd { 11 | 12 | // Holds data returned by XGetWindowProperty. 13 | class Property { 14 | public: 15 | Property(const void* data, int format, unsigned long nitems, Atom type); 16 | Property(Property&&) = default; 17 | Property(Property const&) = delete; 18 | Property& operator=(Property) = delete; 19 | 20 | const void* data; 21 | const int format; 22 | const unsigned long nitems; 23 | const Atom type; 24 | }; 25 | 26 | // Same as Property, but automatically frees the allocated memory with XFree. 27 | class AutoProperty : public Property { 28 | public: 29 | AutoProperty(const void* data, int format, unsigned long nitems, Atom type); 30 | ~AutoProperty(); 31 | 32 | AutoProperty(AutoProperty&&) = default; 33 | AutoProperty(AutoProperty const&) = delete; 34 | AutoProperty& operator=(AutoProperty) = delete; 35 | }; 36 | 37 | AutoProperty ReadProperty(Display* disp, Window w, Atom property); 38 | std::string BuildCommand(std::string const& dnd_launcher_exec, 39 | Property const& prop); 40 | 41 | Atom PickTargetFromList(Display* disp, Atom const* atom_list, int nitems); 42 | Atom PickTargetFromAtoms(Display* disp, Atom t1, Atom t2, Atom t3); 43 | Atom PickTargetFromTargets(Display* disp, Property const& p); 44 | 45 | } // namespace dnd 46 | 47 | #endif // TINT3_DND_DND_HH 48 | -------------------------------------------------------------------------------- /src/dnd/dnd_test.cc: -------------------------------------------------------------------------------- 1 | #include "catch.hpp" 2 | 3 | #include 4 | #include 5 | 6 | #include "dnd/dnd.hh" 7 | 8 | class FakeStringProperty : public dnd::Property { 9 | public: 10 | FakeStringProperty() = delete; 11 | explicit FakeStringProperty(const char* data, unsigned int length) 12 | : dnd::Property{data, 8, length - 1, XA_STRING} {} 13 | }; 14 | 15 | TEST_CASE("BuildCommand", "Command construction is (somewhat) sane") { 16 | constexpr char plain[] = "one\ntwo\nthree"; 17 | FakeStringProperty plain_prop{plain, sizeof(plain) / sizeof(char)}; 18 | REQUIRE(dnd::BuildCommand("test_command", plain_prop) == 19 | "test_command \"one\" \"two\" \"three\""); 20 | 21 | constexpr char escape[] = "one`\ntwo$\nthree\\"; 22 | FakeStringProperty escape_prop{escape, sizeof(escape) / sizeof(char)}; 23 | REQUIRE(dnd::BuildCommand("test_command", escape_prop) == 24 | "test_command \"one\\`\" \"two\\$\" \"three\\\\\""); 25 | } 26 | -------------------------------------------------------------------------------- /src/execp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library( 2 | execp_lib STATIC 3 | execp.cc) 4 | 5 | target_link_libraries( 6 | execp_lib 7 | PRIVATE 8 | common_lib 9 | #environment_lib 10 | log_lib 11 | panel_lib 12 | subprocess_lib 13 | window_lib 14 | PUBLIC 15 | area_lib 16 | color_lib 17 | pango_lib) 18 | 19 | test_target( 20 | execp_test 21 | SOURCES 22 | execp_test.cc 23 | LINK_LIBRARIES 24 | execp_lib 25 | testmain) 26 | -------------------------------------------------------------------------------- /src/execp/execp.hh: -------------------------------------------------------------------------------- 1 | #ifndef TINT3_EXECP_EXECP_HH 2 | #define TINT3_EXECP_EXECP_HH 3 | 4 | #include 5 | #include 6 | 7 | #include "util/area.hh" 8 | #include "util/color.hh" 9 | #include "util/pango.hh" 10 | 11 | class Panel; 12 | class Executor : public Area { 13 | public: 14 | Executor(); 15 | 16 | void InitPanel(Panel* panel); 17 | 18 | std::string GetTooltipText() override; 19 | void DrawForeground(cairo_t* c) override; 20 | bool Resize() override; 21 | 22 | void set_cache_icon(bool cache_icon); 23 | void set_centered(bool centered); 24 | 25 | std::string const& command() const; 26 | void set_command(std::string const& command); 27 | void set_command_left_click(std::string const& command); 28 | void set_command_middle_click(std::string const& command); 29 | void set_command_right_click(std::string const& command); 30 | void set_command_up_wheel(std::string const& command); 31 | void set_command_down_wheel(std::string const& command); 32 | 33 | bool continuous() const; 34 | void set_continuous(unsigned int continuous); 35 | 36 | void set_font(std::string const& font); 37 | void set_font_color(Color const& color); 38 | void set_has_icon(bool has_icon); 39 | 40 | unsigned int icon_height() const; 41 | void set_icon_height(unsigned int height); 42 | 43 | unsigned int icon_width() const; 44 | void set_icon_width(unsigned int width); 45 | 46 | unsigned int interval() const; 47 | void set_interval(unsigned int interval); 48 | 49 | void set_markup(bool continuous); 50 | void set_tooltip(std::string const& tooltip); 51 | 52 | bool HandlesClick(XEvent* event) override; 53 | bool OnClick(XEvent* event) override; 54 | 55 | private: 56 | Background background_; 57 | bool cache_icon_ = false; 58 | bool centered_ = false; 59 | std::string command_; 60 | std::string command_left_click_; 61 | std::string command_middle_click_; 62 | std::string command_right_click_; 63 | std::string command_up_wheel_; 64 | std::string command_down_wheel_; 65 | bool continuous_ = false; 66 | util::pango::FontDescriptionPtr font_description_; 67 | Color font_color_; 68 | bool has_icon_ = false; 69 | unsigned int icon_height_ = 0; 70 | unsigned int icon_width_ = 0; 71 | unsigned int interval_ = 0; 72 | bool markup_ = false; 73 | bool has_tooltip_ = false; 74 | std::string tooltip_; 75 | }; 76 | 77 | #endif // TINT3_EXECP_EXECP_HH 78 | -------------------------------------------------------------------------------- /src/execp/execp_test.cc: -------------------------------------------------------------------------------- 1 | #include "catch.hpp" 2 | 3 | #include "execp/execp.hh" 4 | 5 | TEST_CASE("GetTooltipText") { 6 | SECTION("no tooltip provided") { 7 | Executor e; 8 | // TODO: this should check that GetTooltipText() == last_contents_of_stderr, 9 | // but that part is not yet implemented, so we can't test it here. 10 | REQUIRE(e.GetTooltipText().empty()); 11 | } 12 | SECTION("empty tooltip provided") { 13 | Executor e; 14 | e.set_tooltip(""); 15 | REQUIRE(e.GetTooltipText().empty()); 16 | } 17 | SECTION("non-empty tooltip provided") { 18 | Executor e; 19 | e.set_tooltip("something"); 20 | REQUIRE(e.GetTooltipText() == "something"); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/launcher/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library( 2 | launcher_lib STATIC 3 | launcher.cc) 4 | 5 | target_include_directories( 6 | launcher_lib 7 | PRIVATE 8 | ${CAIRO_INCLUDE_DIRS}) 9 | 10 | target_link_libraries( 11 | launcher_lib 12 | PRIVATE 13 | desktop_entry_lib 14 | fs_lib 15 | log_lib 16 | panel_lib 17 | server_lib 18 | startup_notification_lib 19 | subprocess_lib 20 | taskbar_lib 21 | xdg_lib 22 | absl::str_format 23 | ${CAIRO_LIBRARIES} 24 | PUBLIC 25 | area_lib 26 | common_lib 27 | imlib2_lib 28 | ${XSETTINGS_CLIENT_LIBRARIES}) 29 | 30 | test_target( 31 | launcher_test 32 | SOURCES 33 | launcher_test.cc 34 | DEPENDS 35 | testdata 36 | LINK_LIBRARIES 37 | environment_lib 38 | launcher_lib 39 | panel_lib 40 | testmain) 41 | 42 | add_library( 43 | desktop_entry_lib STATIC 44 | desktop_entry.cc) 45 | 46 | target_link_libraries( 47 | desktop_entry_lib 48 | PRIVATE 49 | common_lib 50 | log_lib 51 | absl::strings 52 | PUBLIC 53 | parser_lib 54 | absl::variant) 55 | 56 | test_target( 57 | desktop_entry_test 58 | SOURCES 59 | desktop_entry_test.cc 60 | LINK_LIBRARIES 61 | desktop_entry_lib 62 | parser_lib 63 | testmain) 64 | -------------------------------------------------------------------------------- /src/launcher/desktop_entry.hh: -------------------------------------------------------------------------------- 1 | #ifndef TINT3_LAUNCHER_DESKTOP_ENTRY_HH 2 | #define TINT3_LAUNCHER_DESKTOP_ENTRY_HH 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "absl/types/variant.h" 9 | 10 | #include "parser/parser.hh" 11 | 12 | namespace launcher { 13 | namespace desktop_entry { 14 | 15 | enum Tokens { 16 | kNewLine = (parser::kEOF + 1), 17 | kPoundSign, 18 | kLeftBracket, 19 | kRightBracket, 20 | kEqualsSign, 21 | kIdentifier, 22 | kWhitespace, 23 | kAny, 24 | }; 25 | 26 | extern const parser::Lexer kLexer; 27 | 28 | class Group { 29 | public: 30 | using StringList = std::vector; 31 | using LocaleString = std::unordered_map; 32 | 33 | using Value = 34 | absl::variant; 35 | 36 | static const std::string kInvalidName; 37 | 38 | explicit Group(std::string const& name); 39 | Group(Group const& other); 40 | Group(Group&& other); 41 | 42 | Group& operator=(Group other); 43 | 44 | std::string const& GetName() const; 45 | 46 | bool HasEntry(std::string const& key) const; 47 | 48 | template 49 | bool IsEntry(std::string const& key) { 50 | return HasEntry(key) && absl::holds_alternative(entries_.at(key)); 51 | } 52 | 53 | template 54 | T& GetEntry(std::string const& key) { 55 | return absl::get(entries_.at(key)); 56 | } 57 | 58 | template 59 | void AddEntry(std::string const& key, T const& value) { 60 | auto it = entries_.find(key); 61 | if (it != entries_.end()) { 62 | entries_.erase(it); 63 | } 64 | entries_.emplace(key, value); 65 | } 66 | 67 | private: 68 | std::string name_; 69 | std::unordered_map entries_; 70 | }; 71 | 72 | // A Desktop Entry is a list of groups and their key/value entries. 73 | using DesktopEntry = std::vector; 74 | 75 | class Parser : public parser::ParseCallback { 76 | // desktop_entry ::= '\n' desktop_entry 77 | // | comment '\n' desktop_entry 78 | // | group; 79 | // comment ::= '#' value '\n'; 80 | // group ::= '[' group_header ']' '\n' group_entry; 81 | // group_header ::= [^\[\]]+ 82 | // group_entry ::= identifier '=' value '\n' group_entry 83 | // | comment '\n' group_entry 84 | // | '\n' group_entry 85 | // | group; 86 | // identifier ::= [A-Za-z][A-Za-z0-9-]* 87 | // value ::= [^\n]+ 88 | 89 | public: 90 | Parser(); 91 | 92 | bool operator()(parser::TokenList* tokens); 93 | DesktopEntry GetDesktopEntry() const; 94 | 95 | private: 96 | Group current_group_; 97 | DesktopEntry groups_; 98 | 99 | bool DesktopEntryParser(parser::TokenList* tokens); 100 | bool Comment(parser::TokenList* tokens); 101 | bool GroupParser(parser::TokenList* tokens); 102 | bool GroupHeader(parser::TokenList* tokens); 103 | bool GroupEntry(parser::TokenList* tokens); 104 | bool AddKeyValue(std::string key, std::string value, std::string locale); 105 | }; 106 | 107 | bool ParseBooleanValue(std::string value_string, bool* value_boolean); 108 | bool ParseNumericValue(std::string value_string, float* value_numeric); 109 | bool ParseStringValue(std::string* value_string); 110 | bool ParseStringListValue(std::string value_string, 111 | Group::StringList* value_string_list); 112 | 113 | bool Validate(DesktopEntry* entry); 114 | 115 | std::string BestLocalizedEntry(Group& group, std::string const& key); 116 | 117 | } // namespace desktop_entry 118 | } // namespace launcher 119 | 120 | #endif // TINT3_LAUNCHER_DESKTOP_ENTRY_HH 121 | -------------------------------------------------------------------------------- /src/launcher/launcher.hh: -------------------------------------------------------------------------------- 1 | #ifndef TINT3_LAUNCHER_LAUNCHER_HH 2 | #define TINT3_LAUNCHER_LAUNCHER_HH 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "util/area.hh" 9 | #include "util/common.hh" 10 | #include "util/imlib2.hh" 11 | 12 | class LauncherIcon : public Area { 13 | public: 14 | util::imlib2::Image icon_original_; 15 | util::imlib2::Image icon_scaled_; 16 | util::imlib2::Image icon_hover_; 17 | util::imlib2::Image icon_pressed_; 18 | std::string cmd_; 19 | std::string icon_name_; 20 | std::string icon_path_; 21 | std::string icon_tooltip_; 22 | int icon_size_ = 0; 23 | bool is_app_desktop_ = false; 24 | int x_ = 0; 25 | int y_ = 0; 26 | 27 | LauncherIcon(); 28 | 29 | void DrawForeground(cairo_t*) override; 30 | std::string GetTooltipText() override; 31 | void OnChangeLayout() override; 32 | bool OnClick(XEvent* event) override; 33 | 34 | #ifdef _TINT3_DEBUG 35 | 36 | std::string GetFriendlyName() const override; 37 | 38 | #endif // _TINT3_DEBUG 39 | }; 40 | 41 | struct DesktopEntry { 42 | std::string name; 43 | std::string exec; 44 | std::string icon; 45 | }; 46 | 47 | enum class IconType { kScalable, kFixed, kThreshold }; 48 | 49 | struct IconThemeDir { 50 | std::string name; 51 | std::string context; 52 | int size; 53 | IconType type; 54 | int max_size; 55 | int min_size; 56 | int threshold; 57 | }; 58 | 59 | class IconTheme { 60 | public: 61 | ~IconTheme(); 62 | 63 | std::string name; 64 | std::vector list_inherits; 65 | std::vector list_directories; 66 | }; 67 | 68 | class Launcher : public Area { 69 | std::string GetIconPath(std::string const& icon_name, int size); 70 | 71 | public: 72 | std::vector list_apps_; // paths to .desktop files 73 | std::vector list_icons_; 74 | std::vector list_themes_; 75 | 76 | int GetIconSize() const; 77 | 78 | void CleanupTheme(); 79 | 80 | // Populates the list_themes list 81 | bool LoadThemes(); 82 | 83 | // Populates the list_icons list 84 | void LoadIcons(); 85 | 86 | bool Resize() override; 87 | 88 | static void InitPanel(Panel* panel); 89 | 90 | #ifdef _TINT3_DEBUG 91 | 92 | std::string GetFriendlyName() const override; 93 | 94 | #endif // _TINT3_DEBUG 95 | }; 96 | 97 | extern bool launcher_enabled; 98 | extern int launcher_max_icon_size; 99 | extern bool launcher_tooltip_enabled; 100 | extern int launcher_alpha; 101 | extern int launcher_saturation; 102 | extern int launcher_brightness; 103 | extern std::string icon_theme_name; // theme name 104 | extern XSettingsClient* xsettings_client; 105 | 106 | // default global data 107 | void DefaultLauncher(); 108 | 109 | // initialize launcher : y position, precision, ... 110 | void InitLauncher(); 111 | void CleanupLauncher(); 112 | 113 | // Looks up for the given desktop entry in well known paths. 114 | // The desktop entry can be a relative or absolute path to a file, or it can 115 | // be simply the file name that will be resolved against standard XDG dirs. 116 | bool FindDesktopEntry(std::string const& name, std::string* output_path); 117 | 118 | #endif // TINT3_LAUNCHER_LAUNCHER_HH 119 | -------------------------------------------------------------------------------- /src/launcher/testdata/.icons/UnitTestTheme/index.theme: -------------------------------------------------------------------------------- 1 | [Icon Theme] 2 | Name=UnitTestTheme 3 | Comment=Not a real theme, mind you. 4 | Inherits=Adwaita,gnome,hicolor 5 | Directories=16x16/actions,16x16/apps,16x16/categories,16x16/devices 6 | 7 | [16x16/actions] 8 | Context=Actions 9 | Size=16 10 | Type=Fixed 11 | 12 | [16x16/apps] 13 | Context=Applications 14 | Size=16 15 | Type=Fixed 16 | 17 | [16x16/categories] 18 | Context=Categories 19 | Size=16 20 | Type=Fixed 21 | 22 | [16x16/devices] 23 | Context=Devices 24 | Size=16 25 | Type=Fixed 26 | -------------------------------------------------------------------------------- /src/launcher/testdata/applications/launcher_test.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=FindDesktopEntry 3 | Comment=The most beautiful desktop entry out there! 4 | Exec=/bin/true 5 | Icon=tint3 6 | Terminal=false 7 | Type=Application 8 | Categories=Office; 9 | -------------------------------------------------------------------------------- /src/parser/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library( 2 | parser_lib STATIC 3 | parser.cc) 4 | 5 | target_link_libraries( 6 | parser_lib 7 | PRIVATE 8 | absl::strings 9 | PUBLIC 10 | common_lib 11 | lexer_lib) 12 | 13 | test_target( 14 | parser_test 15 | SOURCES 16 | parser_test.cc 17 | LINK_LIBRARIES 18 | parser_lib 19 | testmain) 20 | 21 | add_library( 22 | lexer_lib STATIC 23 | lexer.cc) 24 | 25 | test_target( 26 | lexer_test 27 | SOURCES 28 | lexer_test.cc 29 | LINK_LIBRARIES 30 | lexer_lib 31 | testmain) 32 | -------------------------------------------------------------------------------- /src/parser/lexer.hh: -------------------------------------------------------------------------------- 1 | #ifndef TINT3_PARSER_LEXER_HH 2 | #define TINT3_PARSER_LEXER_HH 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace parser { 11 | namespace matcher { 12 | 13 | // NewLine matcher accepts DOS-style (), Macintosh-style () or 14 | // Unix-style () newline sequences. 15 | bool NewLine(std::string const& buffer, unsigned int* position, 16 | std::string* output); 17 | 18 | // Whitespace matcher accepts all the characters accepted by isspace(), except 19 | // for the newline character (which is usually to be accepted as a separate 20 | // token, and not swallowed by this matcher). 21 | bool Whitespace(std::string const& buffer, unsigned int* position, 22 | std::string* output); 23 | 24 | // Any matcher accepts any single character. 25 | bool Any(std::string const& buffer, unsigned int* position, 26 | std::string* output); 27 | 28 | } // namespace matcher 29 | 30 | using Symbol = unsigned int; 31 | 32 | static constexpr Symbol kEOF = 0; 33 | 34 | class TokenMatcher { 35 | public: 36 | using MatcherCallback = bool(std::string const&, unsigned int*, std::string*); 37 | using MatcherFunction = std::function; 38 | 39 | TokenMatcher(TokenMatcher const& other); 40 | TokenMatcher(TokenMatcher&& other); 41 | 42 | TokenMatcher(MatcherCallback matcher); 43 | TokenMatcher(char c); 44 | TokenMatcher(const char* regexp); 45 | TokenMatcher(std::string const& regexp); 46 | 47 | TokenMatcher& operator=(TokenMatcher other); 48 | bool operator()(std::string const& buffer, unsigned int* position, 49 | std::string* output) const; 50 | 51 | private: 52 | MatcherFunction matcher_; 53 | 54 | static MatcherFunction MakeCharacterMatcher(char c); 55 | static MatcherFunction MakeRegexpMatcher(std::string const& pattern); 56 | }; 57 | 58 | class Token { 59 | public: 60 | Symbol const symbol; 61 | unsigned int const begin; 62 | unsigned int const end; 63 | std::string const match; 64 | 65 | Token(Symbol symbol, unsigned int begin, unsigned int end, 66 | std::string const& match); 67 | }; 68 | 69 | class Lexer { 70 | public: 71 | using Result = std::vector; 72 | using MatchPair = std::pair; 73 | 74 | Lexer(Lexer const& other); 75 | Lexer(Lexer&& other); 76 | explicit Lexer(std::initializer_list const& match_map); 77 | 78 | Lexer& operator=(Lexer other); 79 | 80 | bool ProcessContents(std::string const& buffer, Result* result) const; 81 | 82 | private: 83 | // This could be an std::map for brevity, but we want to preserve the 84 | // given matcher ordering. 85 | std::vector matcher_to_symbol_; 86 | }; 87 | 88 | } // namespace parser 89 | 90 | #endif // TINT3_PARSER_LEXER_HH 91 | -------------------------------------------------------------------------------- /src/parser/parser.cc: -------------------------------------------------------------------------------- 1 | #include "absl/strings/str_cat.h" 2 | #include "absl/strings/str_join.h" 3 | 4 | #include "parser/parser.hh" 5 | 6 | #include "util/common.hh" 7 | 8 | namespace parser { 9 | 10 | TokenList::TokenList(Lexer::Result tokens) : tokens_(tokens), current_(0) {} 11 | 12 | Token const& TokenList::Current() const { return tokens_.at(current_); } 13 | 14 | bool TokenList::Accept(Symbol symbol) { 15 | if (Current().symbol == symbol) { 16 | // Skip over the current symbol, unless we're at the end of file. 17 | if (symbol != kEOF) { 18 | Next(); 19 | } 20 | return true; 21 | } 22 | return false; 23 | } 24 | 25 | bool TokenList::Next() { 26 | if (current_ != tokens_.size() - 1) { 27 | ++current_; 28 | return true; 29 | } 30 | return false; 31 | } 32 | 33 | bool TokenList::SkipOver(Symbol symbol, std::vector* output) { 34 | while (Current().symbol == symbol) { 35 | if (output != nullptr) { 36 | output->push_back(Current()); 37 | } 38 | if (!Next()) { 39 | return false; 40 | } 41 | } 42 | return (Current().symbol != kEOF); 43 | } 44 | 45 | bool TokenList::SkipUntil(Symbol symbol, std::vector* output) { 46 | while (Current().symbol != symbol && Current().symbol != kEOF) { 47 | if (output != nullptr) { 48 | output->push_back(Current()); 49 | } 50 | if (!Next()) { 51 | return false; 52 | } 53 | } 54 | return (Current().symbol != kEOF); 55 | } 56 | 57 | std::string TokenList::JoinSkipped(std::vector const& tokens) { 58 | static auto token_formatter = [](std::string* out, Token const& t) { 59 | absl::StrAppend(out, t.match); 60 | }; 61 | return absl::StrJoin(tokens, "", token_formatter); 62 | } 63 | 64 | Parser::Parser(Lexer lexer, ParseCallback* entry_fn) 65 | : lexer_(lexer), parser_entry_fn_(entry_fn) {} 66 | 67 | bool Parser::Parse(std::string const& buffer) const { 68 | Lexer::Result result; 69 | if (!lexer_.ProcessContents(buffer, &result)) { 70 | return false; 71 | } 72 | 73 | TokenList tokens{result}; 74 | if (!(*parser_entry_fn_)(&tokens)) { 75 | return false; 76 | } 77 | 78 | // also verify the parser has consumed all content 79 | return (tokens.Current().symbol == kEOF); 80 | } 81 | 82 | } // namespace parser 83 | -------------------------------------------------------------------------------- /src/parser/parser.hh: -------------------------------------------------------------------------------- 1 | #ifndef TINT3_PARSER_PARSER_HH 2 | #define TINT3_PARSER_PARSER_HH 3 | 4 | #include 5 | #include 6 | 7 | #include "parser/lexer.hh" 8 | 9 | namespace parser { 10 | 11 | class TokenList { 12 | public: 13 | explicit TokenList(Lexer::Result tokens); 14 | 15 | Token const& Current() const; 16 | bool Accept(Symbol symbol); 17 | bool Next(); 18 | bool SkipOver(Symbol symbol, std::vector* output = nullptr); 19 | bool SkipUntil(Symbol symbol, std::vector* output = nullptr); 20 | 21 | static std::string JoinSkipped(std::vector const& tokens); 22 | 23 | private: 24 | Lexer::Result tokens_; 25 | unsigned int current_; 26 | }; 27 | 28 | class ParseCallback { 29 | public: 30 | virtual bool operator()(TokenList* tokens) = 0; 31 | }; 32 | 33 | class Parser { 34 | public: 35 | Parser(Lexer lexer, ParseCallback* entry_fn); 36 | 37 | bool Parse(std::string const& buffer) const; 38 | 39 | private: 40 | Lexer lexer_; 41 | ParseCallback* parser_entry_fn_; 42 | }; 43 | 44 | } // namespace parser 45 | 46 | #endif // TINT3_PARSER_PARSER_HH 47 | -------------------------------------------------------------------------------- /src/parser/parser_test.cc: -------------------------------------------------------------------------------- 1 | #include "catch.hpp" 2 | 3 | #include 4 | #include 5 | 6 | #include "parser/parser.hh" 7 | 8 | enum TestFileTokens { 9 | kAdd = (parser::kEOF + 1), 10 | kMultiply, 11 | kLeftBracket, 12 | kRightBracket, 13 | kNumber, 14 | kWhitespace 15 | }; 16 | 17 | static const parser::Lexer test_file_lexer{ 18 | std::make_pair('+', kAdd), std::make_pair('*', kMultiply), 19 | std::make_pair('(', kLeftBracket), std::make_pair(')', kRightBracket), 20 | std::make_pair("[0-9-]+", kNumber), std::make_pair("\\s+", kWhitespace), 21 | }; 22 | 23 | class SimpleArithmeticParser : public parser::ParseCallback { 24 | // ::= "+" | "*" | "(" ")" | [0-9]+ 25 | 26 | public: 27 | bool operator()(parser::TokenList* tokens) { return Expression(tokens); } 28 | 29 | void Clear() { 30 | while (!op_stack_.empty()) { 31 | op_stack_.pop(); 32 | } 33 | op_stack_.emplace(0); 34 | } 35 | 36 | unsigned int Result() const { return op_stack_.top(); } 37 | 38 | private: 39 | std::stack op_stack_; 40 | 41 | bool Expression(parser::TokenList* tokens) { 42 | if (tokens->Current().symbol == kNumber) { 43 | op_stack_.emplace(std::stoul(tokens->Current().match)); 44 | tokens->Next(); 45 | } else if (tokens->Current().symbol == kLeftBracket) { 46 | if (!WrappedExpression(tokens)) { 47 | return false; 48 | } 49 | } else { 50 | return false; 51 | } 52 | 53 | tokens->SkipOver(kWhitespace); 54 | 55 | if (tokens->Accept(kAdd)) { 56 | tokens->SkipOver(kWhitespace); 57 | if (!Expression(tokens)) { 58 | return false; 59 | } 60 | 61 | unsigned int op2 = op_stack_.top(); 62 | op_stack_.pop(); 63 | unsigned int op1 = op_stack_.top(); 64 | op_stack_.pop(); 65 | op_stack_.emplace(op1 + op2); 66 | } else if (tokens->Accept(kMultiply)) { 67 | tokens->SkipOver(kWhitespace); 68 | if (!Expression(tokens)) { 69 | return false; 70 | } 71 | 72 | unsigned int op2 = op_stack_.top(); 73 | op_stack_.pop(); 74 | unsigned int op1 = op_stack_.top(); 75 | op_stack_.pop(); 76 | op_stack_.emplace(op1 * op2); 77 | } 78 | 79 | return true; 80 | } 81 | 82 | bool WrappedExpression(parser::TokenList* tokens) { 83 | if (!tokens->Accept(kLeftBracket)) { 84 | return false; 85 | } 86 | if (!Expression(tokens)) { 87 | return false; 88 | } 89 | return tokens->Accept(kRightBracket); 90 | } 91 | }; 92 | 93 | TEST_CASE("Parser", "Parses valid contents and rejects invalid ones") { 94 | SimpleArithmeticParser simple_arithmetic_parser; 95 | parser::Parser test_parser{test_file_lexer, &simple_arithmetic_parser}; 96 | 97 | // valid expressions 98 | simple_arithmetic_parser.Clear(); 99 | REQUIRE(test_parser.Parse("7")); 100 | REQUIRE(simple_arithmetic_parser.Result() == 7); 101 | 102 | simple_arithmetic_parser.Clear(); 103 | REQUIRE(test_parser.Parse("2 * 6")); 104 | REQUIRE(simple_arithmetic_parser.Result() == 12); 105 | 106 | simple_arithmetic_parser.Clear(); 107 | REQUIRE(test_parser.Parse("(5 * 2) + (3 * (1 + 2))")); 108 | REQUIRE(simple_arithmetic_parser.Result() == 19); 109 | 110 | // invalid expressions 111 | simple_arithmetic_parser.Clear(); 112 | REQUIRE_FALSE(test_parser.Parse("")); 113 | 114 | simple_arithmetic_parser.Clear(); 115 | REQUIRE_FALSE(test_parser.Parse("()")); 116 | 117 | simple_arithmetic_parser.Clear(); 118 | REQUIRE_FALSE(test_parser.Parse("1 + 2 * 3 ()")); 119 | 120 | simple_arithmetic_parser.Clear(); 121 | REQUIRE_FALSE(test_parser.Parse("5 - 3")); 122 | } 123 | -------------------------------------------------------------------------------- /src/server.hh: -------------------------------------------------------------------------------- 1 | #ifndef TINT3_SERVER_HH 2 | #define TINT3_SERVER_HH 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "startup_notification.hh" 14 | #include "util/x11.hh" 15 | 16 | struct Monitor { 17 | unsigned int number; 18 | int x; 19 | int y; 20 | unsigned int width; 21 | unsigned int height; 22 | std::vector names; 23 | }; 24 | 25 | class Server { 26 | public: 27 | Display* dsp = nullptr; 28 | Window composite_manager = None; 29 | unsigned int screen = 0; 30 | int depth = 32; 31 | // number of monitors (without monitors included into another one) 32 | unsigned int num_monitors = 0; 33 | std::vector monitor; 34 | Visual* visual = nullptr; 35 | // root background 36 | GC gc = None; 37 | util::x11::Colormap colormap; 38 | 39 | SnDisplay* sn_dsp = nullptr; 40 | #ifdef HAVE_SN 41 | std::unordered_map pids; 42 | #endif // HAVE_SN 43 | 44 | void Cleanup(); 45 | void UpdateCurrentDesktop(); 46 | std::vector GetDesktopNames() const; 47 | void UpdateNumberOfDesktops(); 48 | int GetNumberOfDesktops(); 49 | void GetRootPixmap(); 50 | void InitGC(Window win); 51 | void InitAtoms(); 52 | void InitDesktops(); 53 | void InitVisual(); 54 | void InitX11(); 55 | 56 | util::x11::Pixmap CreatePixmap(unsigned int width, unsigned int height) const; 57 | 58 | unsigned int desktop() const; 59 | unsigned int num_desktops() const; 60 | 61 | Window root_window() const; 62 | void UpdateRootWindow(); 63 | 64 | bool real_transparency() const; 65 | 66 | Atom atom(std::string const& name) const; 67 | 68 | template 69 | util::x11::ClientData GetProperty(Window win, Atom at, Atom type, 70 | int* num_results) { 71 | if (!win) { 72 | return util::x11::ClientData(nullptr); 73 | } 74 | 75 | Atom type_ret; 76 | int format_ret = 0; 77 | unsigned long nitems_ret = 0; 78 | unsigned long bafter_ret = 0; 79 | unsigned char* prop_value = nullptr; 80 | int result = 81 | XGetWindowProperty(dsp, win, at, 0, 0x7fffffff, False, type, &type_ret, 82 | &format_ret, &nitems_ret, &bafter_ret, &prop_value); 83 | 84 | // Send back resultcount 85 | if (num_results != nullptr) { 86 | (*num_results) = static_cast(nitems_ret); 87 | } 88 | 89 | if (result == Success && prop_value != nullptr) { 90 | return util::x11::ClientData(prop_value); 91 | } 92 | 93 | return util::x11::ClientData(nullptr); 94 | } 95 | 96 | template 97 | T GetProperty32(Window win, Atom at, Atom type) { 98 | int num_results; 99 | auto data = GetProperty(win, at, type, &num_results); 100 | return (data != nullptr) ? static_cast(*data) : T(); 101 | } 102 | 103 | private: 104 | Window root_window_ = None; 105 | std::unordered_map atoms_; 106 | unsigned int desktop_ = 0; 107 | unsigned int num_desktops_ = 0; 108 | }; 109 | 110 | extern Server server; 111 | 112 | void SendEvent32(Window win, Atom at, long data1, long data2, long data3); 113 | int GetProperty32(Window win, Atom at, Atom type); 114 | 115 | // detect monitors and desktops 116 | void GetMonitors(); 117 | 118 | template 119 | util::x11::ClientData ServerGetProperty(Window win, Atom at, Atom type, 120 | int* num_results) { 121 | return server.GetProperty(win, at, type, num_results); 122 | } 123 | 124 | template 125 | T GetProperty32(Window win, Atom at, Atom type) { 126 | return server.GetProperty32(win, at, type); 127 | } 128 | 129 | #endif // TINT3_SERVER_HH 130 | -------------------------------------------------------------------------------- /src/startup_notification.cc: -------------------------------------------------------------------------------- 1 | #include "startup_notification.hh" 2 | 3 | #include 4 | #include 5 | 6 | StartupNotification::StartupNotification(SnDisplay* SN_MAYBE_UNUSED(dpy), 7 | unsigned int SN_MAYBE_UNUSED(screen)) { 8 | #ifdef HAVE_SN 9 | context_ = sn_launcher_context_new(dpy, screen); 10 | #endif // HAVE_SN 11 | } 12 | 13 | StartupNotification::StartupNotification( 14 | StartupNotification const& SN_MAYBE_UNUSED(other)) { 15 | #ifdef HAVE_SN 16 | context_ = other.context_; 17 | if (context_ != nullptr) { 18 | sn_launcher_context_ref(context_); 19 | } 20 | #endif // HAVE_SN 21 | } 22 | 23 | StartupNotification::~StartupNotification() { 24 | #ifdef HAVE_SN 25 | if (context_ != nullptr) { 26 | sn_launcher_context_unref(context_); 27 | } 28 | #endif // HAVE_SN 29 | } 30 | 31 | StartupNotification& StartupNotification::operator=(StartupNotification other) { 32 | #ifdef HAVE_SN 33 | std::swap(context_, other.context_); 34 | #endif // HAVE_SN 35 | return (*this); 36 | } 37 | 38 | SnLauncherContext* StartupNotification::context() const { 39 | #ifdef HAVE_SN 40 | return context_; 41 | #else 42 | return nullptr; 43 | #endif // HAVE_SN 44 | } 45 | 46 | void StartupNotification::set_name( 47 | std::string const& SN_MAYBE_UNUSED(name)) const { 48 | #ifdef HAVE_SN 49 | if (context_ != nullptr) { 50 | sn_launcher_context_set_name(context_, name.c_str()); 51 | } 52 | #endif // HAVE_SN 53 | } 54 | 55 | void StartupNotification::set_description( 56 | std::string const& SN_MAYBE_UNUSED(description)) const { 57 | #ifdef HAVE_SN 58 | if (context_ != nullptr) { 59 | sn_launcher_context_set_description(context_, description.c_str()); 60 | } 61 | #endif // HAVE_SN 62 | } 63 | 64 | void StartupNotification::IncrementRef() const { 65 | #ifdef HAVE_SN 66 | if (context_ != nullptr) { 67 | sn_launcher_context_ref(context_); 68 | } 69 | #endif // HAVE_SN 70 | } 71 | 72 | void StartupNotification::Initiate( 73 | std::string const& SN_MAYBE_UNUSED(binary_name), 74 | Time SN_MAYBE_UNUSED(time)) const { 75 | #ifdef HAVE_SN 76 | if (context_) { 77 | sn_launcher_context_set_binary_name(context_, binary_name.c_str()); 78 | sn_launcher_context_initiate(context_, "tint3", binary_name.c_str(), time); 79 | } 80 | #endif // HAVE_SN 81 | } 82 | 83 | void StartupNotification::SetupChildProcess() const { 84 | #ifdef HAVE_SN 85 | if (context_) { 86 | sn_launcher_context_setup_child_process(context_); 87 | } 88 | #endif // HAVE_SN 89 | } 90 | 91 | void StartupNotification::Complete() const { 92 | #ifdef HAVE_SN 93 | if (context_) { 94 | sn_launcher_context_complete(context_); 95 | } 96 | #endif // HAVE_SN 97 | } 98 | -------------------------------------------------------------------------------- /src/startup_notification.hh: -------------------------------------------------------------------------------- 1 | #ifndef TINT3_STARTUP_NOTIFICATION_H 2 | #define TINT3_STARTUP_NOTIFICATION_H 3 | 4 | #ifdef HAVE_SN 5 | #include 6 | #include 7 | #define SN_MAYBE_UNUSED(x) x 8 | #else 9 | using Time = unsigned long; 10 | using SnDisplay = void; 11 | using SnLauncherContext = void; 12 | #define SN_MAYBE_UNUSED(x) 13 | #endif // HAVE_SN 14 | 15 | #include 16 | 17 | class StartupNotification { 18 | public: 19 | StartupNotification() = default; 20 | StartupNotification(StartupNotification const& SN_MAYBE_UNUSED(other)); 21 | StartupNotification(StartupNotification&&) = default; 22 | StartupNotification(SnDisplay* SN_MAYBE_UNUSED(dpy), 23 | unsigned int SN_MAYBE_UNUSED(screen)); 24 | ~StartupNotification(); 25 | 26 | StartupNotification& operator=(StartupNotification other); 27 | 28 | SnLauncherContext* context() const; 29 | 30 | void set_name(std::string const& SN_MAYBE_UNUSED(name)) const; 31 | void set_description(std::string const& SN_MAYBE_UNUSED(description)) const; 32 | 33 | void IncrementRef() const; 34 | void Initiate(std::string const& SN_MAYBE_UNUSED(binary_name), 35 | Time SN_MAYBE_UNUSED(time)) const; 36 | void SetupChildProcess() const; 37 | void Complete() const; 38 | 39 | private: 40 | #ifdef HAVE_SN 41 | SnLauncherContext* context_ = nullptr; 42 | #endif // HAVE_SN 43 | }; 44 | 45 | #endif // TINT3_STARTUP_NOTIFICATION_H 46 | -------------------------------------------------------------------------------- /src/startup_notification_test.cc: -------------------------------------------------------------------------------- 1 | #include "catch.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #ifdef HAVE_SN 9 | #include 10 | #endif // HAVE_SN 11 | 12 | #include "absl/base/attributes.h" 13 | 14 | #include "startup_notification.hh" 15 | #include "util/common.hh" 16 | #include "util/environment.hh" 17 | 18 | namespace { 19 | 20 | SnDisplay* GetDisplay() { 21 | #ifdef HAVE_SN 22 | Display* dpy = XOpenDisplay(nullptr); 23 | if (!dpy) { 24 | FAIL("Couldn't connect to the X server on DISPLAY=" 25 | << environment::Get("DISPLAY")); 26 | } 27 | return sn_display_new(dpy, nullptr, nullptr); 28 | #else 29 | return nullptr; 30 | #endif // HAVE_SN 31 | } 32 | 33 | void CloseDisplay(SnDisplay* SN_MAYBE_UNUSED(sn_dpy)) { 34 | #ifdef HAVE_SN 35 | Display* dpy = sn_display_get_x_display(sn_dpy); 36 | sn_display_unref(sn_dpy); 37 | XCloseDisplay(dpy); 38 | #endif // HAVE_SN 39 | } 40 | 41 | } // namespace 42 | 43 | TEST_CASE("StartupNotification") { 44 | SnDisplay* sn_dpy = GetDisplay(); 45 | ABSL_ATTRIBUTE_UNUSED auto sn_dpy_deleter = 46 | util::MakeScopedCallback([=] { CloseDisplay(sn_dpy); }); 47 | 48 | StartupNotification sn{sn_dpy, 0}; 49 | sn.set_name("test"); 50 | sn.set_description("Simple startup notification for testing"); 51 | 52 | #ifdef HAVE_SN 53 | // not yet initiated 54 | REQUIRE_FALSE(sn_launcher_context_get_initiated(sn.context())); 55 | #endif // HAVE_SN 56 | sn.Initiate("startup_notification_test", CurrentTime); 57 | #ifdef HAVE_SN 58 | // initiated! 59 | REQUIRE(sn_launcher_context_get_initiated(sn.context())); 60 | #endif // HAVE_SN 61 | 62 | pid_t pid = fork(); 63 | if (pid < 0) { 64 | FAIL("fork() failed"); 65 | } 66 | 67 | if (pid == 0) { // child 68 | sn.IncrementRef(); 69 | sn.SetupChildProcess(); 70 | _exit(0); 71 | } 72 | 73 | // parent 74 | int status; 75 | waitpid(pid, &status, 0); 76 | if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { 77 | FAIL("abnormal termination of child process"); 78 | } 79 | sn.Complete(); 80 | } 81 | -------------------------------------------------------------------------------- /src/subprocess.cc: -------------------------------------------------------------------------------- 1 | #include "subprocess.hh" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "util/log.hh" 8 | 9 | void Subprocess::apply_options() { 10 | // Base case for the recursive template expansion in the header. 11 | // This one actually does nothing, it's only here to stop the recursion. 12 | } 13 | 14 | void Subprocess::set_option(capture&& option) { 15 | capture_ = std::move(option.value); 16 | } 17 | 18 | void Subprocess::set_option(child_callback&& option) { 19 | child_callback_ = std::move(option.value); 20 | } 21 | 22 | void Subprocess::set_option(session_leader&& option) { 23 | session_leader_ = std::move(option.value); 24 | } 25 | 26 | void Subprocess::set_option(shell&& option) { 27 | shell_ = std::move(option.value); 28 | } 29 | 30 | int Subprocess::start() { 31 | if (command_.empty()) { 32 | util::log::Error() << "Refusing to launch empty command\n"; 33 | return -1; 34 | } 35 | 36 | std::function child_callback_ptr = child_callback_; 37 | if (capture_) { 38 | stdout_.reset(new util::Pipe{util::Pipe::Options::kNonBlocking}); 39 | stderr_.reset(new util::Pipe{util::Pipe::Options::kNonBlocking}); 40 | child_callback_ptr = [&] { 41 | if (dup2(stdout_->WriteEnd(), STDOUT_FILENO) == -1 || 42 | dup2(stderr_->WriteEnd(), STDERR_FILENO) == -1) { 43 | util::log::Error() << "dup2: " << strerror(errno) << '\n'; 44 | _exit(1); 45 | } 46 | if (child_callback_) { 47 | child_callback_(); 48 | } 49 | }; 50 | } 51 | 52 | pid_t child_pid = fork(); 53 | if (child_pid < 0) { 54 | util::log::Error() << "fork: " << std::strerror(errno) << '\n'; 55 | return -1; 56 | } 57 | if (child_pid == 0) { 58 | if (child_callback_ptr) { 59 | child_callback_ptr(); 60 | } 61 | 62 | // Allow child to exist after parent destruction 63 | if (session_leader_) { 64 | setsid(); 65 | } 66 | 67 | if (shell_) { 68 | // "/bin/sh" should be guaranteed to be a POSIX-compliant shell 69 | // accepting the "-c" flag: 70 | // http://pubs.opengroup.org/onlinepubs/9699919799/utilities/sh.html 71 | execlp("/bin/sh", "sh", "-c", command_.c_str(), nullptr); 72 | 73 | // In case execlp() fails and the process image is not replaced 74 | util::log::Error() << "execlp(\"" << command_ 75 | << "\"): " << std::strerror(errno) << '\n'; 76 | _exit(1); 77 | } else { 78 | execl(command_.c_str(), command_.c_str(), nullptr); 79 | 80 | // In case execl() fails and the process image is not replaced 81 | util::log::Error() << "execl(\"" << command_ 82 | << "\"): " << std::strerror(errno) << '\n'; 83 | _exit(1); 84 | } 85 | } 86 | return child_pid; 87 | } 88 | 89 | bool Subprocess::communicate(std::ostream* out_ss, std::ostream* err_ss) const { 90 | static constexpr ssize_t kBufferSize = 1024; 91 | 92 | auto read_fully = [&](int fd, std::ostream& ss) -> bool { 93 | while (true) { 94 | char buffer[kBufferSize]; 95 | int ret = read(fd, buffer, (kBufferSize - 1) * sizeof(char)); 96 | if (ret <= 0) { 97 | if (ret == -1 && errno != EAGAIN) { 98 | util::log::Error() << "Failed reading from pipe " << fd << ": " 99 | << strerror(errno) << '\n'; 100 | return false; 101 | } 102 | return true; 103 | } 104 | buffer[ret] = '\0'; 105 | ss << buffer; 106 | } 107 | }; 108 | 109 | bool read_stdout = true; 110 | if (out_ss != nullptr) { 111 | read_stdout = read_fully(stdout_->ReadEnd(), *out_ss); 112 | } 113 | 114 | bool read_stderr = true; 115 | if (err_ss != nullptr) { 116 | read_stderr = read_fully(stderr_->ReadEnd(), *err_ss); 117 | } 118 | 119 | return read_stdout && read_stderr; 120 | } 121 | -------------------------------------------------------------------------------- /src/subprocess.hh: -------------------------------------------------------------------------------- 1 | #ifndef TINT3_SUBPROCESS_HH 2 | #define TINT3_SUBPROCESS_HH 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include "util/pipe.hh" 12 | 13 | struct capture { 14 | capture(bool capture) : value{capture} {} 15 | bool value = true; 16 | }; 17 | 18 | struct child_callback { 19 | child_callback(std::function child_callback) 20 | : value{child_callback} {} 21 | std::function value; 22 | }; 23 | 24 | struct session_leader { 25 | session_leader(bool session_leader) : value{session_leader} {} 26 | bool value = true; 27 | }; 28 | 29 | struct shell { 30 | shell(bool shell) : value{shell} {} 31 | bool value = true; 32 | }; 33 | 34 | class Subprocess { 35 | public: 36 | Subprocess(Subprocess const& other) = delete; 37 | Subprocess(Subprocess&& other) = default; 38 | 39 | void set_option(capture&& option); 40 | void set_option(child_callback&& option); 41 | void set_option(session_leader&& option); 42 | void set_option(shell&& option); 43 | 44 | pid_t start(); 45 | bool communicate(std::ostream* out_ss, std::ostream* err_ss) const; 46 | 47 | private: 48 | template 49 | friend Subprocess make_subprocess(std::string command, Args&&... args); 50 | 51 | template 52 | explicit Subprocess(std::string command, Args&&... args) : command_{command} { 53 | apply_options(args...); 54 | } 55 | 56 | void apply_options(); 57 | 58 | template 59 | void apply_options(T first_option, Args... rest) { 60 | set_option(std::forward(first_option)); 61 | apply_options(rest...); 62 | } 63 | 64 | bool capture_ = false; 65 | std::string command_; 66 | std::function child_callback_; 67 | bool shell_ = false; 68 | bool session_leader_ = false; 69 | 70 | std::unique_ptr stdout_; 71 | std::unique_ptr stderr_; 72 | }; 73 | 74 | template 75 | Subprocess make_subprocess(std::string command, Args&&... args) { 76 | return Subprocess{command, std::forward(args)...}; 77 | } 78 | 79 | // ShellExec executes a command through /bin/sh, invoking the provided callback 80 | // in the child process. 81 | template 82 | pid_t ShellExec(std::string const& command, Args&&... args) { 83 | auto sp = make_subprocess(command, session_leader{true}, shell{true}, 84 | std::forward(args)...); 85 | return sp.start(); 86 | } 87 | 88 | #endif // TINT3_SUBPROCESS_HH 89 | -------------------------------------------------------------------------------- /src/subprocess_test.cc: -------------------------------------------------------------------------------- 1 | #include "catch.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #include "subprocess.hh" 11 | 12 | TEST_CASE("make_subprocess") { 13 | auto exit_status = [](pid_t child_pid) { 14 | int status = 0; 15 | if (waitpid(child_pid, &status, 0) == -1) { 16 | FAIL("waitpid(): " << std::strerror(errno)); 17 | } 18 | if (!WIFEXITED(status)) { 19 | FAIL("not WIFEXITED()"); 20 | } 21 | return WEXITSTATUS(status); 22 | }; 23 | 24 | SECTION("empty command") { 25 | auto sp = make_subprocess(""); 26 | pid_t child_pid = sp.start(); 27 | REQUIRE(child_pid < 0); 28 | } 29 | 30 | SECTION("execution failure") { 31 | auto sp = make_subprocess("bogus command"); 32 | pid_t child_pid = sp.start(); 33 | REQUIRE(child_pid != -1); 34 | REQUIRE(exit_status(child_pid) != 0); 35 | } 36 | 37 | SECTION("default") { 38 | auto sp = make_subprocess("/bin/true"); 39 | pid_t child_pid = sp.start(); 40 | REQUIRE(child_pid != -1); 41 | REQUIRE(exit_status(child_pid) == 0); 42 | } 43 | 44 | SECTION("callback") { 45 | auto sp = make_subprocess("/bin/true", child_callback{[] { _exit(123); }}); 46 | pid_t child_pid = sp.start(); 47 | REQUIRE(child_pid != -1); 48 | REQUIRE(exit_status(child_pid) == 123); 49 | } 50 | 51 | SECTION("capture (no callback)") { 52 | auto sp = make_subprocess("printf stdout >&1; printf stderr >&2", 53 | capture{true}, shell{true}); 54 | pid_t child_pid = sp.start(); 55 | REQUIRE(child_pid != -1); 56 | REQUIRE(exit_status(child_pid) == 0); 57 | 58 | std::ostringstream stdout, stderr; 59 | REQUIRE(sp.communicate(&stdout, &stderr)); 60 | REQUIRE(stdout.str() == "stdout"); 61 | REQUIRE(stderr.str() == "stderr"); 62 | } 63 | 64 | SECTION("capture (with callback)") { 65 | auto sp = make_subprocess( 66 | "printf stdout >&1; printf stderr >&2; exit ${EXIT_STATUS}", 67 | capture{true}, child_callback{[] { setenv("EXIT_STATUS", "123", 1); }}, 68 | shell{true}); 69 | pid_t child_pid = sp.start(); 70 | REQUIRE(child_pid != -1); 71 | REQUIRE(exit_status(child_pid) == 123); 72 | 73 | std::ostringstream stdout, stderr; 74 | REQUIRE(sp.communicate(&stdout, &stderr)); 75 | REQUIRE(stdout.str() == "stdout"); 76 | REQUIRE(stderr.str() == "stderr"); 77 | } 78 | 79 | SECTION("shell") { 80 | auto sp = make_subprocess("[ true ] && echo \"Hooray! Todd episode!\"", 81 | shell{true}); 82 | pid_t child_pid = sp.start(); 83 | REQUIRE(child_pid != -1); 84 | REQUIRE(exit_status(child_pid) == 0); 85 | } 86 | 87 | SECTION("multiple options") { 88 | auto sp = make_subprocess("/bin/true", shell{false}, session_leader{true}); 89 | pid_t child_pid = sp.start(); 90 | REQUIRE(child_pid != -1); 91 | REQUIRE(exit_status(child_pid) == 0); 92 | } 93 | } 94 | 95 | TEST_CASE("ShellExec") { 96 | SECTION("empty command") { 97 | // doesn't make sense, should be refused 98 | REQUIRE(ShellExec("") < 0); 99 | } 100 | 101 | SECTION("bogus command") { 102 | // won't really do anything, but is a valid input, should fork() and exec*() 103 | REQUIRE(ShellExec("there is no such command") > 0); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/systray/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library( 2 | systraybar_lib STATIC 3 | systraybar.cc) 4 | 5 | target_link_libraries( 6 | systraybar_lib 7 | PRIVATE 8 | collection_lib 9 | log_lib 10 | panel_lib 11 | server_lib 12 | x11_lib 13 | absl::strings 14 | ${IMLIB2_LIBRARIES} 15 | ${X11_Xcomposite_LIB} 16 | ${X11_Xrender_LIB} 17 | PUBLIC 18 | area_lib 19 | common_lib 20 | timer_lib 21 | tray_window_lib 22 | ${X11_Xdamage_LIB}) 23 | 24 | add_library( 25 | tray_window_lib STATIC 26 | tray_window.cc) 27 | 28 | target_include_directories( 29 | tray_window_lib 30 | PUBLIC 31 | ${X11_X11_INCLUDE_DIRS} 32 | ${X11_Xdamage_INCLUDE_DIRS}) 33 | 34 | target_link_libraries( 35 | tray_window_lib 36 | PRIVATE 37 | server_lib 38 | PUBLIC 39 | timer_lib 40 | ${X11_X11_LIB} 41 | ${X11_Xdamage_LIB}) 42 | -------------------------------------------------------------------------------- /src/systray/systraybar.hh: -------------------------------------------------------------------------------- 1 | #ifndef TINT3_SYSTRAYBAR_SYSTRAYBAR_HH 2 | #define TINT3_SYSTRAYBAR_SYSTRAYBAR_HH 3 | 4 | #include 5 | 6 | #include 7 | 8 | #include "systray/tray_window.hh" 9 | #include "util/area.hh" 10 | #include "util/common.hh" 11 | #include "util/timer.hh" 12 | 13 | // XEMBED messages 14 | #define XEMBED_EMBEDDED_NOTIFY 0 15 | // Flags for _XEMBED_INFO 16 | #define XEMBED_MAPPED (1 << 0) 17 | 18 | class Systraybar : public Area { 19 | void RemoveIconInternal(TrayWindow* traywin, Timer& timer); 20 | 21 | public: 22 | int sort; 23 | int alpha, saturation, brightness; 24 | int icon_size, icons_per_column, icons_per_row, margin_; 25 | 26 | bool needs_true_color() const; 27 | bool should_refresh() const; 28 | void set_should_refresh(bool should_refresh); 29 | 30 | void SetParentPanel(Panel* panel); 31 | 32 | void DrawForeground(cairo_t*) override; 33 | void OnChangeLayout() override; 34 | bool Resize() override; 35 | 36 | size_t VisibleIcons() const; 37 | bool AddIcon(Window id); 38 | TrayWindow* FindTrayWindow(Window window_id); 39 | void RefreshIcons(Timer& timer); 40 | void RenderIcon(TrayWindow* traywin, Timer& timer); 41 | void RemoveIcon(TrayWindow* traywin, Timer& timer); 42 | void RemoveAllIcons(Timer& timer); 43 | void Clear(Timer& timer); 44 | 45 | // systray protocol 46 | // many tray icon don't manage stop/restart of the systray manager 47 | void StartNet(Timer& timer); 48 | void StopNet(Timer& timer); 49 | void NetMessage(XClientMessageEvent* e); 50 | 51 | #ifdef _TINT3_DEBUG 52 | 53 | std::string GetFriendlyName() const override; 54 | 55 | #endif // _TINT3_DEBUG 56 | 57 | private: 58 | bool should_refresh_; 59 | std::list list_icons_; 60 | }; 61 | 62 | // net_sel_win != None when protocol started 63 | extern Window net_sel_win; 64 | extern Systraybar systray; 65 | extern bool systray_enabled; 66 | extern int systray_max_icon_size; 67 | 68 | // default global data 69 | void DefaultSystray(); 70 | 71 | // freed memory 72 | void CleanupSystray(Timer& timer); 73 | 74 | // initialize protocol and panel position 75 | void InitSystray(Timer& timer); 76 | 77 | #endif // TINT3_SYSTRAYBAR_SYSTRAYBAR_HH 78 | -------------------------------------------------------------------------------- /src/systray/tray_window.cc: -------------------------------------------------------------------------------- 1 | #include "systray/tray_window.hh" 2 | 3 | #include "server.hh" 4 | 5 | TrayWindow::TrayWindow(Server* server, Window tray_id, Window child_id) 6 | : tray_id(tray_id), 7 | child_id(child_id), 8 | owned(false), 9 | x(0), 10 | y(0), 11 | width(0), 12 | height(0), 13 | hide(false), 14 | depth(0), 15 | damage(0), 16 | server_(server) {} 17 | 18 | TrayWindow::~TrayWindow() { 19 | XSelectInput(server_->dsp, child_id, NoEventMask); 20 | 21 | if (!hide) { 22 | XUnmapWindow(server_->dsp, child_id); 23 | } 24 | 25 | if (damage != None) { 26 | XDamageDestroy(server_->dsp, damage); 27 | } 28 | 29 | XReparentWindow(server_->dsp, child_id, server_->root_window(), 0, 0); 30 | XDestroyWindow(server_->dsp, tray_id); 31 | XSync(server_->dsp, False); 32 | } 33 | -------------------------------------------------------------------------------- /src/systray/tray_window.hh: -------------------------------------------------------------------------------- 1 | #ifndef TINT3_SYSTRAYBAR_TRAY_WINDOW_HH 2 | #define TINT3_SYSTRAYBAR_TRAY_WINDOW_HH 3 | 4 | #include 5 | #include 6 | 7 | #include "util/timer.hh" 8 | 9 | // forward declaration 10 | class Server; 11 | 12 | class TrayWindow { 13 | public: 14 | TrayWindow(Server* server, Window tray_id, Window child_id); 15 | ~TrayWindow(); 16 | 17 | Window tray_id; 18 | Window child_id; 19 | bool owned; 20 | int x, y; 21 | int width, height; 22 | // TODO: manage icon's show/hide 23 | bool hide; 24 | int depth; 25 | Damage damage; 26 | Interval::Id render_timeout; 27 | 28 | private: 29 | Server* server_; 30 | }; 31 | 32 | #endif // TINT3_SYSTRAYBAR_TRAY_WINDOW_HH 33 | -------------------------------------------------------------------------------- /src/taskbar/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library( 2 | task_lib STATIC 3 | task.cc) 4 | 5 | target_include_directories( 6 | task_lib 7 | PRIVATE 8 | ${IMLIB2_INCLUDE_DIRS} 9 | ${X11_X11_INCLUDE_DIRS}) 10 | 11 | target_link_libraries( 12 | task_lib 13 | PRIVATE 14 | collection_lib 15 | log_lib 16 | panel_lib 17 | server_lib 18 | taskbar_lib 19 | tooltip_lib 20 | window_lib 21 | PUBLIC 22 | area_lib 23 | common_lib 24 | pango_lib 25 | timer_lib 26 | x11_lib 27 | ${IMLIB2_LIBRARIES} 28 | ${PANGOCAIRO_LIBRARIES} 29 | ${X11_X11_LIB}) 30 | 31 | add_library( 32 | taskbar_lib STATIC 33 | taskbar.cc) 34 | 35 | target_include_directories( 36 | taskbar_lib 37 | PRIVATE 38 | ${IMLIB2_INCLUDE_DIRS} 39 | ${X11_X11_INCLUDE_DIRS}) 40 | 41 | target_link_libraries( 42 | taskbar_lib 43 | PRIVATE 44 | collection_lib 45 | log_lib 46 | panel_lib 47 | server_lib 48 | tooltip_lib 49 | window_lib 50 | PUBLIC 51 | task_lib 52 | taskbarbase_lib 53 | taskbarname_lib 54 | ${X11_X11_LIB}) 55 | 56 | add_library( 57 | taskbarbase_lib STATIC 58 | taskbarbase.cc) 59 | 60 | target_link_libraries( 61 | taskbarbase_lib 62 | PRIVATE 63 | server_lib 64 | PUBLIC 65 | area_lib 66 | x11_lib) 67 | 68 | add_library( 69 | taskbarname_lib STATIC 70 | taskbarname.cc) 71 | 72 | target_include_directories( 73 | taskbarname_lib 74 | PRIVATE 75 | ${IMLIB2_INCLUDE_DIRS} 76 | ${X11_X11_INCLUDE_DIRS}) 77 | 78 | target_link_libraries( 79 | taskbarname_lib 80 | PRIVATE 81 | panel_lib 82 | server_lib 83 | taskbar_lib 84 | window_lib 85 | PUBLIC 86 | area_lib 87 | common_lib 88 | pango_lib 89 | taskbarbase_lib 90 | ${PANGOCAIRO_LIBRARIES}) 91 | -------------------------------------------------------------------------------- /src/taskbar/task.hh: -------------------------------------------------------------------------------- 1 | #ifndef TINT3_TASKBAR_TASK_HH 2 | #define TINT3_TASKBAR_TASK_HH 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "util/area.hh" 10 | #include "util/common.hh" 11 | #include "util/pango.hh" 12 | #include "util/timer.hh" 13 | #include "util/x11.hh" 14 | 15 | enum TaskState { 16 | kTaskNormal, 17 | kTaskActive, 18 | kTaskIconified, 19 | kTaskUrgent, 20 | kTaskStateCount 21 | }; 22 | 23 | // -------------------------------------------------- 24 | // global task parameter 25 | class Global_task : public Area { 26 | public: 27 | Global_task(); 28 | 29 | bool text; 30 | bool icon; 31 | bool centered; 32 | 33 | int icon_posy; 34 | int icon_size1; 35 | int maximum_width; 36 | int maximum_height; 37 | int alpha[kTaskStateCount]; 38 | int saturation[kTaskStateCount]; 39 | int brightness[kTaskStateCount]; 40 | int config_asb_mask; 41 | Background background[kTaskStateCount]; 42 | int config_background_mask; 43 | // starting position for text ~ task_padding + task_border + icon_size 44 | double text_posx, text_height; 45 | 46 | bool font_shadow; 47 | util::pango::FontDescriptionPtr font_desc; 48 | Color font[kTaskStateCount]; 49 | int config_font_mask; 50 | bool tooltip_enabled; 51 | }; 52 | 53 | // TODO: make this inherit from a common base class that exposes state_pixmap 54 | class Task : public Area { 55 | public: 56 | explicit Task(Timer& timer); 57 | 58 | // TODO: group task with list of windows here 59 | Window win; 60 | unsigned int desktop; 61 | int current_state; 62 | util::imlib2::Image icon[kTaskStateCount]; 63 | util::imlib2::Image icon_hover[kTaskStateCount]; 64 | util::imlib2::Image icon_pressed[kTaskStateCount]; 65 | util::x11::Pixmap state_pix[kTaskStateCount]; 66 | unsigned int icon_width; 67 | unsigned int icon_height; 68 | int urgent_tick; 69 | 70 | void DrawForeground(cairo_t* c) override; 71 | std::string GetTooltipText() override; 72 | bool UpdateTitle(); // TODO: find a more descriptive name 73 | std::string GetTitle() const; 74 | void SetTitle(std::string const& title); 75 | void SetState(int state); 76 | void OnChangeLayout() override; 77 | Task& SetTooltipEnabled(bool); 78 | 79 | void AddUrgent(); 80 | void DelUrgent(); 81 | 82 | #ifdef _TINT3_DEBUG 83 | 84 | std::string GetFriendlyName() const override; 85 | 86 | #endif // _TINT3_DEBUG 87 | 88 | private: 89 | bool tooltip_enabled_; 90 | std::string title_; 91 | Timer& timer_; 92 | 93 | void DrawIcon(int); 94 | }; 95 | 96 | extern Interval::Id urgent_timeout; 97 | extern std::list urgent_list; 98 | 99 | Task* AddTask(Window win, Timer& timer); 100 | void RemoveTask(Task* tsk); 101 | 102 | void GetIcon(Task* tsk); 103 | void ActiveTask(); 104 | void SetTaskRedraw(Task* tsk); 105 | 106 | Task* FindActiveTask(Task* current_task, Task* active_task); 107 | Task* NextTask(Task* tsk); 108 | Task* PreviousTask(Task* tsk); 109 | 110 | #endif // TINT3_TASKBAR_TASK_HH 111 | -------------------------------------------------------------------------------- /src/taskbar/taskbar.hh: -------------------------------------------------------------------------------- 1 | #ifndef TINT3_TASKBAR_TASKBAR_HH 2 | #define TINT3_TASKBAR_TASKBAR_HH 3 | 4 | #include 5 | #include 6 | 7 | #include "task.hh" 8 | #include "taskbarbase.hh" 9 | #include "taskbarname.hh" 10 | 11 | using TaskPtrArray = std::vector; 12 | using WindowToTaskMap = std::unordered_map; 13 | extern WindowToTaskMap win_to_task_map; 14 | 15 | extern Task* task_active; 16 | extern Task* task_drag; 17 | extern bool taskbar_enabled; 18 | 19 | // tint3 uses one taskbar per desktop. 20 | class Taskbar : public TaskbarBase { 21 | public: 22 | unsigned int desktop; 23 | int text_width_; 24 | Taskbarname bar_name; 25 | 26 | Taskbar& SetState(size_t state); 27 | void DrawForeground(cairo_t*) override; 28 | void OnChangeLayout() override; 29 | bool Resize() override; 30 | 31 | util::iterator_range::iterator> filtered_children(); 32 | bool RemoveChild(Area* child) override; 33 | 34 | static void InitPanel(Panel* panel); 35 | 36 | #ifdef _TINT3_DEBUG 37 | 38 | std::string GetFriendlyName() const override; 39 | 40 | #endif // _TINT3_DEBUG 41 | }; 42 | 43 | class Global_taskbar : public Taskbar { 44 | public: 45 | Global_taskbar(); 46 | 47 | std::vector background; 48 | std::vector background_name; 49 | }; 50 | 51 | // default global data 52 | void DefaultTaskbar(); 53 | 54 | // free memory 55 | void CleanupTaskbar(); 56 | 57 | void InitTaskbar(); 58 | 59 | void TaskbarRemoveTask(Window win); 60 | Task* TaskGetTask(Window win); 61 | TaskPtrArray TaskGetTasks(Window win); 62 | void TaskRefreshTasklist(Timer& timer); 63 | 64 | #endif // TINT3_TASKBAR_TASKBAR_HH 65 | -------------------------------------------------------------------------------- /src/taskbar/taskbarbase.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "server.hh" 4 | #include "taskbarbase.hh" 5 | 6 | util::x11::Pixmap TaskbarBase::state_pixmap(size_t i) const { 7 | return state_pixmap_[i]; 8 | } 9 | 10 | TaskbarBase& TaskbarBase::set_state_pixmap(size_t i, 11 | util::x11::Pixmap const& value) { 12 | state_pixmap_[i] = value; 13 | return (*this); 14 | } 15 | 16 | TaskbarBase& TaskbarBase::reset_state_pixmap(size_t i) { 17 | state_pixmap_[i] = {}; 18 | return (*this); 19 | } 20 | -------------------------------------------------------------------------------- /src/taskbar/taskbarbase.hh: -------------------------------------------------------------------------------- 1 | #ifndef TINT3_TASKBAR_TASKBARBASE_HH 2 | #define TINT3_TASKBAR_TASKBARBASE_HH 3 | 4 | #include 5 | 6 | #include "util/area.hh" 7 | #include "util/x11.hh" 8 | 9 | enum TaskbarState { kTaskbarNormal, kTaskbarActive, kTaskbarCount }; 10 | 11 | class TaskbarBase : public Area { 12 | public: 13 | util::x11::Pixmap state_pixmap(size_t i) const; 14 | TaskbarBase& set_state_pixmap(size_t i, util::x11::Pixmap const& value); 15 | TaskbarBase& reset_state_pixmap(size_t i); 16 | 17 | private: 18 | util::x11::Pixmap state_pixmap_[kTaskbarCount]; 19 | }; 20 | 21 | #endif // TINT3_TASKBAR_TASKBARBASE_HH 22 | -------------------------------------------------------------------------------- /src/taskbar/taskbarname.hh: -------------------------------------------------------------------------------- 1 | #ifndef TINT3_TASKBAR_TASKBARNAME_HH 2 | #define TINT3_TASKBAR_TASKBARNAME_HH 3 | 4 | #include "taskbar/taskbarbase.hh" 5 | #include "util/area.hh" 6 | #include "util/common.hh" 7 | #include "util/pango.hh" 8 | 9 | extern bool taskbarname_enabled; 10 | extern util::pango::FontDescriptionPtr taskbarname_font_desc; 11 | extern Color taskbarname_font; 12 | extern Color taskbarname_active_font; 13 | 14 | class Taskbarname : public TaskbarBase { 15 | std::string name_; 16 | 17 | public: 18 | std::string const& name() const; 19 | Taskbarname& set_name(std::string const& name); 20 | 21 | void DrawForeground(cairo_t*) override; 22 | bool Resize() override; 23 | 24 | static void Default(); 25 | static void Cleanup(); 26 | static void InitPanel(Panel* panel); 27 | }; 28 | 29 | #endif // TINT3_TASKBAR_TASKBARNAME_HH 30 | -------------------------------------------------------------------------------- /src/theme_manager.hh: -------------------------------------------------------------------------------- 1 | #ifndef TINT3_THEME_MANAGER_HH 2 | 3 | #include "util/collection.hh" 4 | 5 | #include "json.hpp" 6 | using nlohmann::json; 7 | 8 | struct ThemeInfo { 9 | ThemeInfo() = delete; 10 | ThemeInfo(std::string author, std::string name, unsigned int version); 11 | 12 | std::string ToString() const; 13 | bool operator==(ThemeInfo const& other) const; 14 | bool operator<(ThemeInfo const& other) const; 15 | 16 | const std::string author; 17 | const std::string name; 18 | const unsigned int version; 19 | }; 20 | 21 | class Repository { 22 | public: 23 | Repository() = delete; 24 | 25 | static Repository FromJSON(std::string const& content); 26 | 27 | std::string Dump() const; 28 | 29 | void AddTheme(ThemeInfo const& theme_info); 30 | 31 | template 32 | bool RemoveMatchingThemes(Matcher&& match, Confirmation&& confirm_deletion) { 33 | bool found_any = false; 34 | 35 | for (auto& entry : repository_) { 36 | auto matching_theme = [&](json const& theme) { 37 | ThemeInfo theme_info{entry["author"], theme["name"], theme["version"]}; 38 | if (!match(theme_info)) return false; 39 | 40 | found_any = true; 41 | return confirm_deletion(theme_info); 42 | }; 43 | 44 | auto& themes = entry["themes"]; 45 | erase_if(themes, matching_theme); 46 | if (themes.empty()) entry.erase("themes"); 47 | } 48 | 49 | static auto has_no_themes = [](json const& entry) { 50 | return entry.find("themes") == entry.end(); 51 | }; 52 | erase_if(repository_, has_no_themes); 53 | 54 | return found_any; 55 | } 56 | 57 | template 58 | void ForEach(Callback&& callback) const { 59 | for (auto& entry : repository_) { 60 | for (auto& theme : entry["themes"]) { 61 | ThemeInfo theme_info{entry["author"], theme["name"], theme["version"]}; 62 | callback(theme_info); 63 | } 64 | } 65 | } 66 | 67 | private: 68 | explicit Repository(std::string const& content); 69 | 70 | json repository_; 71 | }; 72 | 73 | int ThemeManager(int argc, char* argv[]); 74 | 75 | #endif // TINT3_THEME_MANAGER_HH 76 | -------------------------------------------------------------------------------- /src/tooltip/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library( 2 | tooltip_lib STATIC 3 | tooltip.cc) 4 | 5 | target_include_directories( 6 | tooltip_lib 7 | PUBLIC 8 | ${CAIRO_INCLUDE_DIRS}) 9 | 10 | target_link_libraries( 11 | tooltip_lib 12 | PRIVATE 13 | common_lib 14 | log_lib 15 | panel_lib 16 | window_lib 17 | PUBLIC 18 | area_lib 19 | color_lib 20 | pango_lib 21 | server_lib 22 | timer_lib 23 | ${CAIRO_LIBRARIES} 24 | ${PANGOCAIRO_LIBRARIES}) 25 | 26 | test_target( 27 | tooltip_test 28 | SOURCES 29 | tooltip_test.cc 30 | INCLUDE_DIRS 31 | ${X11_X11_INCLUDE_DIRS} 32 | LINK_LIBRARIES 33 | area_lib 34 | environment_lib 35 | panel_lib 36 | server_lib 37 | testmain 38 | timer_test_utils_lib 39 | tooltip_lib 40 | ${X11_X11_LIB} 41 | USE_XVFB_RUN) 42 | -------------------------------------------------------------------------------- /src/tooltip/tooltip.hh: -------------------------------------------------------------------------------- 1 | #ifndef TINT3_TOOLTIP_TOOLTIP_HH 2 | #define TINT3_TOOLTIP_TOOLTIP_HH 3 | 4 | #include 5 | 6 | #include 7 | 8 | #include "server.hh" 9 | #include "util/area.hh" 10 | #include "util/color.hh" 11 | #include "util/pango.hh" 12 | #include "util/timer.hh" 13 | 14 | struct TooltipConfig { 15 | Background bg; 16 | Color font_color = Color{Color::Array{1.0, 1.0, 1.0}, 1.0}; 17 | util::pango::FontDescriptionPtr font_desc; 18 | int paddingx = 0; 19 | int paddingy = 0; 20 | unsigned int show_timeout_msec = 0; 21 | unsigned int hide_timeout_msec = 0; 22 | }; 23 | 24 | extern TooltipConfig tooltip_config; 25 | 26 | class Tooltip { 27 | public: 28 | Tooltip(Server* server, Timer* timer); 29 | Tooltip() = delete; 30 | Tooltip(Tooltip const&) = delete; 31 | Tooltip(Tooltip&&) = delete; 32 | ~Tooltip(); 33 | 34 | // Getter: returns the Window associated with this Tooltip. 35 | Window window() const; 36 | 37 | // IsBound tells whether the tooltip is bound to any Area. 38 | bool IsBound() const; 39 | 40 | // IsBoundTo tells whether the tooltip is bound to the given Area. 41 | bool IsBoundTo(Area const* area) const; 42 | 43 | // Show triggers the show tooltip timeout, which maps the tooltip window on 44 | // the screen and calls out to Update(). 45 | void Show(Area const* area, XEvent const* e, std::string text); 46 | 47 | // Update binds the tooltip to given Area, resizes and redraws the tooltip 48 | // window with the given text. 49 | void Update(Area const* area, XEvent const* e, std::string const& text); 50 | 51 | // Hide triggers the hide tooltip timeout, which unbinds the tooltip from the 52 | // Area and unmaps the tooltip window from the screen. 53 | void Hide(); 54 | 55 | private: 56 | Server* server_; 57 | Timer* timer_; 58 | Area const* area_; 59 | util::pango::FontDescriptionPtr font_desc_; 60 | Window window_; 61 | Interval::Id timeout_; 62 | 63 | void GetExtents(std::string const& text, int* x, int* y, int* width, 64 | int* height); 65 | void DrawBackground(cairo_t* c, int width, int height); 66 | void DrawBorder(cairo_t* c, int width, int height); 67 | void DrawText(cairo_t* c, int width, int height, std::string const& text); 68 | }; 69 | 70 | #endif // TINT3_TOOLTIP_TOOLTIP_HH 71 | -------------------------------------------------------------------------------- /src/unix_features.hh.in: -------------------------------------------------------------------------------- 1 | #ifndef TINT3_UNIX_FEATURES_HH 2 | #define TINT3_UNIX_FEATURES_HH 3 | 4 | #cmakedefine TINT3_HAVE_SHM_OPEN 5 | 6 | #endif // TINT3_UNIX_FEATURES_HH 7 | -------------------------------------------------------------------------------- /src/util/bimap_test.cc: -------------------------------------------------------------------------------- 1 | #include "catch.hpp" 2 | 3 | #include 4 | #include 5 | #include "util/bimap.hh" 6 | 7 | TEST_CASE("insert", "Insertion and lookup both work") { 8 | util::bimap bimap; 9 | 10 | bimap.insert(1, "test"); 11 | REQUIRE(bimap.size() == 1); 12 | 13 | bimap.insert(2, "toast"); 14 | REQUIRE(bimap.size() == 2); 15 | 16 | REQUIRE(bimap.left.has(1)); 17 | REQUIRE(bimap.right.has("test")); 18 | REQUIRE(bimap.left.has(2)); 19 | REQUIRE(bimap.right.has("toast")); 20 | } 21 | 22 | TEST_CASE("erase", "Removal works as expected") { 23 | util::bimap bimap; 24 | bimap.insert(1, "test"); 25 | bimap.insert(2, "toast"); 26 | 27 | REQUIRE(bimap.left.erase(1)); 28 | REQUIRE_FALSE(bimap.left.has(1)); 29 | REQUIRE_FALSE(bimap.right.has("test")); 30 | 31 | REQUIRE(bimap.right.erase("toast")); 32 | REQUIRE_FALSE(bimap.left.has(2)); 33 | REQUIRE_FALSE(bimap.right.has("toast")); 34 | 35 | REQUIRE(bimap.empty()); 36 | } 37 | 38 | TEST_CASE("const_iterator", "Iterating works from both sides") { 39 | util::bimap bimap; 40 | std::vector> expected{ 41 | {1, "test"}, {2, "toast"}, 42 | }; 43 | 44 | for (auto& p : expected) { 45 | bimap.insert(p.first, p.second); 46 | } 47 | 48 | SECTION("left iteration") { 49 | auto it = bimap.left.begin(); 50 | REQUIRE(it != bimap.left.end()); 51 | for (auto& p : expected) { 52 | REQUIRE(it->first == p.first); 53 | REQUIRE(it->second == p.second); 54 | ++it; 55 | } 56 | } 57 | 58 | SECTION("left iteration with range-based for loop") { 59 | unsigned int i = 0; 60 | for (auto& p : bimap.left) { 61 | REQUIRE(p.first == expected[i].first); 62 | REQUIRE(p.second == expected[i].second); 63 | ++i; 64 | } 65 | REQUIRE(i == expected.size()); 66 | } 67 | 68 | SECTION("right iteration") { 69 | auto it = bimap.right.begin(); 70 | REQUIRE(it != bimap.right.end()); 71 | for (auto& p : expected) { 72 | REQUIRE(it->second == p.first); 73 | REQUIRE(it->first == p.second); 74 | ++it; 75 | } 76 | } 77 | 78 | SECTION("right iteration with range-based for loop") { 79 | unsigned int i = 0; 80 | for (auto& p : bimap.right) { 81 | REQUIRE(p.first == expected[i].second); 82 | REQUIRE(p.second == expected[i].first); 83 | ++i; 84 | } 85 | REQUIRE(i == expected.size()); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/util/collection.hh: -------------------------------------------------------------------------------- 1 | #ifndef TINT3_UTIL_COLLECTION_HH 2 | #define TINT3_UTIL_COLLECTION_HH 3 | 4 | #include 5 | #include 6 | 7 | template 8 | typename T::iterator erase(T& container, V const& value) { 9 | return container.erase( 10 | std::remove(std::begin(container), std::end(container), value), 11 | std::end(container)); 12 | } 13 | 14 | template 15 | typename T::iterator erase_if(T& container, P unary_predicate) { 16 | return container.erase( 17 | std::remove_if(std::begin(container), std::end(container), unary_predicate), 18 | std::end(container)); 19 | } 20 | 21 | #endif // TINT3_UTIL_COLLECTION_HH 22 | -------------------------------------------------------------------------------- /src/util/collection_test.cc: -------------------------------------------------------------------------------- 1 | #include "catch.hpp" 2 | 3 | #include "util/collection.hh" 4 | 5 | TEST_CASE("erase") { 6 | std::vector container = {1, 2, 2, 3, 3, 3, 4, 5}; 7 | auto original_size = container.size(); 8 | 9 | SECTION("no matches") { 10 | erase(container, 42) == container.end(); 11 | REQUIRE(container.size() == original_size); 12 | } 13 | 14 | SECTION("single match") { 15 | erase(container, 1) != container.end(); 16 | REQUIRE(container.size() == original_size - 1); 17 | } 18 | 19 | SECTION("multiple matches") { 20 | erase(container, 2) != container.end(); 21 | REQUIRE(container.size() == original_size - 2); 22 | } 23 | } 24 | 25 | TEST_CASE("erase_if") { 26 | std::vector container = {1, 2, 2, 3, 3, 3, 4, 5}; 27 | auto original_size = container.size(); 28 | 29 | SECTION("no matches") { 30 | erase_if(container, [](int) { return false; }) == container.end(); 31 | REQUIRE(container.size() == original_size); 32 | } 33 | 34 | SECTION("single match") { 35 | erase_if(container, [](int x) { return x >= 5; }) != container.end(); 36 | REQUIRE(container.size() == original_size - 1); 37 | } 38 | 39 | SECTION("multiple matches") { 40 | erase_if(container, [](int x) { return x >= 4; }) != container.end(); 41 | REQUIRE(container.size() == original_size - 2); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/util/color_test.cc: -------------------------------------------------------------------------------- 1 | #include "catch.hpp" 2 | 3 | #include 4 | 5 | #include "behavior_control.hh" 6 | #include "util/color.hh" 7 | 8 | TEST_CASE("Color", "SetColorFromHexString") { 9 | Color c; 10 | 11 | SECTION("Malformed expressions get rejected") { 12 | REQUIRE_FALSE(c.SetColorFromHexString("oh hai")); 13 | REQUIRE_FALSE(c.SetColorFromHexString("ff007f")); 14 | } 15 | 16 | SECTION("Three-digit values are parsed correctly") { 17 | REQUIRE(c.SetColorFromHexString("#fff")); 18 | REQUIRE(c[0] == 1.0); 19 | REQUIRE(c[1] == 1.0); 20 | REQUIRE(c[2] == 1.0); 21 | } 22 | 23 | SECTION("Black is black") { 24 | REQUIRE(c.SetColorFromHexString("#000000")); 25 | REQUIRE(c[0] == 0.0); 26 | REQUIRE(c[1] == 0.0); 27 | REQUIRE(c[2] == 0.0); 28 | } 29 | 30 | SECTION("Gray is gray") { 31 | REQUIRE(c.SetColorFromHexString("#666666")); 32 | REQUIRE(c[0] == 0.4); 33 | REQUIRE(c[1] == 0.4); 34 | REQUIRE(c[2] == 0.4); 35 | } 36 | 37 | SECTION("White is white") { 38 | REQUIRE(c.SetColorFromHexString("#FFFFFF")); 39 | REQUIRE(c[0] == 1.0); 40 | REQUIRE(c[1] == 1.0); 41 | REQUIRE(c[2] == 1.0); 42 | } 43 | 44 | SECTION("Mixed case also works") { 45 | REQUIRE(c.SetColorFromHexString("#fFffFf")); 46 | REQUIRE(c[0] == 1.0); 47 | REQUIRE(c[1] == 1.0); 48 | REQUIRE(c[2] == 1.0); 49 | } 50 | } 51 | 52 | TEST_CASE("Border", "Copying") { 53 | Border b1; 54 | b1.set_color(Color{{{0.25, 0.50, 1.00}}, 0.75}); 55 | 56 | Border b2{b1}; 57 | REQUIRE(b2 == b1); 58 | 59 | FORCE_STD_MOVE(Border b3{std::move(b1)}); 60 | // b1 was moved at this point, check against b2 which was guaranteed to be 61 | // equal to b1 by the above check 62 | REQUIRE(b3 == b2); 63 | } 64 | 65 | TEST_CASE("Border::set_mask") { 66 | // Defaults to BORDER_ALL 67 | Border b; 68 | REQUIRE(b.mask() == BORDER_ALL); 69 | 70 | // Filters out bits not in the BORDER_ALL mask 71 | b.set_mask(std::numeric_limits::max()); 72 | REQUIRE(b.mask() == BORDER_ALL); 73 | } 74 | 75 | TEST_CASE("Border::width_for_side") { 76 | // Defaults to BORDER_ALL 77 | Border b; 78 | b.set_width(2); 79 | REQUIRE(b.width() == 2); 80 | REQUIRE(b.width_for_side(BORDER_TOP) == 2); 81 | REQUIRE(b.width_for_side(BORDER_RIGHT) == 2); 82 | REQUIRE(b.width_for_side(BORDER_BOTTOM) == 2); 83 | REQUIRE(b.width_for_side(BORDER_LEFT) == 2); 84 | 85 | // Limit it to a single side 86 | b.set_mask(BORDER_LEFT); 87 | REQUIRE(b.width() == 2); 88 | REQUIRE(b.width_for_side(BORDER_TOP) == 0); 89 | REQUIRE(b.width_for_side(BORDER_RIGHT) == 0); 90 | REQUIRE(b.width_for_side(BORDER_BOTTOM) == 0); 91 | REQUIRE(b.width_for_side(BORDER_LEFT) == 2); 92 | } 93 | 94 | TEST_CASE("Border::GetInnerAreaRect") { 95 | // Default: no border 96 | Border b; 97 | REQUIRE(b.GetInnerAreaRect(100, 100) == util::Rect({0, 0, 100, 100})); 98 | 99 | // 2px border, on all sides 100 | b.set_width(2); 101 | REQUIRE(b.GetInnerAreaRect(100, 100) == util::Rect({2, 2, 96, 96})); 102 | 103 | // 2px border, only on the top and left sides 104 | b.set_mask(BORDER_TOP | BORDER_LEFT); 105 | REQUIRE(b.GetInnerAreaRect(100, 100) == util::Rect({2, 2, 98, 98})); 106 | } 107 | -------------------------------------------------------------------------------- /src/util/common.hh: -------------------------------------------------------------------------------- 1 | #ifndef TINT3_UTIL_COMMON_HH 2 | #define TINT3_UTIL_COMMON_HH 3 | 4 | #define WM_CLASS_TINT "panel" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "absl/strings/string_view.h" 20 | 21 | #include "util/log.hh" 22 | 23 | // forward declaration 24 | class Server; 25 | 26 | namespace util { 27 | 28 | struct GObjectUnrefDeleter { 29 | void operator()(gpointer data) const; 30 | }; 31 | 32 | template 33 | using GObjectPtr = std::unique_ptr; 34 | 35 | // ScopedCallback is a class that receives a callable and invokes it when it 36 | // goes out of scope. 37 | template 38 | struct ScopedCallback { 39 | explicit ScopedCallback(T callback) : callback_{callback} {} 40 | ~ScopedCallback() { callback_(); } 41 | T callback_; 42 | }; 43 | 44 | // MakeScopedCallback is a helper method to create a ScopedCallback of the right 45 | // type from the argument it's passed. 46 | template 47 | ScopedCallback MakeScopedCallback(T callback) { 48 | return ScopedCallback{callback}; 49 | } 50 | 51 | template 52 | class iterator_range { 53 | public: 54 | using iterator = It_; 55 | using const_iterator = It_; 56 | 57 | iterator_range() = delete; 58 | iterator_range(iterator_range const&) = default; 59 | iterator_range(iterator_range&) = default; 60 | 61 | iterator_range(It_ begin, It_ end) : begin_{begin}, end_{end} {} 62 | It_ begin() const { return begin_; } 63 | It_ end() const { return end_; } 64 | 65 | iterator_range& operator=(iterator_range other) { 66 | std::swap(begin_, other.begin_); 67 | std::swap(end_, other.end_); 68 | return (*this); 69 | } 70 | 71 | private: 72 | It_ begin_; 73 | It_ end_; 74 | }; 75 | 76 | template 77 | iterator_range make_iterator_range(It_ begin, It_ end) { 78 | return iterator_range{begin, end}; 79 | } 80 | 81 | template 82 | iterator_range range_skip_n(T& container, size_t offset) { 83 | return make_iterator_range(container.begin() + offset, container.end()); 84 | } 85 | 86 | namespace string { 87 | 88 | template 89 | std::string Representation(T const& value) { 90 | std::ostringstream ss; 91 | ss << value; 92 | return ss.str(); 93 | } 94 | 95 | bool ToNumber(absl::string_view str, int* ptr); 96 | bool ToNumber(absl::string_view str, long* ptr); 97 | bool ToNumber(absl::string_view str, float* ptr); 98 | 99 | bool RegexMatch(std::string const& pattern, std::string const& string); 100 | 101 | } // namespace string 102 | } // namespace util 103 | 104 | // mouse actions 105 | enum class MouseAction { 106 | kNone, 107 | kClose, 108 | kToggle, 109 | kIconify, 110 | kShade, 111 | kToggleIconify, 112 | kMaximizeRestore, 113 | kMaximize, 114 | kRestore, 115 | kDesktopLeft, 116 | kDesktopRight, 117 | kNextTask, 118 | kPrevTask 119 | }; 120 | 121 | extern const unsigned int kAllDesktops; 122 | 123 | bool SignalAction(int signal_number, void signal_handler(int), int flags = 0); 124 | 125 | // adjust Alpha/Saturation/Brightness on an ARGB icon 126 | // alpha from 0 to 100, satur from 0 to 1, bright from 0 to 1. 127 | void AdjustASB(DATA32* data, unsigned int w, unsigned int h, int alpha, 128 | float saturation_adjustment, float brightness_adjustment); 129 | void CreateHeuristicMask(DATA32* data, int w, int h); 130 | 131 | void RenderImage(Server* server, Drawable drawable, Imlib_Image image, int x, 132 | int y); 133 | 134 | std::ostream& operator<<(std::ostream& os, std::nullptr_t); 135 | 136 | #endif // TINT3_UTIL_COMMON_HH 137 | -------------------------------------------------------------------------------- /src/util/environment.cc: -------------------------------------------------------------------------------- 1 | #include "util/environment.hh" 2 | 3 | namespace environment { 4 | 5 | std::string Get(std::string const& key) { 6 | char* value = getenv(key.c_str()); 7 | if (value != nullptr) { 8 | return value; 9 | } 10 | return std::string{}; 11 | } 12 | 13 | bool Unset(std::string const& key) { 14 | if (unsetenv(key.c_str()) != 0) { 15 | util::log::Error() << "unsetenv(): " << std::strerror(errno) << '\n'; 16 | return false; 17 | } 18 | return true; 19 | } 20 | 21 | } // namespace environment 22 | -------------------------------------------------------------------------------- /src/util/environment.hh: -------------------------------------------------------------------------------- 1 | #ifndef TINT3_UTIL_ENVIRONMENT_HH 2 | #define TINT3_UTIL_ENVIRONMENT_HH 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "util/common.hh" 9 | #include "util/log.hh" 10 | 11 | namespace environment { 12 | 13 | std::string Get(std::string const& key); 14 | bool Unset(std::string const& key); 15 | 16 | template 17 | bool Set(std::string const& key, T const& value) { 18 | std::string value_string = util::string::Representation(value); 19 | if (setenv(key.c_str(), value_string.c_str(), 1) != 0) { 20 | util::log::Error() << "setenv(): " << std::strerror(errno) << '\n'; 21 | return false; 22 | } 23 | return true; 24 | } 25 | 26 | template 27 | class ScopedOverride { 28 | public: 29 | ScopedOverride(std::string const& key, T const& value) 30 | : key_(key), original_value_(getenv(key.c_str())) { 31 | Set(key, util::string::Representation(value)); 32 | } 33 | 34 | ~ScopedOverride() { 35 | if (original_value_ != nullptr) { 36 | setenv(key_.c_str(), original_value_, 1); 37 | } else { 38 | unsetenv(key_.c_str()); 39 | } 40 | } 41 | 42 | private: 43 | std::string key_; 44 | const char* original_value_; 45 | }; 46 | 47 | template 48 | ScopedOverride MakeScopedOverride(std::string const& key, T const& value) { 49 | return ScopedOverride(key, value); 50 | } 51 | 52 | } // namespace environment 53 | 54 | #endif // TINT3_UTIL_ENVIRONMENT_HH 55 | -------------------------------------------------------------------------------- /src/util/environment_test.cc: -------------------------------------------------------------------------------- 1 | #include "catch.hpp" 2 | 3 | #include 4 | #include 5 | #include "util/environment.hh" 6 | 7 | TEST_CASE("Get", "Reading from the environment is sane") { 8 | setenv("__BOGUS_NAME__", "__BOGUS_VALUE__", 1); 9 | REQUIRE(environment::Get("__BOGUS_NAME__") == "__BOGUS_VALUE__"); 10 | 11 | unsetenv("__BOGUS_NAME__"); 12 | REQUIRE(environment::Get("__BOGUS_NAME__").empty()); 13 | } 14 | 15 | TEST_CASE("Set", "Writing to the environment works") { 16 | // Fails on invalid key. 17 | REQUIRE_FALSE(environment::Set("", "__BOGUS_VALUE__")); 18 | 19 | REQUIRE(getenv("__BOGUS_NAME__") == nullptr); 20 | REQUIRE(environment::Set("__BOGUS_NAME__", "__BOGUS_VALUE__")); 21 | REQUIRE(std::strcmp(getenv("__BOGUS_NAME__"), "__BOGUS_VALUE__") == 0); 22 | } 23 | 24 | TEST_CASE("Unset", "Deleting from the environment works") { 25 | // Fails on invalid key. 26 | REQUIRE_FALSE(environment::Unset("")); 27 | 28 | setenv("__BOGUS_NAME__", "__BOGUS_VALUE__", 1); 29 | REQUIRE(std::strcmp(getenv("__BOGUS_NAME__"), "__BOGUS_VALUE__") == 0); 30 | REQUIRE(environment::Unset("__BOGUS_NAME__")); 31 | REQUIRE(getenv("__BOGUS_NAME__") == nullptr); 32 | 33 | // No-op when removing an already deleted key name. 34 | REQUIRE(environment::Unset("__BOGUS_NAME__")); 35 | } 36 | 37 | TEST_CASE("ScopedEnvironmentOverride", 38 | "Temporary environment overrides work as expected") { 39 | SECTION("An pre-existing variable gets overwritten, then restored") { 40 | setenv("__BOGUS_NAME__", "__BOGUS_VALUE__", 1); 41 | REQUIRE(std::strcmp(getenv("__BOGUS_NAME__"), "__BOGUS_VALUE__") == 0); 42 | 43 | { 44 | auto new_value = 45 | environment::MakeScopedOverride("__BOGUS_NAME__", "__NEW_VALUE__"); 46 | REQUIRE(std::strcmp(getenv("__BOGUS_NAME__"), "__NEW_VALUE__") == 0); 47 | } 48 | 49 | REQUIRE(std::strcmp(getenv("__BOGUS_NAME__"), "__BOGUS_VALUE__") == 0); 50 | } 51 | 52 | SECTION("A non-existing variable gets set, then unset") { 53 | unsetenv("__BOGUS_NAME__"); 54 | REQUIRE(getenv("__BOGUS_NAME__") == nullptr); 55 | 56 | { 57 | auto new_value = 58 | environment::MakeScopedOverride("__BOGUS_NAME__", "__NEW_VALUE__"); 59 | REQUIRE(std::strcmp(getenv("__BOGUS_NAME__"), "__NEW_VALUE__") == 0); 60 | } 61 | 62 | REQUIRE(getenv("__BOGUS_NAME__") == nullptr); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/util/fs.hh: -------------------------------------------------------------------------------- 1 | #ifndef TINT3_UTIL_FS_HH 2 | #define TINT3_UTIL_FS_HH 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "absl/strings/string_view.h" 14 | 15 | namespace util { 16 | namespace fs { 17 | 18 | class SystemInterface { 19 | public: 20 | virtual bool stat(std::string const& name, struct stat* buf); 21 | virtual bool symlink(std::string const& target, std::string const& linkpath); 22 | virtual bool unlink(std::string const& name); 23 | }; 24 | 25 | // Replaces the current system interface with the new one, and returns a 26 | // pointer to the old one. 27 | SystemInterface* SetSystemInterface(SystemInterface* interface); 28 | 29 | class DirectoryContents { 30 | public: 31 | explicit DirectoryContents(std::string const& path); 32 | ~DirectoryContents(); 33 | 34 | class iterator { 35 | public: 36 | friend class DirectoryContents; 37 | 38 | iterator& operator++(); 39 | std::string const operator*() const; 40 | bool operator!=(iterator const& other) const; 41 | 42 | private: 43 | iterator(); 44 | iterator(DIR* dir); 45 | 46 | DIR* dir_; 47 | struct dirent* entry_; 48 | long pos_; 49 | }; 50 | 51 | iterator const begin() const; 52 | iterator const end() const; 53 | 54 | private: 55 | DIR* dir_; 56 | }; 57 | 58 | class Path { 59 | public: 60 | friend std::ostream& operator<<(std::ostream& os, Path const& path); 61 | 62 | Path() = default; 63 | Path(Path const& other) = default; 64 | Path(Path&& other) = default; 65 | Path(const char* path); 66 | Path(std::string const& path); 67 | Path(absl::string_view path); 68 | 69 | Path& operator=(Path other); 70 | Path operator/(absl::string_view component); 71 | Path& operator/=(absl::string_view component); 72 | operator std::string() const; 73 | 74 | // Returns the base name of the current path (the last component in the path). 75 | // If the path is '/', it's returned as is. 76 | // If the path doesn't contain any slash, the path itself is returned. 77 | std::string BaseName() const; 78 | 79 | // Returns the logical path of the parent directory of the current path. 80 | // This method doesn't differentiate a current path pointing to a file from 81 | // one pointing to a directory. 82 | // If the path is '/', it's returned as is. 83 | // If the path doesn't contain any slash, '.' is returned. 84 | Path DirectoryName() const; 85 | 86 | bool operator==(Path const& other) const; 87 | 88 | private: 89 | std::string path_; 90 | }; 91 | 92 | std::ostream& operator<<(std::ostream& os, Path const& path); 93 | 94 | std::string BuildPath(std::initializer_list parts); 95 | bool CopyFile(std::string const& from_path, std::string const& to_path); 96 | bool CreateDirectory(std::string const& path, mode_t mode = 0700); 97 | bool DirectoryExists(std::string const& path); 98 | bool FileExists(std::string const& path); 99 | bool FileExists(std::initializer_list parts); 100 | bool IsSymbolicLink(std::string const& path); 101 | Path HomeDirectory(); 102 | bool WriteFile(std::string const& path, absl::string_view content); 103 | bool ReadFile(std::string const& path, std::string* output); 104 | bool ReadFile(std::string const& path, 105 | std::function const& fn); 106 | bool ReadFileByLine(std::string const& path, 107 | std::function const& fn); 108 | bool SymbolicLink(std::string const& target, std::string const& linkpath); 109 | bool Unlink(std::string const& path); 110 | 111 | } // namespace fs 112 | } // namespace util 113 | 114 | #endif // TINT3_UTIL_FS_HH 115 | -------------------------------------------------------------------------------- /src/util/fs_test_utils.cc: -------------------------------------------------------------------------------- 1 | #include "util/fs_test_utils.hh" 2 | 3 | bool FakeFileSystemInterface::stat(std::string const& path, struct stat* buf) { 4 | auto response = stat_responses.find(path); 5 | if (response == stat_responses.end()) return false; 6 | if (response->second.empty()) return false; 7 | 8 | *buf = response->second.front(); 9 | response->second.pop_front(); 10 | return true; 11 | } 12 | 13 | bool FakeFileSystemInterface::symlink(std::string const& target, 14 | std::string const& linkpath) { 15 | auto response = symlink_responses.find({target, linkpath}); 16 | if (response == symlink_responses.end()) return false; 17 | if (response->second.empty()) return false; 18 | 19 | bool result = response->second.front(); 20 | response->second.pop_front(); 21 | return result; 22 | } 23 | 24 | bool FakeFileSystemInterface::unlink(std::string const& path) { 25 | auto response = unlink_responses.find(path); 26 | if (response == unlink_responses.end()) return false; 27 | if (response->second.empty()) return false; 28 | 29 | bool result = response->second.front(); 30 | response->second.pop_front(); 31 | return result; 32 | } 33 | -------------------------------------------------------------------------------- /src/util/fs_test_utils.hh: -------------------------------------------------------------------------------- 1 | #ifndef TINT3_UTIL_FS_TEST_UTILS_HH 2 | #define TINT3_UTIL_FS_TEST_UTILS_HH 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "util/fs.hh" 14 | 15 | namespace std { 16 | 17 | template 18 | struct hash> { 19 | size_t operator()(std::pair const& p) const { 20 | size_t lhs = std::hash()(p.first); 21 | size_t rhs = std::hash()(p.second); 22 | lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2); 23 | return lhs; 24 | }; 25 | }; 26 | 27 | } // namespace std 28 | 29 | struct FakeFileSystemInterface : public util::fs::SystemInterface { 30 | bool stat(std::string const& path, struct stat* buf); 31 | std::unordered_map> stat_responses; 32 | 33 | bool symlink(std::string const& target, std::string const& linkpath); 34 | std::unordered_map, std::list> 35 | symlink_responses; 36 | 37 | bool unlink(std::string const& path); 38 | std::unordered_map> unlink_responses; 39 | }; 40 | 41 | #endif // TINT3_UTIL_FS_TEST_UTILS_HH 42 | -------------------------------------------------------------------------------- /src/util/geometry.cc: -------------------------------------------------------------------------------- 1 | #include "util/geometry.hh" 2 | 3 | namespace util { 4 | 5 | Rect::Rect(int x, int y, unsigned int w, unsigned int h) 6 | : tl_(std::make_pair(x, y)), br_(std::make_pair(x + w, y + h)) {} 7 | 8 | bool Rect::operator==(Rect const& other) const { 9 | return tl_ == other.tl_ && br_ == other.br_; 10 | } 11 | 12 | bool Rect::Contains(Rect const& other) { 13 | bool top_left_smaller = 14 | tl_.first <= other.tl_.first && tl_.second <= other.tl_.second; 15 | bool bottom_right_bigger = 16 | br_.first >= other.br_.first && br_.second >= other.br_.second; 17 | return top_left_smaller && bottom_right_bigger; 18 | } 19 | 20 | void Rect::ExpandBy(unsigned int p) { 21 | tl_ = std::make_pair(tl_.first - p, tl_.second - p); 22 | br_ = std::make_pair(br_.first + p, br_.second + p); 23 | } 24 | 25 | bool Rect::ShrinkBy(unsigned int p) { 26 | if (br_.first - tl_.first < static_cast(2 * p) || 27 | br_.second - tl_.second < static_cast(2 * p)) { 28 | return false; 29 | } 30 | 31 | tl_ = std::make_pair(tl_.first + p, tl_.second + p); 32 | br_ = std::make_pair(br_.first - p, br_.second - p); 33 | return true; 34 | } 35 | 36 | Point Rect::top_left() const { return tl_; } 37 | 38 | Point Rect::bottom_right() const { return br_; } 39 | 40 | } // namespace util 41 | -------------------------------------------------------------------------------- /src/util/geometry.hh: -------------------------------------------------------------------------------- 1 | #ifndef TINT3_UTIL_GEOMETRY_HH 2 | #define TINT3_UTIL_GEOMETRY_HH 3 | 4 | #include 5 | 6 | namespace util { 7 | 8 | using Point = std::pair; 9 | 10 | class Rect { 11 | public: 12 | Rect() = delete; 13 | 14 | // Tests equality of two Rect objects. 15 | // To be considered equal, they must have the same size *and* position. 16 | bool operator==(Rect const& other) const; 17 | 18 | // Creates a new rectangle from the given coordinates. 19 | // The created rectangle strecthes from (x; y) to (x + w; y + h). 20 | Rect(int x, int y, unsigned int w, unsigned int h); 21 | 22 | // Tells if the other rectangle is contained in this one. 23 | bool Contains(Rect const& other); 24 | 25 | // Expands the rectangle in all directions by the given amount of pixels. 26 | void ExpandBy(unsigned int p); 27 | 28 | // Shrinks the rectangle in all directions by the given amount of pixels. 29 | // Returns a boolean indicating whether shrinking succeded or failed (which 30 | // can happen when the rectangle is smaller than 2*p in either direction). 31 | bool ShrinkBy(unsigned int p); 32 | 33 | // Returns the top-left vertex. 34 | Point top_left() const; 35 | 36 | // Returns the bottom-right vertex. 37 | Point bottom_right() const; 38 | 39 | private: 40 | Point tl_; 41 | Point br_; 42 | }; 43 | 44 | } // namespace util 45 | 46 | #endif // TINT3_UTIL_GEOMETRY_HH 47 | -------------------------------------------------------------------------------- /src/util/geometry_test.cc: -------------------------------------------------------------------------------- 1 | #include "catch.hpp" 2 | 3 | #include 4 | 5 | #include "util/geometry.hh" 6 | 7 | TEST_CASE("Rect", "Come on, I can get at least this one right") { 8 | util::Rect r{100, 100, 50, 100}; 9 | 10 | SECTION("Constructor") { 11 | REQUIRE(r.top_left() == std::make_pair(100, 100)); 12 | REQUIRE(r.bottom_right() == std::make_pair(150, 200)); 13 | } 14 | 15 | SECTION("operator==()") { 16 | util::Rect a{0, 0, 20, 20}; 17 | REQUIRE(a == a); 18 | 19 | util::Rect b{0, 0, 20, 20}; 20 | REQUIRE(a == b); 21 | 22 | util::Rect c{10, 10, 30, 30}; 23 | REQUIRE_FALSE(a == c); 24 | } 25 | 26 | SECTION("Contains") { 27 | util::Rect inside{100, 100, 20, 20}; 28 | REQUIRE(r.Contains(inside)); 29 | 30 | util::Rect outside_left{99, 100, 20, 20}; 31 | REQUIRE_FALSE(r.Contains(outside_left)); 32 | 33 | util::Rect outside_top{100, 99, 20, 20}; 34 | REQUIRE_FALSE(r.Contains(outside_top)); 35 | 36 | util::Rect outside_right{131, 100, 20, 20}; 37 | REQUIRE_FALSE(r.Contains(outside_right)); 38 | 39 | util::Rect outside_bottom{100, 181, 20, 20}; 40 | REQUIRE_FALSE(r.Contains(outside_bottom)); 41 | } 42 | 43 | SECTION("ExpandBy") { 44 | r.ExpandBy(50); 45 | REQUIRE(r.top_left() == std::make_pair(50, 50)); 46 | REQUIRE(r.bottom_right() == std::make_pair(200, 250)); 47 | } 48 | 49 | SECTION("ShrinkBy") { 50 | // First iteration: shrinks by 10px 51 | REQUIRE(r.ShrinkBy(10)); 52 | REQUIRE(r.top_left() == std::make_pair(110, 110)); 53 | REQUIRE(r.bottom_right() == std::make_pair(140, 190)); 54 | 55 | // Second iteration: shrinks by 10px 56 | REQUIRE(r.ShrinkBy(10)); 57 | REQUIRE(r.top_left() == std::make_pair(120, 120)); 58 | REQUIRE(r.bottom_right() == std::make_pair(130, 180)); 59 | 60 | // Third iteration: can't shrink anymore, doesn't change dimensions 61 | REQUIRE_FALSE(r.ShrinkBy(10)); 62 | REQUIRE(r.top_left() == std::make_pair(120, 120)); 63 | REQUIRE(r.bottom_right() == std::make_pair(130, 180)); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/util/gradient.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "util/gradient.hh" 5 | #include "util/log.hh" 6 | 7 | namespace util { 8 | 9 | Gradient::Gradient(GradientKind kind) : kind_(kind) {} 10 | 11 | void Gradient::set_start_color(Color color) { start_color_ = color; } 12 | 13 | void Gradient::set_end_color(Color color) { end_color_ = color; } 14 | 15 | bool Gradient::AddColorStop(unsigned short stop_percent, Color color) { 16 | if (stop_percent == 0 || stop_percent >= 100) { 17 | return false; 18 | } 19 | return color_stops_.emplace(stop_percent, color).second; 20 | } 21 | 22 | void Gradient::Draw(cairo_t* c, Rect const& r) { 23 | // Values for radial gradients. Center at (cx, cy), diameter equals d. 24 | auto cx = (r.bottom_right().first + r.top_left().first) / 2; 25 | auto cy = (r.bottom_right().second + r.top_left().second) / 2; 26 | auto d = std::min(r.bottom_right().first - r.top_left().first, 27 | r.bottom_right().second - r.top_left().second); 28 | 29 | cairo_pattern_t* pat = nullptr; 30 | 31 | switch (kind_) { 32 | case GradientKind::kVertical: 33 | pat = cairo_pattern_create_linear(r.top_left().first, r.top_left().second, 34 | r.top_left().first, 35 | r.bottom_right().second); 36 | cairo_rectangle(c, r.top_left().first, r.top_left().second, 37 | r.bottom_right().first, r.bottom_right().second); 38 | break; 39 | 40 | case GradientKind::kHorizontal: 41 | pat = cairo_pattern_create_linear(r.top_left().first, r.top_left().second, 42 | r.bottom_right().first, 43 | r.top_left().second); 44 | cairo_rectangle(c, r.top_left().first, r.top_left().second, 45 | r.bottom_right().first, r.bottom_right().second); 46 | break; 47 | 48 | case GradientKind::kRadial: 49 | pat = cairo_pattern_create_radial(cx, cy, 0, cx, cy, d / 2); 50 | cairo_arc(c, cx, cy, d / 2, 0, 2 * M_PI); 51 | break; 52 | } 53 | 54 | if (cairo_pattern_status(pat) != CAIRO_STATUS_SUCCESS) { 55 | util::log::Error() << "cairo_pattern_create_*() failed\n"; 56 | return; 57 | } 58 | 59 | cairo_pattern_add_color_stop_rgba(pat, 0.0, start_color_[0], start_color_[1], 60 | start_color_[2], start_color_.alpha()); 61 | for (auto const& it : color_stops_) { 62 | cairo_pattern_add_color_stop_rgba(pat, it.first / 100.0, it.second[0], 63 | it.second[1], it.second[2], 64 | it.second.alpha()); 65 | } 66 | cairo_pattern_add_color_stop_rgba(pat, 1.0, end_color_[0], end_color_[1], 67 | end_color_[2], end_color_.alpha()); 68 | 69 | cairo_set_source(c, pat); 70 | cairo_fill(c); 71 | cairo_pattern_destroy(pat); 72 | } 73 | 74 | bool Gradient::operator==(Gradient const& other) const { 75 | return kind_ == other.kind_ && start_color_ == other.start_color_ && 76 | end_color_ == other.end_color_ && color_stops_ == other.color_stops_; 77 | } 78 | 79 | } // namespace util 80 | -------------------------------------------------------------------------------- /src/util/gradient.hh: -------------------------------------------------------------------------------- 1 | #ifndef TINT3_UTIL_GRADIENT_HH 2 | #define TINT3_UTIL_GRADIENT_HH 3 | 4 | #include 5 | 6 | #include 7 | 8 | #include "util/color.hh" 9 | #include "util/geometry.hh" 10 | 11 | namespace test { 12 | 13 | class GradientHelper; 14 | 15 | } // namespace test 16 | 17 | namespace util { 18 | 19 | enum class GradientKind { kVertical, kHorizontal, kRadial }; 20 | 21 | class Gradient { 22 | public: 23 | friend class test::GradientHelper; 24 | 25 | Gradient() = default; 26 | explicit Gradient(GradientKind kind); 27 | Gradient(Gradient&&) = default; 28 | 29 | Gradient(Gradient const&) = delete; 30 | Gradient& operator=(Gradient const&) = delete; 31 | 32 | void set_start_color(Color color); 33 | void set_end_color(Color color); 34 | bool AddColorStop(unsigned short stop_percent, Color color); 35 | 36 | void Draw(cairo_t* c, util::Rect const& r); 37 | 38 | bool operator==(Gradient const& other) const; 39 | 40 | private: 41 | GradientKind kind_ = GradientKind::kVertical; 42 | Color start_color_; 43 | Color end_color_; 44 | std::map color_stops_; 45 | }; 46 | 47 | } // namespace util 48 | 49 | #endif // TINT3_UTIL_GRADIENT_HH 50 | -------------------------------------------------------------------------------- /src/util/imlib2.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "util/common.hh" 5 | #include "util/imlib2.hh" 6 | 7 | namespace util { 8 | namespace imlib2 { 9 | 10 | namespace { 11 | 12 | class ScopedCurrentImageRestorer { 13 | public: 14 | ScopedCurrentImageRestorer() : image_(imlib_context_get_image()) {} 15 | ~ScopedCurrentImageRestorer() { imlib_context_set_image(image_); } 16 | 17 | private: 18 | Imlib_Image image_; 19 | }; 20 | 21 | Imlib_Image CloneImlib2Image(Imlib_Image other_image) { 22 | if (!other_image) { 23 | return nullptr; 24 | } 25 | ScopedCurrentImageRestorer restorer; 26 | imlib_context_set_image(other_image); 27 | return imlib_clone_image(); 28 | } 29 | 30 | } // namespace 31 | 32 | Image::Image(Imlib_Image image) : image_(image) {} 33 | 34 | Image::Image(Image const& other) : image_(CloneImlib2Image(other.image_)) {} 35 | 36 | Image::Image(Image&& other) : image_(std::move(other.image_)) {} 37 | 38 | Image::~Image() { Free(); } 39 | 40 | Image& Image::operator=(Image other) { 41 | std::swap(image_, other.image_); 42 | return (*this); 43 | } 44 | 45 | Image& Image::operator=(Imlib_Image image) { 46 | Free(); 47 | image_ = image; 48 | return (*this); 49 | } 50 | 51 | Image::operator Imlib_Image() const { return image_; } 52 | 53 | void Image::AdjustASB(int alpha, float saturation_adjustment, 54 | float brightness_adjustment) { 55 | if (image_ != nullptr) { 56 | ScopedCurrentImageRestorer restorer; 57 | imlib_context_set_image(image_); 58 | DATA32* data = imlib_image_get_data(); 59 | ::AdjustASB(data, imlib_image_get_width(), imlib_image_get_height(), alpha, 60 | saturation_adjustment, brightness_adjustment); 61 | imlib_image_put_back_data(data); 62 | } 63 | } 64 | 65 | void Image::Free() { 66 | if (image_ != nullptr) { 67 | ScopedCurrentImageRestorer restorer; 68 | imlib_context_set_image(image_); 69 | imlib_free_image(); 70 | image_ = nullptr; 71 | } 72 | } 73 | 74 | Image Image::CloneExisting(Imlib_Image other_image) { 75 | return Image{CloneImlib2Image(other_image)}; 76 | } 77 | 78 | } // namespace imlib2 79 | } // namespace util 80 | -------------------------------------------------------------------------------- /src/util/imlib2.hh: -------------------------------------------------------------------------------- 1 | #ifndef TINT3_UTIL_IMLIB2 2 | #define TINT3_UTIL_IMLIB2 3 | 4 | #include 5 | 6 | namespace util { 7 | namespace imlib2 { 8 | 9 | class Image { 10 | public: 11 | Image(Imlib_Image image = nullptr); 12 | Image(Image const& other); 13 | Image(Image&& other); 14 | ~Image(); 15 | 16 | Image& operator=(Image other); 17 | Image& operator=(Imlib_Image image); 18 | operator Imlib_Image() const; 19 | 20 | void AdjustASB(int alpha, float saturation_adjustment, 21 | float brightness_adjustment); 22 | void Free(); 23 | 24 | static Image CloneExisting(Imlib_Image other_image); 25 | 26 | private: 27 | Imlib_Image image_; 28 | }; 29 | 30 | } // namespace imlib2 31 | } // namespace util 32 | 33 | #endif // TINT3_UTIL_IMLIB2 34 | -------------------------------------------------------------------------------- /src/util/imlib2_test.cc: -------------------------------------------------------------------------------- 1 | #include "catch.hpp" 2 | 3 | #include 4 | #include 5 | 6 | #include "behavior_control.hh" 7 | #include "util/imlib2.hh" 8 | 9 | TEST_CASE("imlib2::Image::Image", "Construction/destruction") { 10 | util::imlib2::Image null_image; 11 | REQUIRE(null_image == nullptr); 12 | 13 | // AddressSanitizer should make sure imlib_empty_image gets freed by the 14 | // destructor and we're not leaking memory. 15 | Imlib_Image imlib_empty_image = imlib_create_image(100, 100); 16 | util::imlib2::Image empty_image{imlib_empty_image}; 17 | REQUIRE(empty_image != nullptr); 18 | REQUIRE(empty_image == imlib_empty_image); 19 | 20 | util::imlib2::Image copy_of_empty_image{empty_image}; 21 | REQUIRE(copy_of_empty_image != nullptr); 22 | REQUIRE(copy_of_empty_image != empty_image); 23 | 24 | // Lambda that returns a temporary util::imlib2::Image object, to test the 25 | // move constructor also works. 26 | auto new_imlib2_image = []() -> util::imlib2::Image { 27 | return imlib_create_image(100, 100); 28 | }; 29 | FORCE_STD_MOVE( 30 | util::imlib2::Image moved_image{std::move(new_imlib2_image())}); 31 | REQUIRE(moved_image != nullptr); 32 | } 33 | 34 | TEST_CASE("imlib2::Image::operator=", "Assignment") { 35 | // Assignment of an Imlib_Image object. 36 | Imlib_Image imlib_image1 = imlib_create_image(100, 100); 37 | util::imlib2::Image image1; 38 | REQUIRE(image1 == nullptr); 39 | image1 = imlib_image1; 40 | REQUIRE(image1 == imlib_image1); 41 | 42 | // Assignment of an util::imlib2::Image object. 43 | // We'll verify: 44 | // 1) the object wraps a non-null pointer; 45 | // 2) the object is no longer assigned to imlib_image1; 46 | // 3) the object has cloned the Imlib_Image from image2, so it doesn't wrap 47 | // the same pointer value. 48 | util::imlib2::Image image2{imlib_create_image(100, 100)}; 49 | image1 = image2; 50 | REQUIRE(image1 != nullptr); 51 | REQUIRE(image1 != imlib_image1); 52 | REQUIRE(image1 != image2); 53 | } 54 | 55 | TEST_CASE("imlib2::Image::AdjustASB", "Adjustment in place works") { 56 | // Prepare a 1x1 image to adjust. 57 | util::imlib2::Image image{imlib_create_image(1, 1)}; 58 | imlib_context_set_image(image); 59 | imlib_image_set_has_alpha(1); 60 | DATA32* original_data = imlib_image_get_data(); 61 | original_data[0] = 0xffc86464; // rgba(200, 100, 100, 1.0) 62 | imlib_image_put_back_data(original_data); 63 | 64 | image.AdjustASB(50, 0.0, +0.1); 65 | DATA32* adjusted_data = imlib_image_get_data(); 66 | REQUIRE(adjusted_data[0] == 0x7fe27171); // rgba(226, 113, 113, 0.5) 67 | REQUIRE(adjusted_data == original_data); // data was not reallocated 68 | } 69 | 70 | TEST_CASE("imlib2::Image::CloneExisting", 71 | "Returns an Image object holding a clone of the given Imlib_Image") { 72 | static constexpr unsigned int const width = 10; 73 | static constexpr unsigned int const height = 10; 74 | 75 | Imlib_Image original = imlib_create_image(width, height); 76 | util::imlib2::Image original_cleanup{original}; // to free the image 77 | 78 | imlib_context_set_image(original); 79 | DATA32* original_data = imlib_image_get_data(); 80 | 81 | std::random_device device; 82 | std::mt19937 generator{device()}; 83 | std::normal_distribution<> distribution{ 84 | std::numeric_limits::min(), 85 | std::numeric_limits::max()}; 86 | 87 | for (unsigned int i = 0; i < width * height; ++i) { 88 | original_data[i] = distribution(generator); 89 | } 90 | 91 | auto clone = util::imlib2::Image::CloneExisting(original); 92 | REQUIRE(clone != nullptr); 93 | REQUIRE(clone != original); 94 | 95 | imlib_context_set_image(clone); 96 | DATA32* clone_data = imlib_image_get_data(); 97 | 98 | for (unsigned int i = 0; i < width * height; ++i) { 99 | REQUIRE(clone_data[i] == original_data[i]); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/util/log.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "log.hh" 5 | 6 | namespace { 7 | 8 | const util::log::Logger::LoggerMode kDebugLogMode = 9 | #ifndef _TINT3_DEBUG 10 | util::log::Logger::kDisabled; 11 | #else 12 | util::log::Logger::kAutoFlush; 13 | #endif // _TINT3_DEBUG 14 | 15 | } // namespace 16 | 17 | namespace util { 18 | 19 | namespace log { 20 | 21 | Logger& Debug() { 22 | static std::ofstream stream("/tmp/tint3.dbg", 23 | std::ofstream::out | std::ofstream::app); 24 | static Logger debug(stream, kDebugLogMode); 25 | return debug; 26 | } 27 | 28 | Logger& Error() { 29 | static Logger error(std::cerr, util::log::Logger::kAutoFlush); 30 | return error; 31 | } 32 | 33 | } // namespace log 34 | 35 | } // namespace util 36 | -------------------------------------------------------------------------------- /src/util/log.hh: -------------------------------------------------------------------------------- 1 | #ifndef TINT3_UTIL_LOG_HH 2 | #define TINT3_UTIL_LOG_HH 3 | 4 | #include 5 | 6 | namespace util { 7 | namespace log { 8 | 9 | class Logger { 10 | friend Logger& Debug(); 11 | friend Logger& Error(); 12 | 13 | public: 14 | enum LoggerMode { 15 | kDefault = 0, 16 | kDisabled = 1, 17 | kAutoFlush = 2, 18 | }; 19 | 20 | private: 21 | std::ostream& stream_; 22 | LoggerMode mode_; 23 | 24 | Logger(std::ostream& stream, LoggerMode mode = kDefault) 25 | : stream_(stream), mode_(mode) {} 26 | 27 | public: 28 | template 29 | Logger& operator<<(V const& val) { 30 | if (!(mode_ & kDisabled)) { 31 | stream_ << val; 32 | 33 | if (mode_ & kAutoFlush) { 34 | stream_.flush(); 35 | } 36 | } 37 | 38 | return (*this); 39 | } 40 | }; 41 | 42 | Logger& Debug(); 43 | Logger& Error(); 44 | 45 | } // namespace log 46 | } // namespace util 47 | 48 | #endif // TINT3_UTIL_LOG_HH -------------------------------------------------------------------------------- /src/util/pango.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | namespace util { 7 | namespace pango { 8 | 9 | FontDescriptionPtr::FontDescriptionPtr() 10 | : font_description_(pango_font_description_from_string("sans 10")) {} 11 | 12 | FontDescriptionPtr::FontDescriptionPtr(PangoFontDescription* ptr) 13 | : font_description_(ptr) {} 14 | 15 | FontDescriptionPtr::FontDescriptionPtr(FontDescriptionPtr const& other) 16 | : font_description_(pango_font_description_copy(other.font_description_)) {} 17 | 18 | FontDescriptionPtr::FontDescriptionPtr(FontDescriptionPtr&& other) 19 | : font_description_(std::move(other.font_description_)) { 20 | other.font_description_ = nullptr; 21 | } 22 | 23 | FontDescriptionPtr::~FontDescriptionPtr() { 24 | if (font_description_) { 25 | pango_font_description_free(font_description_); 26 | } 27 | } 28 | 29 | FontDescriptionPtr& FontDescriptionPtr::operator=(FontDescriptionPtr other) { 30 | std::swap(font_description_, other.font_description_); 31 | return (*this); 32 | } 33 | 34 | FontDescriptionPtr& FontDescriptionPtr::operator=(PangoFontDescription* ptr) { 35 | if (font_description_ && font_description_ != ptr) { 36 | pango_font_description_free(font_description_); 37 | } 38 | font_description_ = ptr; 39 | return (*this); 40 | } 41 | 42 | PangoFontDescription* FontDescriptionPtr::operator()() const { 43 | return font_description_; 44 | } 45 | 46 | FontDescriptionPtr FontDescriptionPtr::FromPointer(PangoFontDescription* ptr) { 47 | return ptr; 48 | } 49 | 50 | } // namespace pango 51 | } // namespace util 52 | -------------------------------------------------------------------------------- /src/util/pango.hh: -------------------------------------------------------------------------------- 1 | #ifndef TINT3_UTIL_PANGO_HH 2 | #define TINT3_UTIL_PANGO_HH 3 | 4 | #include 5 | 6 | namespace util { 7 | namespace pango { 8 | 9 | class FontDescriptionPtr { 10 | public: 11 | FontDescriptionPtr(); 12 | FontDescriptionPtr(FontDescriptionPtr const& other); 13 | FontDescriptionPtr(FontDescriptionPtr&& other); 14 | ~FontDescriptionPtr(); 15 | 16 | FontDescriptionPtr& operator=(FontDescriptionPtr other); 17 | FontDescriptionPtr& operator=(PangoFontDescription* ptr); 18 | PangoFontDescription* operator()() const; 19 | 20 | static FontDescriptionPtr FromPointer(PangoFontDescription* ptr); 21 | 22 | private: 23 | FontDescriptionPtr(PangoFontDescription* ptr); 24 | 25 | PangoFontDescription* font_description_; 26 | }; 27 | 28 | } // namespace pango 29 | } // namespace util 30 | 31 | #endif // TINT3_UTIL_PANGO_HH 32 | -------------------------------------------------------------------------------- /src/util/pango_test.cc: -------------------------------------------------------------------------------- 1 | #include "catch.hpp" 2 | 3 | #include "util/pango.hh" 4 | 5 | TEST_CASE("FontDescriptionPtr") { 6 | SECTION("copy constructor") { 7 | util::pango::FontDescriptionPtr ptr1; 8 | util::pango::FontDescriptionPtr ptr2{ptr1}; 9 | REQUIRE(pango_font_description_equal(ptr1(), ptr2())); 10 | REQUIRE(ptr1() != ptr2()); 11 | } 12 | 13 | SECTION("move constructor") { 14 | util::pango::FontDescriptionPtr ptr1; 15 | util::pango::FontDescriptionPtr ptr2{std::move(ptr1)}; 16 | REQUIRE(ptr1() == nullptr); 17 | REQUIRE(ptr2() != nullptr); 18 | } 19 | 20 | SECTION("copy assignment") { 21 | // setup 22 | auto ptr1 = util::pango::FontDescriptionPtr::FromPointer( 23 | pango_font_description_from_string("sans 10")); 24 | auto ptr2 = util::pango::FontDescriptionPtr::FromPointer( 25 | pango_font_description_from_string("sans 8")); 26 | REQUIRE_FALSE(pango_font_description_equal(ptr1(), ptr2())); 27 | 28 | ptr1 = ptr2; 29 | REQUIRE(pango_font_description_equal(ptr1(), ptr2())); 30 | REQUIRE(ptr1() != ptr2()); 31 | 32 | PangoFontDescription* ptr1_previous = ptr1(); 33 | ptr1 = ptr2; 34 | REQUIRE(pango_font_description_equal(ptr1(), ptr2())); 35 | REQUIRE(ptr1() != ptr2()); 36 | REQUIRE(ptr1() != ptr1_previous); 37 | } 38 | 39 | SECTION("assignment to PangoFontDescription*") { 40 | // setup 41 | auto ptr = util::pango::FontDescriptionPtr::FromPointer( 42 | pango_font_description_from_string("sans 10")); 43 | PangoFontDescription* font = pango_font_description_from_string("sans 8"); 44 | REQUIRE_FALSE(pango_font_description_equal(ptr(), font)); 45 | 46 | // assign once 47 | ptr = font; 48 | REQUIRE(pango_font_description_equal(ptr(), font)); 49 | REQUIRE(ptr() == font); 50 | 51 | // assignment is idempotent 52 | ptr = font; 53 | REQUIRE(pango_font_description_equal(ptr(), font)); 54 | REQUIRE(ptr() == font); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/util/pipe.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | #include "util/log.hh" 8 | #include "util/pipe.hh" 9 | 10 | namespace util { 11 | 12 | Pipe::Pipe(Pipe::Options opts) : alive_{true} { 13 | if (pipe(pipe_fd_) != 0) { 14 | util::log::Error() << "Failed to create pipe: " << std::strerror(errno) 15 | << '\n'; 16 | alive_ = false; 17 | return; 18 | } 19 | 20 | if (opts == Options::kNonBlocking) { 21 | if (!SetFlag(ReadEnd(), O_NONBLOCK)) { 22 | alive_ = false; 23 | return; 24 | } 25 | if (!SetFlag(WriteEnd(), O_NONBLOCK)) { 26 | alive_ = false; 27 | return; 28 | } 29 | } 30 | } 31 | 32 | Pipe::Pipe(Pipe&& other) { 33 | alive_ = other.alive_; 34 | other.alive_ = false; 35 | 36 | pipe_fd_[0] = other.pipe_fd_[0]; 37 | other.pipe_fd_[0] = -1; 38 | 39 | pipe_fd_[1] = other.pipe_fd_[1]; 40 | other.pipe_fd_[1] = -1; 41 | } 42 | 43 | Pipe::~Pipe() { 44 | if (alive_) { 45 | close(pipe_fd_[0]); 46 | close(pipe_fd_[1]); 47 | } 48 | } 49 | 50 | bool Pipe::IsAlive() const { return alive_; } 51 | 52 | int Pipe::ReadEnd() const { return pipe_fd_[0]; } 53 | 54 | int Pipe::WriteEnd() const { return pipe_fd_[1]; } 55 | 56 | bool Pipe::SetFlag(int fd, int flag) const { 57 | int flags = fcntl(fd, F_GETFL); 58 | if (flags == -1) { 59 | util::log::Error() << "Failed to retrieve flags on read end of self pipe: " 60 | << std::strerror(errno) << '\n'; 61 | return false; 62 | } 63 | if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { 64 | util::log::Error() << "Failed to flag read end of self pipe " 65 | << "as non blocking: " << std::strerror(errno) << '\n'; 66 | return false; 67 | } 68 | return true; 69 | } 70 | 71 | SelfPipe::SelfPipe() : Pipe{Pipe::Options::kNonBlocking} {} 72 | 73 | void SelfPipe::WriteOneByte() { write(WriteEnd(), "1", 1); } 74 | 75 | void SelfPipe::ReadPendingBytes() { 76 | while (true) { 77 | char byte; 78 | int ret = read(ReadEnd(), &byte, 1); 79 | if (ret <= 0) { 80 | if (ret == -1 && errno != EAGAIN) { 81 | util::log::Error() << "Failed reading from self pipe: " 82 | << strerror(errno) << '\n'; 83 | } 84 | break; 85 | } 86 | } 87 | } 88 | 89 | } // namespace util 90 | -------------------------------------------------------------------------------- /src/util/pipe.hh: -------------------------------------------------------------------------------- 1 | #ifndef TINT3_UTIL_PIPE_HH 2 | #define TINT3_UTIL_PIPE_HH 3 | 4 | namespace test { 5 | 6 | class MockSelfPipe; 7 | 8 | } // namespace test 9 | 10 | namespace util { 11 | 12 | class Pipe { 13 | public: 14 | enum class Options { 15 | kNone, 16 | kNonBlocking, 17 | }; 18 | 19 | Pipe(Options opts = Options::kNone); 20 | Pipe(Pipe const&) = delete; 21 | Pipe(Pipe&& other); 22 | ~Pipe(); 23 | 24 | bool IsAlive() const; 25 | int ReadEnd() const; 26 | int WriteEnd() const; 27 | 28 | private: 29 | friend class test::MockSelfPipe; 30 | 31 | bool alive_; 32 | int pipe_fd_[2]; 33 | 34 | bool SetFlag(int fd, int flag) const; 35 | }; 36 | 37 | class SelfPipe : public Pipe { 38 | public: 39 | SelfPipe(); 40 | 41 | void WriteOneByte(); 42 | void ReadPendingBytes(); 43 | }; 44 | 45 | } // namespace util 46 | 47 | #endif // TINT3_UTIL_PIPE_HH 48 | -------------------------------------------------------------------------------- /src/util/testdata/fs_test.txt: -------------------------------------------------------------------------------- 1 | Name: cat 2 | State: R (running) 3 | Tgid: 24440 4 | Ngid: 0 5 | Pid: 24440 6 | PPid: 24352 7 | TracerPid: 0 8 | Uid: 1000 1000 1000 1000 9 | Gid: 1000 1000 1000 1000 10 | FDSize: 256 11 | Groups: 4 24 27 30 46 113 128 1000 12 | NStgid: 24440 13 | NSpid: 24440 14 | NSpgid: 24440 15 | NSsid: 24352 16 | VmPeak: 25336 kB 17 | VmSize: 25336 kB 18 | VmLck: 0 kB 19 | VmPin: 0 kB 20 | VmHWM: 832 kB 21 | VmRSS: 832 kB 22 | VmData: 324 kB 23 | VmStk: 136 kB 24 | VmExe: 48 kB 25 | VmLib: 1940 kB 26 | VmPTE: 40 kB 27 | VmPMD: 12 kB 28 | VmSwap: 0 kB 29 | HugetlbPages: 0 kB 30 | Threads: 1 31 | SigQ: 1/31190 32 | SigPnd: 0000000000000000 33 | ShdPnd: 0000000000000000 34 | SigBlk: 0000000000000000 35 | SigIgn: 0000000000000000 36 | SigCgt: 0000000000000000 37 | CapInh: 0000000000000000 38 | CapPrm: 0000000000000000 39 | CapEff: 0000000000000000 40 | CapBnd: 0000003fffffffff 41 | CapAmb: 0000000000000000 42 | Seccomp: 0 43 | Cpus_allowed: ff 44 | Cpus_allowed_list: 0-7 45 | Mems_allowed: 00000000,00000001 46 | Mems_allowed_list: 0 47 | voluntary_ctxt_switches: 0 48 | nonvoluntary_ctxt_switches: 1 49 | -------------------------------------------------------------------------------- /src/util/timer_test_utils.cc: -------------------------------------------------------------------------------- 1 | #include "util/timer_test_utils.hh" 2 | 3 | FakeClock::FakeClock(unsigned int seconds_from_epoch) 4 | : current_time_(absl::FromUnixSeconds(seconds_from_epoch)) {} 5 | 6 | absl::Time FakeClock::Now() const { return current_time_; } 7 | 8 | void FakeClock::AdvanceBy(absl::Duration amount) { current_time_ += amount; } -------------------------------------------------------------------------------- /src/util/timer_test_utils.hh: -------------------------------------------------------------------------------- 1 | #ifndef TINT3_UTIL_TIMER_TEST_UTILS_HH 2 | #define TINT3_UTIL_TIMER_TEST_UTILS_HH 3 | 4 | #include "absl/time/time.h" 5 | 6 | #include "util/timer.hh" 7 | 8 | class FakeClock : public Timer { 9 | public: 10 | FakeClock() = delete; 11 | FakeClock(FakeClock& other) = delete; 12 | 13 | explicit FakeClock(unsigned int seconds_from_epoch); 14 | 15 | absl::Time Now() const; 16 | void AdvanceBy(absl::Duration amount); 17 | 18 | private: 19 | absl::Time current_time_; 20 | }; 21 | 22 | #endif // TINT3_UTIL_TIMER_TEST_UTILS_HH -------------------------------------------------------------------------------- /src/util/window.hh: -------------------------------------------------------------------------------- 1 | #ifndef TINT3_UTIL_WINDOW_HH 2 | #define TINT3_UTIL_WINDOW_HH 3 | 4 | #include 5 | #include 6 | 7 | #include "util/pango.hh" 8 | 9 | namespace util { 10 | namespace window { 11 | 12 | void SetActive(Window win); 13 | void SetClose(Window win); 14 | bool IsIconified(Window win); 15 | bool IsUrgent(Window win); 16 | bool IsHidden(Window win); 17 | bool IsActive(Window win); 18 | bool IsSkipTaskbar(Window win); 19 | void MaximizeRestore(Window win); 20 | void ToggleShade(Window win); 21 | int GetDesktop(Window win); 22 | void SetDesktop(Window win, int desktop); 23 | unsigned int GetMonitor(Window win); 24 | Window GetActive(); 25 | 26 | } // namespace window 27 | } // namespace util 28 | 29 | void SetDesktop(int desktop); 30 | 31 | int GetIconCount(unsigned long* data, int num); 32 | unsigned long* GetBestIcon(unsigned long* data, int icon_count, int num, 33 | int* iw, int* ih, int best_icon_size); 34 | 35 | enum class MarkupTag { 36 | kNoMarkup, 37 | kHasMarkup, 38 | }; 39 | 40 | void GetTextSize(util::pango::FontDescriptionPtr const& font, 41 | std::string const& text, MarkupTag markup_tag, 42 | int* width, int* height); 43 | 44 | #endif // TINT3_UTIL_WINDOW_HH 45 | -------------------------------------------------------------------------------- /src/util/x11.hh: -------------------------------------------------------------------------------- 1 | #ifndef TINT3_UTIL_X11_HH 2 | #define TINT3_UTIL_X11_HH 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "util/pipe.hh" 14 | #include "util/timer.hh" 15 | 16 | extern int signal_pending; 17 | extern bool pending_children; 18 | 19 | class Server; 20 | 21 | namespace util { 22 | namespace x11 { 23 | 24 | class ScopedErrorHandler { 25 | public: 26 | explicit ScopedErrorHandler(XErrorHandler new_handler); 27 | ~ScopedErrorHandler(); 28 | 29 | private: 30 | XErrorHandler old_handler_; 31 | }; 32 | 33 | class XFreeDeleter { 34 | public: 35 | void operator()(void* data) const; 36 | }; 37 | 38 | template 39 | class ClientData : public std::unique_ptr { 40 | public: 41 | explicit ClientData(void* data) 42 | : std::unique_ptr(static_cast(data)) {} 43 | }; 44 | 45 | class Colormap { 46 | public: 47 | Colormap() = default; 48 | Colormap(Display* display, ::Colormap colormap); 49 | Colormap(Colormap const& other) = default; 50 | Colormap(Colormap&& other) = default; 51 | ~Colormap(); 52 | 53 | Colormap& operator=(Colormap other); 54 | operator ::Colormap() const; 55 | 56 | static Colormap DefaultForScreen(Display* display, int screen_number); 57 | static Colormap Create(Display* display, Window window, Visual* visual, 58 | int alloc); 59 | 60 | private: 61 | Display* display_ = nullptr; 62 | ::Colormap colormap_ = None; 63 | }; 64 | 65 | class Pixmap { 66 | public: 67 | Pixmap() = default; 68 | Pixmap(Display* display, ::Pixmap pixmap); 69 | Pixmap(Pixmap const& other) = default; 70 | Pixmap(Pixmap&& other) = default; 71 | ~Pixmap(); 72 | 73 | Pixmap& operator=(Pixmap other); 74 | operator ::Pixmap() const; 75 | 76 | static Pixmap Create(Display* display, Window window, unsigned int width, 77 | unsigned int height, unsigned int depth); 78 | 79 | private: 80 | Display* display_ = nullptr; 81 | ::Pixmap pixmap_ = None; 82 | }; 83 | 84 | class EventLoop { 85 | public: 86 | using EventHandler = std::function; 87 | 88 | EventLoop(Server const* const server, Timer& timer); 89 | 90 | bool IsAlive() const; 91 | bool RunLoop(); 92 | void WakeUp(); 93 | EventLoop& RegisterHandler(int event, EventHandler handler); 94 | EventLoop& RegisterHandler(std::initializer_list event_list, 95 | EventHandler handler); 96 | 97 | private: 98 | bool alive_; 99 | Server const* const server_; 100 | int x11_file_descriptor_; 101 | util::SelfPipe self_pipe_; 102 | Timer& timer_; 103 | std::unordered_map handler_map_; 104 | 105 | void ReapChildPIDs() const; 106 | }; 107 | 108 | bool GetWMName(Display* display, Window window, std::string* output); 109 | 110 | pid_t GetWindowPID(Window window); 111 | int SetWindowPID(Window window); 112 | Window CreateSimpleWindow(Window parent, int x, int y, unsigned int width, 113 | unsigned int height, unsigned int border_width, 114 | unsigned long border, unsigned long background); 115 | Window CreateWindow(Window parent, int x, int y, unsigned int width, 116 | unsigned int height, unsigned int border_width, int depth, 117 | unsigned int window_class, Visual* visual, 118 | unsigned long valuemask, XSetWindowAttributes* attributes); 119 | 120 | Visual* GetTrueColorVisual(Display* display, int screen_number); 121 | 122 | } // namespace x11 123 | } // namespace util 124 | 125 | #endif // TINT3_UTIL_X11_HH 126 | -------------------------------------------------------------------------------- /src/util/xdg.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "absl/strings/str_split.h" 6 | #include "absl/strings/string_view.h" 7 | 8 | #include "util/common.hh" 9 | #include "util/environment.hh" 10 | #include "util/fs.hh" 11 | #include "util/log.hh" 12 | #include "util/xdg.hh" 13 | 14 | namespace { 15 | 16 | using transform_fn = std::function; 17 | 18 | transform_fn DefaultValue(util::fs::Path value) { 19 | return [value](absl::string_view other) { 20 | return !other.empty() ? other : value; 21 | }; 22 | } 23 | 24 | transform_fn GetDefaultDirectory(absl::string_view relative_path) { 25 | return DefaultValue(util::fs::HomeDirectory() / relative_path); 26 | } 27 | 28 | } // namespace 29 | 30 | namespace util { 31 | namespace xdg { 32 | namespace basedir { 33 | 34 | util::fs::Path ConfigHome() { 35 | static auto default_ = GetDefaultDirectory("/.config"); 36 | return default_(environment::Get("XDG_CONFIG_HOME")); 37 | } 38 | 39 | util::fs::Path DataHome() { 40 | static auto default_ = GetDefaultDirectory("/.local/share"); 41 | return default_(environment::Get("XDG_DATA_HOME")); 42 | } 43 | 44 | std::vector DataDirs() { 45 | static auto default_ = DefaultValue("/usr/local/share:/usr/share"); 46 | auto path_components = default_(environment::Get("XDG_DATA_DIRS")); 47 | return absl::StrSplit(std::string(path_components), ":"); 48 | } 49 | 50 | std::vector ConfigDirs() { 51 | static auto default_ = DefaultValue("/usr/local/etc/xdg:/etc/xdg"); 52 | auto path_components = default_(environment::Get("XDG_CONFIG_DIRS")); 53 | return absl::StrSplit(std::string(path_components), ":"); 54 | } 55 | 56 | } // namespace basedir 57 | } // namespace xdg 58 | } // namespace util 59 | -------------------------------------------------------------------------------- /src/util/xdg.hh: -------------------------------------------------------------------------------- 1 | #ifndef TINT3_UTIL_XDG_HH 2 | #define TINT3_UTIL_XDG_HH 3 | 4 | #include 5 | #include 6 | 7 | #include "util/fs.hh" 8 | 9 | namespace util { 10 | namespace xdg { 11 | namespace basedir { 12 | 13 | util::fs::Path ConfigHome(); 14 | util::fs::Path DataHome(); 15 | std::vector ConfigDirs(); 16 | std::vector DataDirs(); 17 | 18 | } // namespace basedir 19 | } // namespace xdg 20 | } // namespace util 21 | 22 | #endif // TINT3_UTIL_XDG_HH 23 | -------------------------------------------------------------------------------- /src/util/xdg_test.cc: -------------------------------------------------------------------------------- 1 | #include "catch.hpp" 2 | 3 | #include 4 | 5 | #include "util/environment.hh" 6 | #include "util/xdg.hh" 7 | 8 | TEST_CASE("ConfigHome", "Overrideable through the environment") { 9 | auto env = environment::MakeScopedOverride("XDG_CONFIG_HOME", "something"); 10 | REQUIRE(util::xdg::basedir::ConfigHome() == "something"); 11 | } 12 | 13 | TEST_CASE("DataDirs", "Overrideable through the environment") { 14 | auto env = environment::MakeScopedOverride("XDG_DATA_DIRS", "dir1:dir2"); 15 | std::vector expected_dirs{"dir1", "dir2"}; 16 | REQUIRE(util::xdg::basedir::DataDirs() == expected_dirs); 17 | } 18 | 19 | TEST_CASE("ConfigDirs", "Overrideable through the environment") { 20 | auto env = environment::MakeScopedOverride("XDG_CONFIG_DIRS", "dir1:dir2"); 21 | std::vector expected_dirs{"dir1", "dir2"}; 22 | REQUIRE(util::xdg::basedir::ConfigDirs() == expected_dirs); 23 | } 24 | -------------------------------------------------------------------------------- /src/version.hh.in: -------------------------------------------------------------------------------- 1 | #ifndef VERSION_HH 2 | #define VERSION_HH 3 | 4 | #define TINT3_RELEASE_VERSION "0.3.0" 5 | #define GIT_BRANCH "@GIT_BRANCH@" 6 | #define GIT_COMMIT_HASH "@GIT_COMMIT_HASH@" 7 | 8 | #endif // VERSION_HH 9 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library( 2 | testmain STATIC 3 | main.cc) 4 | 5 | target_include_directories( 6 | testmain 7 | PRIVATE 8 | ${CAIRO_INCLUDE_DIRS} 9 | ${PANGOCAIRO_INCLUDE_DIRS}) 10 | 11 | target_link_libraries( 12 | testmain 13 | PRIVATE 14 | ${CAIRO_LIBRARIES} 15 | ${PANGOCAIRO_LIBRARIES}) 16 | -------------------------------------------------------------------------------- /test/Xnest.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Not quite the "unofficial BASH strict mode", but better than nothing. 4 | set -eu 5 | IFS="$(printf '\t\n.')" 6 | IFS="${IFS%?}" 7 | 8 | # Simple test script to launch the locally-built tint3 and the Openbox window 9 | # manager in a nested X session. 10 | # 11 | # Prerequisites: 12 | # - Xnest is installed and available via $PATH 13 | # - openbox is installed and available via $PATH 14 | # - the CMake build directory is named "build" and can be found inside the 15 | # repository root directory 16 | 17 | if [ $# -ne 1 ]; then 18 | echo " ✘ Wrong number of arguments." >&2 19 | echo " Expecting: a display name (e.g., \":1\")" >&2 20 | exit 1 21 | fi 22 | 23 | _DISPLAY="${1}" 24 | _ROOT="$(cd "$(dirname "${0}")/.."; pwd)" 25 | _XNEST_PID="" 26 | _OPENBOX_PID="" 27 | _TINT3_PID="" 28 | 29 | if ! command -v Xnest >/dev/null 2>&1; then 30 | echo " ✘ Xnest not found!" >&2 31 | exit 1 32 | fi 33 | 34 | if ! command -v openbox >/dev/null 2>&1; then 35 | echo " ✘ openbox not found!" >&2 36 | exit 1 37 | fi 38 | 39 | if [ ! -x "${_ROOT}/build/tint3" ]; then 40 | echo " ✘ local tint3 build not found!" >&2 41 | exit 1 42 | fi 43 | 44 | # {{{ cleanup 45 | 46 | kill_running_processes() { 47 | if [ -n "${_TINT3_PID}" ]; then 48 | echo " ⌛ Sending SIGTERM to tint3..." 49 | kill "${_TINT3_PID}" 2>/dev/null 50 | wait "${_TINT3_PID}" 51 | fi 52 | 53 | if [ -n "${_OPENBOX_PID}" ]; then 54 | echo " ⌛ Sending SIGTERM to openbox..." 55 | kill "${_OPENBOX_PID}" 2>/dev/null 56 | wait "${_OPENBOX_PID}" 57 | fi 58 | 59 | if [ -n "${_XNEST_PID}" ]; then 60 | echo " ⌛ Sending SIGTERM to Xnest..." 61 | kill "${_XNEST_PID}" 2>/dev/null 62 | wait "${_XNEST_PID}" 63 | fi 64 | } 65 | 66 | trap kill_running_processes EXIT 67 | 68 | gracefully_terminate() { 69 | echo " >> Caught SIGINT, will now terminate." 70 | exit 0 71 | } 72 | 73 | trap gracefully_terminate INT 74 | 75 | # }}} cleanup 76 | 77 | # {{{ launch 78 | 79 | alive() { 80 | kill -s 0 "${1}" 2>/dev/null 81 | } 82 | 83 | Xnest "${_DISPLAY}" >/tmp/Xnest.log 2>&1 & 84 | _XNEST_PID="${!}" 85 | 86 | if alive "${_XNEST_PID}"; then 87 | echo " ✔ Launched Xnest as PID ${_XNEST_PID}." 88 | sleep 1 89 | else 90 | echo " ✘ Launching Xnest failed." >&2 91 | exit 1 92 | fi 93 | 94 | env -i DISPLAY="${_DISPLAY}" \ 95 | openbox >/tmp/openbox.log 2>&1 & 96 | _OPENBOX_PID="${!}" 97 | 98 | if alive "${_OPENBOX_PID}"; then 99 | echo " ✔ Launched openbox as PID ${_OPENBOX_PID}." 100 | sleep 1 101 | else 102 | echo " ✘ Launching openbox failed." >&2 103 | exit 1 104 | fi 105 | 106 | env -i DISPLAY="${_DISPLAY}" \ 107 | "${_ROOT}/build/tint3" >/tmp/tint3.log 2>&1 & 108 | _TINT3_PID="${!}" 109 | 110 | if alive "${_TINT3_PID}"; then 111 | echo " ✔ Launched tint3 as PID ${_TINT3_PID}." 112 | sleep 1 113 | else 114 | echo " ✘ Launching tint3 failed." >&2 115 | exit 1 116 | fi 117 | 118 | # }}} launch 119 | 120 | wait 121 | -------------------------------------------------------------------------------- /test/main.cc: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_RUNNER 2 | #include "catch.hpp" 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | // https://github.com/philsquared/Catch/blob/master/docs/own-main.md 11 | int main(int argc, char* argv[]) { 12 | // Make GLib code more Valgrind-friendly. 13 | // https://developer.gnome.org/glib/unstable/glib-running.html 14 | setlocale(LC_ALL, ""); 15 | setenv("G_DEBUG", "gc-friendly", 1); 16 | setenv("G_SLICE", "always-malloc", 1); 17 | 18 | int result = Catch::Session().run(argc, argv); 19 | 20 | // Pango and Cairo use Fontconfig, which handles memory in "unconventional" 21 | // ways to deal with mmap()-ed cache files, which confuses both Valgrind and 22 | // LeakSanitizer. These additional cleanup steps reduce the amount of false 23 | // positives somewhat. 24 | pango_cairo_font_map_set_default(nullptr); 25 | cairo_debug_reset_static_data(); 26 | 27 | return (result < 0xff ? result : 0xff); 28 | } 29 | -------------------------------------------------------------------------------- /test/suppressions/lsan.txt: -------------------------------------------------------------------------------- 1 | # False positives from Fontconfig. 2 | # Previously reported for Chromium as well, and similarly filtered out. 3 | # See: http://crbug.com/39050 4 | leak:libfontconfig.so 5 | 6 | # Cairo shows up in the leak reports when using Xlib surfaces, even though the 7 | # code looks correct. 8 | leak:libcairo.so 9 | --------------------------------------------------------------------------------