40
include/modules/gps.hpp
Normal file
40
include/modules/gps.hpp
Normal file
@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <sys/statvfs.h>
|
||||
|
||||
#ifdef WANT_RFKILL
|
||||
#include "util/rfkill.hpp"
|
||||
#endif
|
||||
|
||||
#include <gps.h>
|
||||
|
||||
#include "ALabel.hpp"
|
||||
#include "util/sleeper_thread.hpp"
|
||||
|
||||
namespace waybar::modules {
|
||||
|
||||
class Gps : public ALabel {
|
||||
public:
|
||||
Gps(const std::string&, const Json::Value&);
|
||||
virtual ~Gps();
|
||||
auto update() -> void override;
|
||||
|
||||
private:
|
||||
#ifdef WANT_RFKILL
|
||||
util::Rfkill rfkill_;
|
||||
#endif
|
||||
const std::string getFixModeName() const;
|
||||
const std::string getFixModeString() const;
|
||||
|
||||
const std::string getFixStatusString() const;
|
||||
|
||||
util::SleeperThread thread_, gps_thread_;
|
||||
gps_data_t gps_data_;
|
||||
std::string state_;
|
||||
|
||||
bool hideDisconnected = true;
|
||||
bool hideNoFix = false;
|
||||
};
|
||||
|
||||
} // namespace waybar::modules
|
111
man/waybar-gps.5.scd
Normal file
111
man/waybar-gps.5.scd
Normal file
@ -0,0 +1,111 @@
|
||||
waybar-gps(5) "waybar-gps" "User Manual"
|
||||
|
||||
# NAME
|
||||
|
||||
waybar - gps module
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
*gps* module for gpsd.
|
||||
|
||||
|
||||
# FILES
|
||||
|
||||
$XDG_CONFIG_HOME/waybar/config ++
|
||||
Per user configuration file
|
||||
|
||||
# ADDITIONAL FILES
|
||||
|
||||
libgps lives in:
|
||||
|
||||
. /usr/lib/libgps.so or /usr/lib64/libgps.so
|
||||
. /usr/lib/pkgconfig/libgps.pc or /usr/lib64/pkgconfig/libgps.pc
|
||||
. /usr/include/gps
|
||||
|
||||
# CONFIGURATION
|
||||
|
||||
*format*: ++
|
||||
typeof: string ++
|
||||
default: {glyph} ++
|
||||
The text format.
|
||||
|
||||
*tooltip*: ++
|
||||
typeof: bool ++
|
||||
default: true ++
|
||||
Option to disable tooltip on hover.
|
||||
|
||||
*tooltip-format*: ++
|
||||
typeof: string ++
|
||||
default: Games running: {glyph} ++
|
||||
The text format of the tooltip.
|
||||
|
||||
*interval*: ++
|
||||
typeof: integer ++
|
||||
default: 5 ++
|
||||
The interval in which the GPS information gets polled (e.g. current speed).
|
||||
Significant updates (e.g. the current fix mode) are updated immediately.
|
||||
|
||||
*hide-disconnected*: ++
|
||||
typeof: bool ++
|
||||
default: true ++
|
||||
Defines if the module should be hidden if there is no GPS receiver.
|
||||
|
||||
*hide-no-fix*: ++
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
Defines if the module should be hidden if there is no GPS fix.
|
||||
|
||||
# FORMAT REPLACEMENTS
|
||||
|
||||
*{mode}*: Fix mode
|
||||
|
||||
*{status}*: Technology used for GPS fix. Not all GPS receivers report this.
|
||||
|
||||
*{latitude}*: Latitude, decimal degrees. Can be NaN.
|
||||
|
||||
*{latitude_error}*: Latitude uncertainty, meters. Can be NaN.
|
||||
|
||||
*{longitude}*: Longitude, decimal degrees. Can be NaN.
|
||||
|
||||
*{longitude_error}*: Longitude uncertainty, meters. Can be NaN.
|
||||
|
||||
*{altitude_hae}*: Altitude, height above ellipsoid, meters. Can be NaN.
|
||||
|
||||
*{altitude_msl}*: Longitude, MSL, meters. Can be NaN.
|
||||
|
||||
*{altitude_error}*: Altitude uncertainty, meters. Can be NaN.
|
||||
|
||||
*{speed}*: Speed over ground, meters/sec. Can be NaN.
|
||||
|
||||
*{speed_error}*: Speed uncertainty, meters/sec. Can be NaN.
|
||||
|
||||
*{climb}*: Vertical speed, meters/sec. Can be NaN.
|
||||
|
||||
*{climb_error}*: Vertical speed uncertainty, meters/sec. Can be NaN.
|
||||
|
||||
*{satellites_visible}*: Number of satellites visible from the GPS receiver.
|
||||
|
||||
*{satellites_used}*: Number of satellites used for the GPS fix.
|
||||
|
||||
# EXAMPLES
|
||||
|
||||
```
|
||||
"gps": {
|
||||
"format": "{mode}",
|
||||
"format-disabled": "", // an empty format will hide the module
|
||||
"format-no-fix": "No fix",
|
||||
"format-fix-3d": "{status}",
|
||||
"tooltip-format": "{mode}",
|
||||
"tooltip-format-no-fix": "{satellites_visible} satellites visible",
|
||||
"tooltip-format-fix-2d": "{satellites_used}/{satellites_visible} satellites used",
|
||||
"tooltip-format-fix-3d": "Altitude: {altitude_hae}m",
|
||||
"hide-disconnected": false
|
||||
}
|
||||
```
|
||||
# STYLE
|
||||
|
||||
- *#gps*
|
||||
- *#gps.disabled* Applied when GPS is disabled.
|
||||
- *#gps.fix-none* Applied when GPS is present, but there is no fix.
|
||||
- *#gps.fix-2d* Applied when there is a 2D fix.
|
||||
- *#gps.fix-3d* Applied when there is a 3D fix.
|
10
meson.build
10
meson.build
@ -93,6 +93,7 @@ libmpdclient = dependency('libmpdclient', required: get_option('mpd'))
|
||||
xkbregistry = dependency('xkbregistry')
|
||||
libjack = dependency('jack', required: get_option('jack'))
|
||||
libwireplumber = dependency('wireplumber-0.5', required: get_option('wireplumber'))
|
||||
libgps = dependency('libgps', required: get_option('gps'))
|
||||
|
||||
libsndio = compiler.find_library('sndio', required: get_option('sndio'))
|
||||
if libsndio.found()
|
||||
@ -506,6 +507,12 @@ if cava.found()
|
||||
man_files += files('man/waybar-cava.5.scd')
|
||||
endif
|
||||
|
||||
if libgps.found()
|
||||
add_project_arguments('-DHAVE_LIBGPS', language: 'cpp')
|
||||
src_files += files('src/modules/gps.cpp')
|
||||
man_files += files('man/waybar-gps.5.scd')
|
||||
endif
|
||||
|
||||
subdir('protocol')
|
||||
|
||||
app_resources = []
|
||||
@ -544,7 +551,8 @@ executable(
|
||||
libsndio,
|
||||
tz_dep,
|
||||
xkbregistry,
|
||||
cava
|
||||
cava,
|
||||
libgps
|
||||
],
|
||||
include_directories: inc_dirs,
|
||||
install: true,
|
||||
|
@ -21,3 +21,4 @@ option('wireplumber', type: 'feature', value: 'auto', description: 'Enable suppo
|
||||
option('cava', type: 'feature', value: 'auto', description: 'Enable support for Cava')
|
||||
option('niri', type: 'boolean', description: 'Enable support for niri')
|
||||
option('login-proxy', type: 'boolean', description: 'Enable interfacing with dbus login interface')
|
||||
option('gps', type: 'feature', value: 'auto', description: 'Enable support for gps')
|
||||
|
@ -113,6 +113,9 @@
|
||||
#ifdef HAVE_SYSTEMD_MONITOR
|
||||
#include "modules/systemd_failed_units.hpp"
|
||||
#endif
|
||||
#ifdef HAVE_LIBGPS
|
||||
#include "modules/gps.hpp"
|
||||
#endif
|
||||
#include "modules/cffi.hpp"
|
||||
#include "modules/custom.hpp"
|
||||
#include "modules/image.hpp"
|
||||
@ -343,6 +346,11 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name,
|
||||
if (ref == "systemd-failed-units") {
|
||||
return new waybar::modules::SystemdFailedUnits(id, config_[name]);
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_LIBGPS
|
||||
if (ref == "gps") {
|
||||
return new waybar::modules::Gps(id, config_[name]);
|
||||
}
|
||||
#endif
|
||||
if (ref == "temperature") {
|
||||
return new waybar::modules::Temperature(id, config_[name]);
|
||||
|
217
src/modules/gps.cpp
Normal file
217
src/modules/gps.cpp
Normal file
@ -0,0 +1,217 @@
|
||||
#include "modules/gps.hpp"
|
||||
#include <gps.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
|
||||
// In the 80000 version of fmt library authors decided to optimize imports
|
||||
// and moved declarations required for fmt::dynamic_format_arg_store in new
|
||||
// header fmt/args.h
|
||||
#if (FMT_VERSION >= 80000)
|
||||
#include <fmt/args.h>
|
||||
#else
|
||||
#include <fmt/core.h>
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
using namespace waybar::util;
|
||||
constexpr const char *DEFAULT_FORMAT = "{mode}";
|
||||
} // namespace
|
||||
|
||||
|
||||
waybar::modules::Gps::Gps(const std::string& id, const Json::Value& config)
|
||||
: ALabel(config, "gps", id, "{}", 5)
|
||||
#ifdef WANT_RFKILL
|
||||
,rfkill_{RFKILL_TYPE_GPS}
|
||||
#endif
|
||||
{
|
||||
thread_ = [this] {
|
||||
dp.emit();
|
||||
thread_.sleep_for(interval_);
|
||||
};
|
||||
|
||||
if (0 != gps_open("localhost", "2947", &gps_data_)) {
|
||||
throw std::runtime_error("Can't open gpsd socket");
|
||||
}
|
||||
|
||||
if (config_["hide-disconnected"].isBool()) {
|
||||
hideDisconnected = config_["hide-disconnected"].asBool();
|
||||
}
|
||||
|
||||
if (config_["hide-no-fix"].isBool()) {
|
||||
hideNoFix = config_["hide-no-fix"].asBool();
|
||||
}
|
||||
|
||||
gps_thread_ = [this] {
|
||||
dp.emit();
|
||||
gps_stream(&gps_data_, WATCH_ENABLE, NULL);
|
||||
int last_gps_mode = 0;
|
||||
|
||||
while (gps_waiting(&gps_data_, 5000000)) {
|
||||
if (gps_read(&gps_data_, NULL, 0) == -1) {
|
||||
throw std::runtime_error("Can't read data from gpsd.");
|
||||
}
|
||||
|
||||
if (MODE_SET != (MODE_SET & gps_data_.set)) {
|
||||
// did not even get mode, nothing to see here
|
||||
continue;
|
||||
}
|
||||
|
||||
if (gps_data_.fix.mode != last_gps_mode) {
|
||||
// significant update
|
||||
dp.emit();
|
||||
}
|
||||
last_gps_mode = gps_data_.fix.mode;
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef WANT_RFKILL
|
||||
rfkill_.on_update.connect(sigc::hide(sigc::mem_fun(*this, &Gps::update)));
|
||||
#endif
|
||||
}
|
||||
|
||||
const std::string waybar::modules::Gps::getFixModeName() const {
|
||||
switch (gps_data_.fix.mode) {
|
||||
case MODE_NO_FIX:
|
||||
return "fix-none";
|
||||
case MODE_2D:
|
||||
return "fix-2d";
|
||||
case MODE_3D:
|
||||
return "fix-3d";
|
||||
default:
|
||||
#ifdef WANT_RFKILL
|
||||
if (rfkill_.getState()) return "disabled";
|
||||
#endif
|
||||
return "disconnected";
|
||||
}
|
||||
}
|
||||
|
||||
const std::string waybar::modules::Gps::getFixModeString() const {
|
||||
switch (gps_data_.fix.mode) {
|
||||
case MODE_NO_FIX:
|
||||
return "No fix";
|
||||
case MODE_2D:
|
||||
return "2D Fix";
|
||||
case MODE_3D:
|
||||
return "3D Fix";
|
||||
default:
|
||||
return "Disconnected";
|
||||
}
|
||||
}
|
||||
|
||||
const std::string waybar::modules::Gps::getFixStatusString() const {
|
||||
switch (gps_data_.fix.status) {
|
||||
case STATUS_GPS:
|
||||
return "GPS";
|
||||
case STATUS_DGPS:
|
||||
return "DGPS";
|
||||
case STATUS_RTK_FIX:
|
||||
return "RTK Fixed";
|
||||
case STATUS_RTK_FLT:
|
||||
return "RTK Float";
|
||||
case STATUS_DR:
|
||||
return "Dead Reckoning";
|
||||
case STATUS_GNSSDR:
|
||||
return "GNSS + Dead Reckoning";
|
||||
case STATUS_TIME:
|
||||
return "Time Only";
|
||||
case STATUS_PPS_FIX:
|
||||
return "PPS Fix";
|
||||
default:
|
||||
|
||||
#ifdef WANT_RFKILL
|
||||
if (rfkill_.getState()) return "Disabled";
|
||||
#endif
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
auto waybar::modules::Gps::update() -> void {
|
||||
sleep(0); // Wait for gps status change
|
||||
|
||||
if ((gps_data_.fix.mode == MODE_NOT_SEEN && hideDisconnected) || (gps_data_.fix.mode == MODE_NO_FIX && hideNoFix)) {
|
||||
event_box_.set_visible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Show the module
|
||||
if (!event_box_.get_visible()) event_box_.set_visible(true);
|
||||
|
||||
std::string tooltip_format;
|
||||
|
||||
if (!alt_) {
|
||||
auto state = getFixModeName();
|
||||
if (!state_.empty() && label_.get_style_context()->has_class(state_)) {
|
||||
label_.get_style_context()->remove_class(state_);
|
||||
}
|
||||
if (config_["format-" + state].isString()) {
|
||||
default_format_ = config_["format-" + state].asString();
|
||||
} else if (config_["format"].isString()) {
|
||||
default_format_ = config_["format"].asString();
|
||||
} else {
|
||||
default_format_ = DEFAULT_FORMAT;
|
||||
}
|
||||
if (config_["tooltip-format-" + state].isString()) {
|
||||
tooltip_format = config_["tooltip-format-" + state].asString();
|
||||
}
|
||||
if (!label_.get_style_context()->has_class(state)) {
|
||||
label_.get_style_context()->add_class(state);
|
||||
}
|
||||
format_ = default_format_;
|
||||
state_ = state;
|
||||
}
|
||||
|
||||
|
||||
auto format = format_;
|
||||
|
||||
fmt::dynamic_format_arg_store<fmt::format_context> store;
|
||||
store.push_back(fmt::arg("mode", getFixModeString()));
|
||||
store.push_back(fmt::arg("status", getFixStatusString()));
|
||||
|
||||
store.push_back(fmt::arg("latitude", gps_data_.fix.latitude));
|
||||
store.push_back(fmt::arg("latitude_error", gps_data_.fix.epy));
|
||||
|
||||
store.push_back(fmt::arg("longitude", gps_data_.fix.longitude));
|
||||
store.push_back(fmt::arg("longitude_error", gps_data_.fix.epx));
|
||||
|
||||
store.push_back(fmt::arg("altitude_hae", gps_data_.fix.altHAE));
|
||||
store.push_back(fmt::arg("altitude_msl", gps_data_.fix.altMSL));
|
||||
store.push_back(fmt::arg("altitude_error", gps_data_.fix.epv));
|
||||
|
||||
store.push_back(fmt::arg("speed", gps_data_.fix.speed));
|
||||
store.push_back(fmt::arg("speed_error", gps_data_.fix.eps));
|
||||
|
||||
store.push_back(fmt::arg("climb", gps_data_.fix.climb));
|
||||
store.push_back(fmt::arg("climb_error", gps_data_.fix.epc));
|
||||
|
||||
store.push_back(fmt::arg("satellites_used", gps_data_.satellites_used));
|
||||
store.push_back(fmt::arg("satellites_visible", gps_data_.satellites_visible));
|
||||
|
||||
auto text = fmt::vformat(format, store);
|
||||
|
||||
if (tooltipEnabled()) {
|
||||
if (tooltip_format.empty() && config_["tooltip-format"].isString()) {
|
||||
tooltip_format = config_["tooltip-format"].asString();
|
||||
}
|
||||
if (!tooltip_format.empty()) {
|
||||
auto tooltip_text = fmt::vformat(tooltip_format, store);
|
||||
if (label_.get_tooltip_text() != tooltip_text) {
|
||||
label_.set_tooltip_markup(tooltip_text);
|
||||
}
|
||||
} else if (label_.get_tooltip_text() != text) {
|
||||
label_.set_tooltip_markup(text);
|
||||
}
|
||||
}
|
||||
label_.set_markup(text);
|
||||
// Call parent update
|
||||
ALabel::update();
|
||||
}
|
||||
|
||||
waybar::modules::Gps::~Gps() {
|
||||
gps_stream(&gps_data_, WATCH_DISABLE, NULL);
|
||||
gps_close(&gps_data_);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user