diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 00000000..99746764
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -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
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 9e1615c6..00000000
--- a/.travis.yml
+++ /dev/null
@@ -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
diff --git a/README.md b/README.md
index c4c0434f..a2cf7daa 100644
--- a/README.md
+++ b/README.md
@@ -8,8 +8,8 @@ A fast and easy-to-use tool for creating status bars.
+
-
diff --git a/common/ci/configure.sh b/common/ci/configure.sh
new file mode 100755
index 00000000..90718748
--- /dev/null
+++ b/common/ci/configure.sh
@@ -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}" \
+ ..
diff --git a/common/ci/summary.sh b/common/ci/summary.sh
new file mode 100755
index 00000000..a6bfcf82
--- /dev/null
+++ b/common/ci/summary.sh
@@ -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}"
diff --git a/common/travis/build.sh b/common/travis/build.sh
deleted file mode 100755
index 9e376049..00000000
--- a/common/travis/build.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/bash
-cd "${TRAVIS_BUILD_DIR}/build" || false
-make || exit $?
diff --git a/common/travis/configure.sh b/common/travis/configure.sh
deleted file mode 100755
index b67d78bd..00000000
--- a/common/travis/configure.sh
+++ /dev/null
@@ -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[@]}" ..
diff --git a/common/travis/summary.sh b/common/travis/summary.sh
deleted file mode 100755
index 708d6f7b..00000000
--- a/common/travis/summary.sh
+++ /dev/null
@@ -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}"
diff --git a/include/adapters/pulseaudio.hpp b/include/adapters/pulseaudio.hpp
index 39eceb66..09398ac1 100644
--- a/include/adapters/pulseaudio.hpp
+++ b/include/adapters/pulseaudio.hpp
@@ -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};
diff --git a/include/components/logger.hpp b/include/components/logger.hpp
index 5c7d1ba2..6d995072 100644
--- a/include/components/logger.hpp
+++ b/include/components/logger.hpp
@@ -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
diff --git a/include/modules/meta/base.inl b/include/modules/meta/base.inl
index 5e713cd1..6792c02f 100644
--- a/include/modules/meta/base.inl
+++ b/include/modules/meta/base.inl
@@ -48,7 +48,7 @@ namespace modules {
template
string module::type() const {
- return string(CONST_MOD(Impl).TYPE);
+ return string(module::TYPE);
}
template
diff --git a/include/modules/meta/timer_module.hpp b/include/modules/meta/timer_module.hpp
index d4f1a4a2..2cd5f3bf 100644
--- a/include/modules/meta/timer_module.hpp
+++ b/include/modules/meta/timer_module.hpp
@@ -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(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()));
diff --git a/include/modules/xworkspaces.hpp b/include/modules/xworkspaces.hpp
index 62aafd90..d58cf455 100644
--- a/include/modules/xworkspaces.hpp
+++ b/include/modules/xworkspaces.hpp
@@ -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
diff --git a/include/utils/process.hpp b/include/utils/process.hpp
index eb380532..3ab5fd7b 100644
--- a/include/utils/process.hpp
+++ b/include/utils/process.hpp
@@ -1,5 +1,7 @@
#pragma once
+#include
+
#include "common.hpp"
POLYBAR_NS
@@ -10,11 +12,14 @@ namespace process_util {
void redirect_stdio_to_dev_null();
- pid_t fork_detached(std::function const& lambda);
+ pid_t spawn_async(std::function const& lambda);
+ void fork_detached(std::function 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);
diff --git a/src/components/config.cpp b/src/components/config.cpp
index a47050be..a94aaee2 100644
--- a/src/components/config.cpp
+++ b/src/components/config.cpp
@@ -72,34 +72,58 @@ 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 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(section.first, param.first, inherit, inherit)).empty()) {
- throw value_error("Invalid section \"\" defined for \"" + section.first + ".inherit\"");
+ inherit = dereference(section.first, key_name, inherit, inherit);
+
+ std::vector 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(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);
- if (base_section == m_sections.end()) {
- throw value_error("Invalid section \"" + inherit + "\" defined for \"" + section.first + ".inherit\"");
- }
+ inherit_sections.push_back(std::move(inherit));
+ }
+ }
- m_log.trace("config: Copying missing params (sub=\"%s\", base=\"%s\")", section.first, 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 \"" + base_name + "\" defined for \"" + section.first + ".inherit\"");
+ }
- /*
- * Iterate the base and copy the parameters that haven't been defined
- * for the sub-section
- */
- for (auto&& base_param : base_section->second) {
- section.second.emplace(base_param.first, base_param.second);
- }
+ 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
+ * yet.
+ */
+ for (auto&& base_param : base_section->second) {
+ section.second.emplace(base_param.first, base_param.second);
}
}
}
diff --git a/src/components/logger.cpp b/src/components/logger.cpp
index f482eeaf..f5151799 100644
--- a/src/components/logger.cpp
+++ b/src/components/logger.cpp
@@ -1,6 +1,7 @@
+#include "components/logger.hpp"
+
#include
-#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();
}
diff --git a/src/modules/counter.cpp b/src/modules/counter.cpp
index 25c7ae9e..3abd5e40 100644
--- a/src/modules/counter.cpp
+++ b/src/modules/counter.cpp
@@ -9,7 +9,7 @@ namespace modules {
counter_module::counter_module(const bar_settings& bar, string name_)
: timer_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
diff --git a/src/modules/cpu.cpp b/src/modules/cpu.cpp
index 69673511..16d0bba1 100644
--- a/src/modules/cpu.cpp
+++ b/src/modules/cpu.cpp
@@ -16,7 +16,7 @@ namespace modules {
template class module;
cpu_module::cpu_module(const bar_settings& bar, string name_) : timer_module(bar, move(name_)) {
- m_interval = m_conf.get(name(), "interval", 1s);
+ set_interval(1s);
m_totalwarn = m_conf.get(name(), "warn-percentage", m_totalwarn);
m_ramp_padding = m_conf.get(name(), "ramp-coreload-spacing", 1);
diff --git a/src/modules/date.cpp b/src/modules/date.cpp
index 127e7832..5bb4b6b0 100644
--- a/src/modules/date.cpp
+++ b/src/modules/date.cpp
@@ -22,7 +22,7 @@ namespace modules {
throw module_error("No date or time format specified");
}
- m_interval = m_conf.get(name(), "interval", 1s);
+ set_interval(1s);
m_formatter->add(DEFAULT_FORMAT, TAG_LABEL, {TAG_LABEL, TAG_DATE});
diff --git a/src/modules/fs.cpp b/src/modules/fs.cpp
index 339caac3..b8193fd2 100644
--- a/src/modules/fs.cpp
+++ b/src/modules/fs.cpp
@@ -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(name(), "interval", 30s);
+ set_interval(30s);
// Add formats and elements
m_formatter->add(
diff --git a/src/modules/github.cpp b/src/modules/github.cpp
index 29ef5d1c..f40b15ac 100644
--- a/src/modules/github.cpp
+++ b/src/modules/github.cpp
@@ -23,7 +23,7 @@ namespace modules {
m_api_url += '/';
}
- m_interval = m_conf.get(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});
diff --git a/src/modules/memory.cpp b/src/modules/memory.cpp
index 0227d61a..f97864fb 100644
--- a/src/modules/memory.cpp
+++ b/src/modules/memory.cpp
@@ -16,7 +16,7 @@ namespace modules {
template class module;
memory_module::memory_module(const bar_settings& bar, string name_) : timer_module(bar, move(name_)) {
- m_interval = m_conf.get(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,
diff --git a/src/modules/menu.cpp b/src/modules/menu.cpp
index dbf9adea..714bc464 100644
--- a/src/modules/menu.cpp
+++ b/src/modules/menu.cpp
@@ -140,7 +140,7 @@ namespace modules {
m_log.info("%s: Opening menu level '%i'", name(), static_cast(m_level));
if (static_cast(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) {
diff --git a/src/modules/network.cpp b/src/modules/network.cpp
index 39a31a24..ddebde02 100644
--- a/src/modules/network.cpp
+++ b/src/modules/network.cpp
@@ -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(name(), "interval", 1s);
+ set_interval(1s);
m_unknown_up = m_conf.get(name(), "unknown-as-up", false);
m_udspeed_unit = m_conf.get(name(), "speed-unit", m_udspeed_unit);
diff --git a/src/modules/temperature.cpp b/src/modules/temperature.cpp
index 07bb4e5d..06cf88ff 100644
--- a/src/modules/temperature.cpp
+++ b/src/modules/temperature.cpp
@@ -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(name(), "interval", 1s);
+ set_interval(1s);
m_units = m_conf.get(name(), "units", m_units);
if (m_path.empty()) {
diff --git a/src/modules/xworkspaces.cpp b/src/modules/xworkspaces.cpp
index 128524fd..f8b7b9b1 100644
--- a/src/modules/xworkspaces.cpp
+++ b/src/modules/xworkspaces.cpp
@@ -126,9 +126,7 @@ namespace modules {
return;
}
- if (m_timer.allow(evt->time)) {
- broadcast();
- }
+ broadcast();
}
/**
diff --git a/src/utils/command.cpp b/src/utils/command.cpp
index 2f2afc2c..7e8d3a9f 100644
--- a/src/utils/command.cpp
+++ b/src/utils/command.cpp
@@ -33,7 +33,7 @@ command::~command() {
* Execute the command
*/
int command::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;
diff --git a/src/utils/process.cpp b/src/utils/process.cpp
index f5ec0857..1127c606 100644
--- a/src/utils/process.cpp
+++ b/src/utils/process.cpp
@@ -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 const& lambda) {
+ pid_t spawn_async(std::function 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 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
*/
diff --git a/tests/unit_tests/utils/process.cpp b/tests/unit_tests/utils/process.cpp
index cfd8351c..64ff21f6 100644
--- a/tests/unit_tests/utils/process.cpp
+++ b/tests/unit_tests/utils/process.cpp
@@ -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);
diff --git a/version.txt b/version.txt
index 7c5ff776..161ce030 100644
--- a/version.txt
+++ b/version.txt
@@ -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