Merge branch 'hotfix/3.5.1'

This commit is contained in:
patrick96 2020-12-12 13:28:57 +01:00
commit 1c554dd0e0
No known key found for this signature in database
GPG Key ID: 521E5E03AEBCA1A7
30 changed files with 310 additions and 210 deletions

98
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,98 @@
name: CI
on:
workflow_dispatch:
inputs:
ref:
description: 'ref'
required: false
push:
pull_request:
jobs:
docs:
runs-on: ubuntu-20.04
env:
COLOR: "ON"
steps:
- uses: actions/checkout@v2
with:
ref: ${{ github.event.inputs.ref }}
- name: Install Dependencies
run: sudo apt-get install -y python3-sphinx
- name: Build Documentation
run: |
mkdir -p doc/build
cd doc/build
cmake ..
make doc
build:
runs-on: ubuntu-20.04
strategy:
matrix:
cxx: [g++, clang++]
polybar_build_type: ["full"]
build_type: ["Release"]
include:
- cxx: g++
polybar_build_type: "tests"
build_type: "Coverage"
- cxx: g++
polybar_build_type: "minimal"
build_type: "Release"
env:
CXX: ${{ matrix.cxx }}
BUILD_TYPE: ${{ matrix.build_type }}
POLYBAR_BUILD_TYPE: ${{ matrix.polybar_build_type }}
POLYBAR_DIR: ${{ github.workspace }}
BUILD_DIR: "${{ github.workspace}}/build"
MAKEFLAGS: "-j4"
COLOR: "ON"
steps:
- name: Install Dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
libxcb-composite0-dev \
libxcb-ewmh-dev \
libxcb-icccm4-dev \
libxcb-image0-dev \
libxcb-randr0-dev \
libxcb-util0-dev \
libxcb1-dev \
libcairo2-dev \
python3-xcbgen \
xcb-proto
if [ "$POLYBAR_BUILD_TYPE" != "minimal" ]; then
sudo apt-get install -y \
libxcb-xkb-dev \
libxcb-cursor-dev \
libxcb-xrm-dev \
i3-wm \
libcurl4-openssl-dev \
libjsoncpp-dev \
libasound2-dev \
libpulse-dev \
libiw-dev \
libmpdclient-dev
fi
- uses: actions/checkout@v2
with:
submodules: true
ref: ${{ github.event.inputs.ref }}
- name: Summary
run: ./common/ci/summary.sh
- name: Configure
run: ./common/ci/configure.sh
- name: Build
run: |
cd $BUILD_DIR
make
- name: Tests
if: ${{ matrix.polybar_build_type == 'tests' }}
run: |
cd $BUILD_DIR
make check
cd $POLYBAR_DIR
bash <(curl -s https://codecov.io/bash) -F unittests -a "-ap" -Z

View File

@ -1,109 +0,0 @@
sudo: required
dist: focal
language: cpp
env:
global:
- JOBS=4
- MAKEFLAGS="-j ${JOBS}"
- POLYBAR_BUILD_TYPE="compile"
# Build configurations can either not specify anything for 'addon' and use this
# default list of packages. Or they can pick and choose which package groups to
# install
addons:
apt:
packages:
- &base_deps
- libxcb-composite0-dev
- libxcb-ewmh-dev
- libxcb-icccm4-dev
- libxcb-image0-dev
- libxcb-randr0-dev
- libxcb-util0-dev
- python3-xcbgen
- xcb-proto
- &optional_deps
- libxcb-xkb-dev
- libxcb-cursor-dev
- libxcb-xrm-dev
- libxcb1-dev
- xutils-dev
- i3-wm
- libjsoncpp-dev
- libasound2-dev
- libpulse-dev
- libcairo2-dev
- libiw-dev
- libmpdclient-dev
script: source ${TRAVIS_BUILD_DIR}/common/travis/build.sh
matrix:
include:
# Only builds the documentation
- language: generic
# Doesn't actually do anything, just used for the indicator on travis
compiler: Sphinx
addons: {apt: {packages: [python3-sphinx]}}
before_script:
- mkdir -p doc/build
- cd doc/build
- cmake ..
script: make doc
# Disable unnecessary commands
cache:
- compiler: clang
env: BUILD_TYPE=Release
addons: {apt: {packages: [*base_deps, *optional_deps]}}
- compiler: gcc
env: BUILD_TYPE=Coverage POLYBAR_BUILD_TYPE=tests BUILD_TESTS=ON
addons: {apt: {packages: [*base_deps, *optional_deps]}}
script: make check
after_success:
- cd ${TRAVIS_BUILD_DIR}
- bash <(curl -s https://codecov.io/bash) -F unittests -a "-ap" -Z || echo "Codecov did not collect coverage reports"
- compiler: gcc
env: BUILD_TYPE=Release
addons: {apt: {packages: [*base_deps, *optional_deps]}}
# Minimal build, contains no optional dependencies. This makes sure that
# we properly remove files from compilation that depend on libraries that
# are not installed
- compiler: gcc
env: BUILD_TYPE=Release POLYBAR_BUILD_TYPE=minimal
addons: {apt: {packages: [*base_deps]}}
cache:
ccache: true
apt: true
before_script:
- source ${TRAVIS_BUILD_DIR}/common/travis/summary.sh
- source ${TRAVIS_BUILD_DIR}/common/travis/configure.sh
# Only fetch the newest 5 commits instead of 50
git:
depth: 5
notifications:
email: false
irc:
channels:
- "irc.freenode.org#polybar"
template:
- " %{repository_slug}(%{branch})#%{build_number} | \"%{commit_subject}\" by %{author} | Commit #%{commit} %{result}: %{build_url}"
use_notice: true
on_success: never
on_failure: change
on_start: never
webhooks:
urls:
# For the https://gitter.im/polybar/polybar gitter room
- https://webhooks.gitter.im/e/10bdbe25961312646ace
on_success: never
on_failure: always
on_start: never

View File

@ -8,8 +8,8 @@ A fast and easy-to-use tool for creating status bars.
<p align="center">
<a href="https://github.com/polybar/polybar/releases"><img src="https://img.shields.io/github/release/polybar/polybar.svg"></a>
<a href="https://github.com/polybar/polybar/actions?query=workflow%3ACI"><img src="https://github.com/polybar/polybar/workflows/CI/badge.svg"></a>
<a href="https://github.com/polybar/polybar/actions?query=workflow%3A%22Release+Workflow%22"><img src="https://github.com/polybar/polybar/workflows/Release%20Workflow/badge.svg?branch=master"></a>
<a href="https://travis-ci.com/polybar/polybar"><img src="https://travis-ci.com/polybar/polybar.svg?branch=master"></a>
<a href="https://polybar.readthedocs.io"><img src="https://readthedocs.org/projects/polybar/badge/?version=latest"></a>
<a href="https://gitter.im/polybar/polybar"><img src="https://badges.gitter.im/polybar/polybar.svg"></a>
<a href="https://codecov.io/gh/polybar/polybar/branch/master"><img src="https://codecov.io/gh/polybar/polybar/branch/master/graph/badge.svg"></a>

46
common/ci/configure.sh Executable file
View File

@ -0,0 +1,46 @@
#!/usr/bin/env bash
set -eo pipefail
if [ -d "$BUILD_DIR" ]; then
rm -Rf "$BUILD_DIR"
fi
mkdir -p "${BUILD_DIR}"
cd "${BUILD_DIR}"
if [ "$POLYBAR_BUILD_TYPE" != "minimal" ]; then
ENABLE_PULSEAUDIO=ON
ENABLE_NETWORK=ON
ENABLE_MPD=ON
ENABLE_CURL=ON
ENABLE_ALSA=ON
ENABLE_I3=ON
WITH_XRM=ON
WITH_XKB=ON
WITH_XRANDR_MONITORS=ON
WITH_XCURSOR=ON
fi
if [ "$POLYBAR_BUILD_TYPE" = "tests" ]; then
BUILD_TESTS=ON
fi
cmake \
-DCMAKE_CXX_COMPILER="${CXX}" \
-DCMAKE_CXX_FLAGS="${CXXFLAGS} -Werror" \
-DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \
-DBUILD_TESTS:BOOL="${BUILD_TESTS:-OFF}" \
-DBUILD_DOC:BOOL="${BUILD_DOC:-OFF}" \
-DWITH_XRANDR=ON \
-DENABLE_PULSEAUDIO="${ENABLE_PULSEAUDIO:-OFF}" \
-DENABLE_NETWORK="${ENABLE_NETWORK:-OFF}" \
-DENABLE_MPD="${ENABLE_MPD:-OFF}" \
-DENABLE_CURL="${ENABLE_CURL:-OFF}" \
-DENABLE_ALSA="${ENABLE_ALSA:-OFF}" \
-DENABLE_I3="${ENABLE_I3:-OFF}" \
-DWITH_XRM="${WITH_XRM:-OFF}" \
-DWITH_XKB="${WITH_XKB:-OFF}" \
-DWITH_XRANDR_MONITORS="${WITH_XRANDR_MONITORS:-OFF}" \
-DWITH_XCURSOR="${WITH_XCURSOR:-OFF}" \
..

19
common/ci/summary.sh Executable file
View File

@ -0,0 +1,19 @@
#!/usr/bin/env bash
set -eo pipefail
set -x
"${CXX}" --version
cmake --version
set +x
echo "PATH=${PATH}"
echo "CXX=${CXX}"
echo "CXXFLAGS=${CXXFLAGS}"
echo "LDFLAGS=${LDFLAGS}"
echo "LD_LIBRARY_PATH=${LD_LIBRARY_PATH}"
echo "MAKEFLAGS=${MAKEFLAGS}"
echo "POLYBAR_BUILD_TYPE=${POLYBAR_BUILD_TYPE}"
echo "CMAKE_BUILD_TYPE=${BUILD_TYPE}"

View File

@ -1,3 +0,0 @@
#!/bin/bash
cd "${TRAVIS_BUILD_DIR}/build" || false
make || exit $?

View File

@ -1,32 +0,0 @@
#!/bin/bash
mkdir -p "${TRAVIS_BUILD_DIR}/build"
cd "${TRAVIS_BUILD_DIR}/build" || false
FLAGS=""
# Disable all extra modules and X extensions for minimal builds
# Most of these should already be turned off because their libraries are not
# installed, but some may not be
if [ "$POLYBAR_BUILD_TYPE" == "minimal" ]; then
FLAGS=(
"-DENABLE_PULSEAUDIO=OFF"
"-DENABLE_NETWORK=OFF"
"-DENABLE_MPD=OFF"
"-DENABLE_CURL=OFF"
"-DENABLE_ALSA=OFF"
"-DENABLE_I3=OFF"
"-DWITH_XRM=OFF"
"-DWITH_XKB=OFF"
"-DWITH_XRANDR_MONITORS=OFF"
"-DWITH_XCURSOR=OFF"
"-DWITH_XRANDR=ON"
)
fi
cmake \
-DCMAKE_CXX_COMPILER="${CXX}" \
-DCMAKE_CXX_FLAGS="${CXXFLAGS} -Werror" \
-DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \
-DBUILD_TESTS:BOOL="${BUILD_TESTS:-OFF}" \
-DBUILD_DOC:BOOL="${BUILD_DOC:-OFF}" \
"${FLAGS[@]}" ..

View File

@ -1,13 +0,0 @@
#!/bin/bash
echo "${CXX} --version"
eval "${CXX} --version"
echo "cmake --version"
cmake --version
echo "PATH=${PATH}"
echo "CXX=${CXX}"
echo "CXXFLAGS=${CXXFLAGS}"
echo "LDFLAGS=${LDFLAGS}"
echo "LD_LIBRARY_PATH=${LD_LIBRARY_PATH}"
echo "JOBS=${JOBS}"

View File

@ -63,7 +63,7 @@ class pulseaudio {
pa_cvolume cv;
bool muted{false};
// default sink name
static constexpr auto DEFAULT_SINK{"@DEFAULT_SINK@"};
static constexpr auto DEFAULT_SINK = "@DEFAULT_SINK@";
pa_context* m_context{nullptr};
pa_threaded_mainloop* m_mainloop{nullptr};

View File

@ -100,7 +100,8 @@ class logger {
/**
* Convert string
*/
const char* convert(string arg) const; // NOLINT
const char* convert(string& arg) const;
const char* convert(const string& arg) const;
/**
* Convert thread id

View File

@ -48,7 +48,7 @@ namespace modules {
template <typename Impl>
string module<Impl>::type() const {
return string(CONST_MOD(Impl).TYPE);
return string(module<Impl>::TYPE);
}
template <typename Impl>

View File

@ -17,6 +17,20 @@ namespace modules {
}
protected:
/**
* Loads and sets the interval for this module.
*
* Will throw an exception if a non-positive (<= 0) number is given.
*/
void set_interval(interval_t def) {
m_interval = this->m_conf.template get<decltype(m_interval)>(this->name(), "interval", def);
if (m_interval <= 0s) {
throw module_error(
this->name() + ": 'interval' must be larger than 0 (got '" + to_string(m_interval.count()) + "s')");
}
}
void runner() {
this->m_log.trace("%s: Thread id = %i", this->name(), concurrency_util::thread_id(this_thread::get_id()));

View File

@ -110,8 +110,6 @@ namespace modules {
// The following mutex is here to protect the data of this modules.
// This can't be achieved using m_buildlock since we "CRTP override" get_output().
mutable mutex m_workspace_mutex;
event_timer m_timer{0L, 25L};
};
} // namespace modules

View File

@ -1,5 +1,7 @@
#pragma once
#include <sys/types.h>
#include "common.hpp"
POLYBAR_NS
@ -10,11 +12,14 @@ namespace process_util {
void redirect_stdio_to_dev_null();
pid_t fork_detached(std::function<void()> const& lambda);
pid_t spawn_async(std::function<void()> const& lambda);
void fork_detached(std::function<void()> const& lambda);
void exec(char* cmd, char** args);
void exec_sh(const char* cmd);
int wait(pid_t pid);
pid_t wait_for_completion(pid_t process_id, int* status_addr = nullptr, int waitflags = 0);
pid_t wait_for_completion(int* status_addr, int waitflags = 0);
pid_t wait_for_completion_nohang(pid_t process_id, int* status);

View File

@ -72,30 +72,55 @@ void config::warn_deprecated(const string& section, const string& key, string re
* Look for sections set up to inherit from a base section
* and copy the missing parameters
*
* Multiple sections can be specified, separated by a space.
*
* [sub/section]
* inherit = base/section
* inherit = section1 section2
*/
void config::copy_inherited() {
for (auto&& section : m_sections) {
std::vector<string> inherit_sections;
// Collect all sections to be inherited
for (auto&& param : section.second) {
if (param.first == "inherit") {
// Get name of base section
string key_name = param.first;
if (key_name == "inherit") {
auto inherit = param.second;
if ((inherit = dereference<string>(section.first, param.first, inherit, inherit)).empty()) {
throw value_error("Invalid section \"\" defined for \"" + section.first + ".inherit\"");
inherit = dereference<string>(section.first, key_name, inherit, inherit);
std::vector<string> sections = string_util::split(std::move(inherit), ' ');
inherit_sections.insert(inherit_sections.end(), sections.begin(), sections.end());
} else if (key_name.find("inherit") == 0) {
// Legacy support for keys that just start with 'inherit'
m_log.warn(
"\"%s.%s\": Using anything other than 'inherit' for inheriting section keys is deprecated. "
"The 'inherit' key supports multiple section names separated by a space.",
section.first, key_name);
auto inherit = param.second;
inherit = dereference<string>(section.first, key_name, inherit, inherit);
if (inherit.empty() || m_sections.find(inherit) == m_sections.end()) {
throw value_error(
"Invalid section \"" + inherit + "\" defined for \"" + section.first + "." + key_name + "\"");
}
// Find and validate base section
auto base_section = m_sections.find(inherit);
inherit_sections.push_back(std::move(inherit));
}
}
for (const auto& base_name : inherit_sections) {
const auto base_section = m_sections.find(base_name);
if (base_section == m_sections.end()) {
throw value_error("Invalid section \"" + inherit + "\" defined for \"" + section.first + ".inherit\"");
throw value_error("Invalid section \"" + base_name + "\" defined for \"" + section.first + ".inherit\"");
}
m_log.trace("config: Copying missing params (sub=\"%s\", base=\"%s\")", section.first, inherit);
m_log.trace("config: Inheriting keys from \"%s\" in \"%s\"", base_name, section.first);
/*
* Iterate the base and copy the parameters that haven't been defined
* for the sub-section
* yet.
*/
for (auto&& base_param : base_section->second) {
section.second.emplace(base_param.first, base_param.second);
@ -103,7 +128,6 @@ void config::copy_inherited() {
}
}
}
}
template <>
string config::convert(string&& value) const {

View File

@ -1,6 +1,7 @@
#include "components/logger.hpp"
#include <unistd.h>
#include "components/logger.hpp"
#include "errors.hpp"
#include "settings.hpp"
#include "utils/concurrency.hpp"
@ -12,7 +13,11 @@ POLYBAR_NS
/**
* Convert string
*/
const char* logger::convert(string arg) const { // NOLINT
const char* logger::convert(string& arg) const {
return arg.c_str();
}
const char* logger::convert(const string& arg) const {
return arg.c_str();
}

View File

@ -9,7 +9,7 @@ namespace modules {
counter_module::counter_module(const bar_settings& bar, string name_)
: timer_module<counter_module>(bar, move(name_)) {
m_interval = m_conf.get(name(), "interval", m_interval);
set_interval(1s);
m_formatter->add(DEFAULT_FORMAT, TAG_COUNTER, {TAG_COUNTER});
}
@ -25,6 +25,6 @@ namespace modules {
}
return false;
}
}
} // namespace modules
POLYBAR_NS_END

View File

@ -16,7 +16,7 @@ namespace modules {
template class module<cpu_module>;
cpu_module::cpu_module(const bar_settings& bar, string name_) : timer_module<cpu_module>(bar, move(name_)) {
m_interval = m_conf.get<decltype(m_interval)>(name(), "interval", 1s);
set_interval(1s);
m_totalwarn = m_conf.get(name(), "warn-percentage", m_totalwarn);
m_ramp_padding = m_conf.get<decltype(m_ramp_padding)>(name(), "ramp-coreload-spacing", 1);

View File

@ -22,7 +22,7 @@ namespace modules {
throw module_error("No date or time format specified");
}
m_interval = m_conf.get<decltype(m_interval)>(name(), "interval", 1s);
set_interval(1s);
m_formatter->add(DEFAULT_FORMAT, TAG_LABEL, {TAG_LABEL, TAG_DATE});

View File

@ -31,7 +31,7 @@ namespace modules {
m_perc_used_warn = m_conf.get(name(), "warn-percentage", 90);
m_fixed = m_conf.get(name(), "fixed-values", m_fixed);
m_spacing = m_conf.get(name(), "spacing", m_spacing);
m_interval = m_conf.get<decltype(m_interval)>(name(), "interval", 30s);
set_interval(30s);
// Add formats and elements
m_formatter->add(

View File

@ -23,7 +23,7 @@ namespace modules {
m_api_url += '/';
}
m_interval = m_conf.get<decltype(m_interval)>(name(), "interval", 60s);
set_interval(60s);
m_empty_notifications = m_conf.get(name(), "empty-notifications", m_empty_notifications);
m_formatter->add(DEFAULT_FORMAT, TAG_LABEL, {TAG_LABEL});

View File

@ -16,7 +16,7 @@ namespace modules {
template class module<memory_module>;
memory_module::memory_module(const bar_settings& bar, string name_) : timer_module<memory_module>(bar, move(name_)) {
m_interval = m_conf.get<decltype(m_interval)>(name(), "interval", 1s);
set_interval(1s);
m_perc_memused_warn = m_conf.get(name(), "warn-percentage", 90);
m_formatter->add(DEFAULT_FORMAT, TAG_LABEL, {TAG_LABEL, TAG_BAR_USED, TAG_BAR_FREE, TAG_RAMP_USED, TAG_RAMP_FREE,

View File

@ -140,7 +140,7 @@ namespace modules {
m_log.info("%s: Opening menu level '%i'", name(), static_cast<int>(m_level));
if (static_cast<size_t>(m_level) >= m_levels.size()) {
m_log.warn("%s: Cannot open unexisting menu level '%i'", name(), level);
m_log.warn("%s: Cannot open unexisting menu level '%s'", name(), level);
m_level = -1;
}
} else if (action == EVENT_CLOSE) {

View File

@ -19,7 +19,7 @@ namespace modules {
m_ping_nth_update = m_conf.get(name(), "ping-interval", m_ping_nth_update);
m_udspeed_minwidth = m_conf.get(name(), "udspeed-minwidth", m_udspeed_minwidth);
m_accumulate = m_conf.get(name(), "accumulate-stats", m_accumulate);
m_interval = m_conf.get<decltype(m_interval)>(name(), "interval", 1s);
set_interval(1s);
m_unknown_up = m_conf.get<bool>(name(), "unknown-as-up", false);
m_udspeed_unit = m_conf.get<string>(name(), "speed-unit", m_udspeed_unit);

View File

@ -19,7 +19,7 @@ namespace modules {
m_path = m_conf.get(name(), "hwmon-path", ""s);
m_tempbase = m_conf.get(name(), "base-temperature", 0);
m_tempwarn = m_conf.get(name(), "warn-temperature", 80);
m_interval = m_conf.get<decltype(m_interval)>(name(), "interval", 1s);
set_interval(1s);
m_units = m_conf.get(name(), "units", m_units);
if (m_path.empty()) {

View File

@ -126,10 +126,8 @@ namespace modules {
return;
}
if (m_timer.allow(evt->time)) {
broadcast();
}
}
/**
* Rebuild the list of managed clients

View File

@ -33,7 +33,7 @@ command<output_policy::IGNORED>::~command() {
* Execute the command
*/
int command<output_policy::IGNORED>::exec(bool wait_for_completion) {
m_forkpid = process_util::fork_detached([m_cmd = m_cmd] { process_util::exec_sh(m_cmd.c_str()); });
m_forkpid = process_util::spawn_async([m_cmd = m_cmd] { process_util::exec_sh(m_cmd.c_str()); });
if (wait_for_completion) {
auto status = wait();
m_forkpid = -1;

View File

@ -45,15 +45,11 @@ namespace process_util {
}
/**
* Forks a child process and completely detaches it.
* Forks a child process and executes the given lambda function in it.
*
* In the child process, the given lambda function is executed.
*
* Use this if you want to run a command and just forget about it.
*
* \returns The PID of the child process
* Processes spawned this way need to be waited on by the caller.
*/
pid_t fork_detached(std::function<void()> const& lambda) {
pid_t spawn_async(std::function<void()> const& lambda) {
pid_t pid = fork();
switch (pid) {
case -1:
@ -71,6 +67,50 @@ namespace process_util {
}
}
/**
* Forks a child process and completely detaches it.
*
* In the child process, the given lambda function is executed.
* We fork twice so that the first forked process can exit and it's child is
* reparented to the init process.
*
* Ref: https://web.archive.org/web/20120914180018/http://www.steve.org.uk/Reference/Unix/faq_2.html#SEC16
*
* Use this if you want to run a command and just forget about it.
*
* \returns The PID of the child process
*/
void fork_detached(std::function<void()> const& lambda) {
pid_t pid = fork();
switch (pid) {
case -1:
throw runtime_error("fork_detached: Unable to fork: " + string(strerror(errno)));
case 0:
// Child
setsid();
pid = fork();
switch (pid) {
case -1:
throw runtime_error("fork_detached: Unable to fork: " + string(strerror(errno)));
case 0:
// Child
umask(0);
redirect_stdio_to_dev_null();
lambda();
_Exit(0);
}
_Exit(0);
default:
/*
* The first fork immediately exits and we have to collect its exit
* status
*/
wait(pid);
}
}
/**
* Execute command
*/
@ -92,6 +132,15 @@ namespace process_util {
}
}
int wait(pid_t pid) {
int forkstatus;
do {
process_util::wait_for_completion(pid, &forkstatus, WCONTINUED | WUNTRACED);
} while (!WIFEXITED(forkstatus) && !WIFSIGNALED(forkstatus));
return WEXITSTATUS(forkstatus);
}
/**
* Wait for child process
*/

View File

@ -12,8 +12,8 @@
using namespace polybar;
using namespace process_util;
TEST(ForkDetached, is_detached) {
pid_t pid = fork_detached([] { exec_sh("sleep 0.1"); });
TEST(SpawnAsync, is_async) {
pid_t pid = spawn_async([] { exec_sh("sleep 0.1"); });
int status;
pid_t res = process_util::wait_for_completion_nohang(pid, &status);
@ -23,8 +23,8 @@ TEST(ForkDetached, is_detached) {
EXPECT_FALSE(WIFEXITED(status));
}
TEST(ForkDetached, exit_code) {
pid_t pid = fork_detached([] { exec_sh("exit 42"); });
TEST(SpawnAsync, exit_code) {
pid_t pid = spawn_async([] { exec_sh("exit 42"); });
int status = 0;
pid_t res = waitpid(pid, &status, 0);

View File

@ -1,4 +1,4 @@
# Polybar version information
# Update this on every release
# This is used to create the version string if a git repo is not available
3.5.0
3.5.1