feat: init repo
parent
53ff4e0d60
commit
fcb6a9aa8b
|
@ -0,0 +1,30 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gtkmm.h>
|
||||||
|
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
|
||||||
|
|
||||||
|
namespace waybar {
|
||||||
|
|
||||||
|
struct Client;
|
||||||
|
|
||||||
|
struct Bar {
|
||||||
|
Bar(Client& client, std::unique_ptr<struct wl_output *>&& output);
|
||||||
|
Bar(const Bar&) = delete;
|
||||||
|
Client& client;
|
||||||
|
Gtk::Window window;
|
||||||
|
struct wl_surface *surface;
|
||||||
|
struct zwlr_layer_surface_v1 *layer_surface;
|
||||||
|
std::unique_ptr<struct wl_output *> output;
|
||||||
|
bool visible = true;
|
||||||
|
auto set_width(int) -> void;
|
||||||
|
auto toggle() -> void;
|
||||||
|
private:
|
||||||
|
auto setup_widgets() -> void;
|
||||||
|
auto setup_css() -> void;
|
||||||
|
|
||||||
|
int width = 10;
|
||||||
|
Glib::RefPtr<Gtk::StyleContext> style_context;
|
||||||
|
Glib::RefPtr<Gtk::CssProvider> css_provider;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
#include <gdk/gdk.h>
|
||||||
|
#include <gtkmm.h>
|
||||||
|
#include <wayland-client.h>
|
||||||
|
|
||||||
|
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
|
||||||
|
|
||||||
|
#include "util/ptr_vec.hpp"
|
||||||
|
|
||||||
|
#include <gdk/gdkwayland.h>
|
||||||
|
|
||||||
|
#include "bar.hpp"
|
||||||
|
|
||||||
|
namespace waybar {
|
||||||
|
|
||||||
|
struct Client {
|
||||||
|
uint32_t height = 30;
|
||||||
|
std::string css_file = "./resources/style.css";
|
||||||
|
|
||||||
|
Gtk::Main gtk_main;
|
||||||
|
|
||||||
|
Glib::RefPtr<Gdk::Display> gdk_display;
|
||||||
|
struct wl_display *wlDisplay;
|
||||||
|
struct wl_registry *registry;
|
||||||
|
struct zwlr_layer_shell_v1 *layer_shell;
|
||||||
|
util::ptr_vec<Bar> bars;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
sigc::signal<void(int, int)> workspace_state;
|
||||||
|
sigc::signal<void(std::string)> focused_window_name;
|
||||||
|
} signals;
|
||||||
|
|
||||||
|
Client(int argc, char* argv[]);
|
||||||
|
void bind_interfaces();
|
||||||
|
auto setup_css();
|
||||||
|
int main(int argc, char* argv[]);
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include "ipc.hpp"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IPC response including type of IPC response, size of payload and the json
|
||||||
|
* encoded payload string.
|
||||||
|
*/
|
||||||
|
struct ipc_response {
|
||||||
|
uint32_t size;
|
||||||
|
uint32_t type;
|
||||||
|
std::string payload;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the path to the IPC socket from sway.
|
||||||
|
*/
|
||||||
|
std::string get_socketpath(void);
|
||||||
|
/**
|
||||||
|
* Opens the sway socket.
|
||||||
|
*/
|
||||||
|
int ipc_open_socket(std::string socket_path);
|
||||||
|
/**
|
||||||
|
* Issues a single IPC command and returns the buffer. len will be updated with
|
||||||
|
* the length of the buffer returned from sway.
|
||||||
|
*/
|
||||||
|
std::string ipc_single_command(int socketfd, uint32_t type, const char *payload, uint32_t *len);
|
||||||
|
/**
|
||||||
|
* Receives a single IPC response and returns an ipc_response.
|
||||||
|
*/
|
||||||
|
struct ipc_response ipc_recv_response(int socketfd);
|
|
@ -0,0 +1,32 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define event_mask(ev) (1 << (ev & 0x7F))
|
||||||
|
|
||||||
|
enum ipc_command_type {
|
||||||
|
// i3 command types - see i3's I3_REPLY_TYPE constants
|
||||||
|
IPC_COMMAND = 0,
|
||||||
|
IPC_GET_WORKSPACES = 1,
|
||||||
|
IPC_SUBSCRIBE = 2,
|
||||||
|
IPC_GET_OUTPUTS = 3,
|
||||||
|
IPC_GET_TREE = 4,
|
||||||
|
IPC_GET_MARKS = 5,
|
||||||
|
IPC_GET_BAR_CONFIG = 6,
|
||||||
|
IPC_GET_VERSION = 7,
|
||||||
|
IPC_GET_BINDING_MODES = 8,
|
||||||
|
IPC_GET_CONFIG = 9,
|
||||||
|
IPC_SEND_TICK = 10,
|
||||||
|
|
||||||
|
// sway-specific command types
|
||||||
|
IPC_GET_INPUTS = 100,
|
||||||
|
IPC_GET_SEATS = 101,
|
||||||
|
|
||||||
|
// Events sent from sway to clients. Events have the highest bits set.
|
||||||
|
IPC_EVENT_WORKSPACE = ((1<<31) | 0),
|
||||||
|
IPC_EVENT_OUTPUT = ((1<<31) | 1),
|
||||||
|
IPC_EVENT_MODE = ((1<<31) | 2),
|
||||||
|
IPC_EVENT_WINDOW = ((1<<31) | 3),
|
||||||
|
IPC_EVENT_BARCONFIG_UPDATE = ((1<<31) | 4),
|
||||||
|
IPC_EVENT_BINDING = ((1<<31) | 5),
|
||||||
|
IPC_EVENT_SHUTDOWN = ((1<<31) | 6),
|
||||||
|
IPC_EVENT_TICK = ((1<<31) | 7),
|
||||||
|
};
|
|
@ -0,0 +1,26 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <gtkmm.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include "util/chrono.hpp"
|
||||||
|
|
||||||
|
namespace waybar::modules {
|
||||||
|
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
class Battery {
|
||||||
|
public:
|
||||||
|
Battery();
|
||||||
|
auto update() -> void;
|
||||||
|
operator Gtk::Widget&();
|
||||||
|
private:
|
||||||
|
static inline const fs::path _data_dir = "/sys/class/power_supply/";
|
||||||
|
std::vector<fs::path> _batteries;
|
||||||
|
util::SleeperThread _thread;
|
||||||
|
Gtk::Label _label;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gtkmm.h>
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include <thread>
|
||||||
|
#include "util/chrono.hpp"
|
||||||
|
|
||||||
|
namespace waybar::modules {
|
||||||
|
|
||||||
|
class Clock {
|
||||||
|
public:
|
||||||
|
Clock();
|
||||||
|
operator Gtk::Widget &();
|
||||||
|
private:
|
||||||
|
Gtk::Label _label;
|
||||||
|
waybar::util::SleeperThread _thread;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <json/json.h>
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include "bar.hpp"
|
||||||
|
#include "client.hpp"
|
||||||
|
#include "util/chrono.hpp"
|
||||||
|
|
||||||
|
namespace waybar::modules {
|
||||||
|
|
||||||
|
class WorkspaceSelector {
|
||||||
|
public:
|
||||||
|
WorkspaceSelector(waybar::Bar &bar);
|
||||||
|
auto update() -> void;
|
||||||
|
operator Gtk::Widget &();
|
||||||
|
private:
|
||||||
|
void _addWorkspace(Json::Value node);
|
||||||
|
Json::Value _getWorkspaces();
|
||||||
|
Bar &_bar;
|
||||||
|
Gtk::Box *_box;
|
||||||
|
std::unordered_map<int, Gtk::Button> _buttons;
|
||||||
|
util::SleeperThread _thread;
|
||||||
|
int _ipcSocketfd;
|
||||||
|
int _ipcEventSocketfd;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,823 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <functional>
|
||||||
|
#include <initializer_list>
|
||||||
|
#include <numeric>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
namespace waybar::util {
|
||||||
|
|
||||||
|
/// Joins a sequence of strings, separating them using `js`
|
||||||
|
template<class StrIterator> // Models InputIterator<std::string>
|
||||||
|
std::string join_strings(StrIterator b, StrIterator e, std::string_view js = ", ")
|
||||||
|
{
|
||||||
|
std::string result;
|
||||||
|
std::for_each(b, e, [&](auto&& s) {
|
||||||
|
if (!result.empty()) {
|
||||||
|
result.append(js);
|
||||||
|
}
|
||||||
|
result.append(s);
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const char* nonull(const char* str) {
|
||||||
|
if (str == nullptr) return "";
|
||||||
|
return str;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline bool iequals(std::string_view a, std::string_view b)
|
||||||
|
{
|
||||||
|
return std::equal(a.begin(), a.end(), b.begin(), b.end(),
|
||||||
|
[](char a, char b) { return tolower(a) == tolower(b); });
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool starts_with(std::string_view prefix, std::string_view a)
|
||||||
|
{
|
||||||
|
return a.compare(0, prefix.size(), prefix) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool ends_with(std::string_view prefix, std::string_view a)
|
||||||
|
{
|
||||||
|
return a.compare(a.size() - prefix.size(), prefix.size(), prefix) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a closure which compares the adress any reference to T to the address of t
|
||||||
|
template<typename T>
|
||||||
|
constexpr auto addr_eq(T&& t) {
|
||||||
|
return [&t] (auto&& t2) {
|
||||||
|
return &t == &t2;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
bool erase_this(std::vector<T>& cont, T* el)
|
||||||
|
{
|
||||||
|
if (el < cont.data() && el >= cont.data() + cont.size()) return false;
|
||||||
|
cont.erase(cont.begin() + (el - cont.data()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
bool erase_this(std::vector<T>& cont, T& el)
|
||||||
|
{
|
||||||
|
return erase_this(cont, &el);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
template<class Func, int... ns>
|
||||||
|
constexpr auto generate_array_impl(std::integer_sequence<int, ns...>&&, Func&& gen)
|
||||||
|
{
|
||||||
|
return std::array<std::decay_t<decltype(std::invoke(gen, std::declval<int>()))>,
|
||||||
|
sizeof...(ns)>{{std::invoke(gen, ns)...}};
|
||||||
|
}
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
template<int n, class Func>
|
||||||
|
constexpr auto generate_array(Func&& gen)
|
||||||
|
{
|
||||||
|
auto intseq = std::make_integer_sequence<int, n>();
|
||||||
|
return detail::generate_array_impl(std::move(intseq), std::forward<Func>(gen));
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace view {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
template<typename T>
|
||||||
|
using store_or_ref_t = std::conditional_t<std::is_rvalue_reference_v<T>, std::decay_t<T>, T&>;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont>
|
||||||
|
struct reverse {
|
||||||
|
|
||||||
|
reverse(Cont&& cont) noexcept : _container(std::forward<Cont>(cont)) {}
|
||||||
|
|
||||||
|
auto begin()
|
||||||
|
{
|
||||||
|
return std::rbegin(_container);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto end()
|
||||||
|
{
|
||||||
|
return std::rend(_container);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto begin() const
|
||||||
|
{
|
||||||
|
return std::rbegin(_container);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto end() const
|
||||||
|
{
|
||||||
|
return std::rend(_container);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto cbegin() const
|
||||||
|
{
|
||||||
|
return std::crbegin(_container);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto cend() const
|
||||||
|
{
|
||||||
|
return std::crend(_container);
|
||||||
|
}
|
||||||
|
|
||||||
|
detail::store_or_ref_t<Cont&&> _container;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename ContRef>
|
||||||
|
reverse(ContRef&& cont) -> reverse<ContRef&&>;
|
||||||
|
|
||||||
|
template<typename Cont>
|
||||||
|
struct constant {
|
||||||
|
constant(Cont&& cont) noexcept : _container(std::forward<Cont>(cont)){};
|
||||||
|
|
||||||
|
auto begin() const
|
||||||
|
{
|
||||||
|
return std::cbegin(_container);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto end() const
|
||||||
|
{
|
||||||
|
return std::cend(_container);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto cbegin() const
|
||||||
|
{
|
||||||
|
return std::cbegin(_container);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto cend() const
|
||||||
|
{
|
||||||
|
return std::cend(_container);
|
||||||
|
}
|
||||||
|
|
||||||
|
detail::store_or_ref_t<Cont&&> _container;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename ContRef>
|
||||||
|
constant(ContRef&& cont) -> constant<ContRef&&>;
|
||||||
|
|
||||||
|
} // namespace view
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Range algorithms
|
||||||
|
*/
|
||||||
|
|
||||||
|
template<typename InputIt, typename Size, typename F>
|
||||||
|
constexpr InputIt for_each_n(InputIt&& first, Size n, F&& f)
|
||||||
|
{
|
||||||
|
for (Size i = 0; i < n; ++first, ++i) {
|
||||||
|
std::invoke(f, *first);
|
||||||
|
}
|
||||||
|
return first;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `for_each` with access to an index value. Function called as `f(*it, i)`
|
||||||
|
///
|
||||||
|
/// For each item in range `[first, last)`, invoke `f` with args
|
||||||
|
/// `*iter, i` where `iter` is the current iterator, and `i` is
|
||||||
|
/// an incrementing value, starting at zero. Use this instead of
|
||||||
|
/// raw indexed loops wherever possible.
|
||||||
|
///
|
||||||
|
/// \param first Input iterator to the begining of the range
|
||||||
|
/// \param last Input iterator to the end of the range
|
||||||
|
/// \param f Must be invocable with arguments `value_type`, `std::size_t`
|
||||||
|
/// \returns The number of iterations performed
|
||||||
|
template<typename InputIt, typename F>
|
||||||
|
constexpr std::size_t indexed_for(InputIt&& first, InputIt&& last, F&& f)
|
||||||
|
{
|
||||||
|
std::size_t i = 0;
|
||||||
|
std::for_each(std::forward<InputIt>(first), std::forward<InputIt>(last), [&](auto&& a) {
|
||||||
|
std::invoke(f, a, i);
|
||||||
|
i++;
|
||||||
|
});
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Rng, typename F>
|
||||||
|
constexpr std::size_t indexed_for(Rng&& rng, F&& f)
|
||||||
|
{
|
||||||
|
return indexed_for(std::begin(rng), std::end(rng), std::forward<F>(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// `for_each_n` with access to an index value. Function called as `f(*it, i)`
|
||||||
|
///
|
||||||
|
/// for `n` iterations, invoke `f` with args `*iter, i`
|
||||||
|
/// where `iter` is the current iterator starting with `first`,
|
||||||
|
/// and `i` is an incrementing value, starting at zero.
|
||||||
|
/// Use this instead of raw indexed loops wherever possible.
|
||||||
|
///
|
||||||
|
/// \param first Input iterator to the begining of the range
|
||||||
|
/// \param n Number of iterations to go through
|
||||||
|
/// \param f Must be invocable with arguments `value_type`, `std::size_t`
|
||||||
|
/// \returns An iterator one past the last one visited
|
||||||
|
template<class InputIt, class Size, class F>
|
||||||
|
constexpr InputIt indexed_for_n(InputIt first, Size n, F&& f)
|
||||||
|
{
|
||||||
|
for (Size i = 0; i < n; ++first, ++i) {
|
||||||
|
std::invoke(f, *first, i);
|
||||||
|
}
|
||||||
|
return first;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Rng, class Size, class F>
|
||||||
|
constexpr std::size_t indexed_for_n(Rng&& rng, Size n, F&& f)
|
||||||
|
{
|
||||||
|
return indexed_for_n(std::begin(rng), std::end(rng), n, std::forward<F>(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Iter1, typename Iter2, typename F>
|
||||||
|
constexpr void for_both(Iter1&& f1, Iter1&& l1, Iter2&& f2, Iter2&& l2, F&& f)
|
||||||
|
{
|
||||||
|
Iter1 i1 = std::forward<Iter1>(f1);
|
||||||
|
Iter2 i2 = std::forward<Iter2>(f2);
|
||||||
|
for (; i1 != l1 && i2 != l2; i1++, i2++) {
|
||||||
|
std::invoke(f, *i1, *i2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Rng1, typename Rng2, typename F>
|
||||||
|
constexpr void for_both(Rng1&& r1, Rng2&& r2, F&& f)
|
||||||
|
{
|
||||||
|
for_both(std::begin(r1), std::end(r1), std::begin(r2), std::end(r2), std::forward<F>(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Range based standard algorithms
|
||||||
|
*
|
||||||
|
* Thanks, chris from SO!
|
||||||
|
*/
|
||||||
|
|
||||||
|
template<typename Cont, typename T>
|
||||||
|
constexpr auto accumulate(Cont&& cont, T&& init)
|
||||||
|
{
|
||||||
|
// TODO C++20: std::accumulate is constexpr
|
||||||
|
using std::begin, std::end;
|
||||||
|
auto first = begin(cont);
|
||||||
|
auto last = end(cont);
|
||||||
|
for (; first != last; ++first) init = init + *first;
|
||||||
|
return init;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename T, typename BinaryOperation>
|
||||||
|
constexpr auto accumulate(Cont&& cont, T&& init, BinaryOperation&& op)
|
||||||
|
{
|
||||||
|
// TODO C++20: std::accumulate is constexpr
|
||||||
|
using std::begin, std::end;
|
||||||
|
auto first = begin(cont);
|
||||||
|
auto last = end(cont);
|
||||||
|
for (; first != last; ++first) init = op(init, *first);
|
||||||
|
return init;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename OutputIterator>
|
||||||
|
decltype(auto) adjacent_difference(Cont&& cont, OutputIterator&& first)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::adjacent_difference(begin(cont), end(cont), std::forward<OutputIterator>(first));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont>
|
||||||
|
decltype(auto) prev_permutation(Cont&& cont)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::prev_permutation(begin(cont), end(cont));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename Compare>
|
||||||
|
decltype(auto) prev_permutation(Cont&& cont, Compare&& comp)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::prev_permutation(begin(cont), end(cont), std::forward<Compare>(comp));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont>
|
||||||
|
decltype(auto) push_heap(Cont&& cont)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::push_heap(begin(cont), end(cont));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename Compare>
|
||||||
|
decltype(auto) push_heap(Cont&& cont, Compare&& comp)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::push_heap(begin(cont), end(cont), std::forward<Compare>(comp));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename T>
|
||||||
|
decltype(auto) remove(Cont&& cont, T&& value)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::remove(begin(cont), end(cont), std::forward<T>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename OutputIterator, typename T>
|
||||||
|
decltype(auto) remove_copy(Cont&& cont, OutputIterator&& first, T&& value)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::remove_copy(begin(cont), end(cont), std::forward<OutputIterator>(first),
|
||||||
|
std::forward<T>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename OutputIterator, typename UnaryPredicate>
|
||||||
|
decltype(auto) remove_copy_if(Cont&& cont, OutputIterator&& first, UnaryPredicate&& p)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::remove_copy_if(begin(cont), end(cont), std::forward<OutputIterator>(first),
|
||||||
|
std::forward<UnaryPredicate>(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename UnaryPredicate>
|
||||||
|
decltype(auto) remove_if(Cont&& cont, UnaryPredicate&& p)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::remove_if(begin(cont), end(cont), std::forward<UnaryPredicate>(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename T, typename T2>
|
||||||
|
decltype(auto) replace(Cont&& cont, T&& old_value, T2&& new_value)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::replace(begin(cont), end(cont), std::forward<T>(old_value),
|
||||||
|
std::forward<T2>(new_value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename OutputIterator, typename T, typename T2>
|
||||||
|
decltype(auto) replace_copy(Cont&& cont, OutputIterator&& first, T&& old_value, T2&& new_value)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::replace_copy(begin(cont), end(cont), std::forward<OutputIterator>(first),
|
||||||
|
std::forward<T>(old_value), std::forward<T2>(old_value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename OutputIterator, typename UnaryPredicate, typename T>
|
||||||
|
decltype(auto) replace_copy_if(Cont&& cont,
|
||||||
|
OutputIterator&& first,
|
||||||
|
UnaryPredicate&& p,
|
||||||
|
T&& new_value)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::replace_copy(begin(cont), end(cont), std::forward<OutputIterator>(first),
|
||||||
|
std::forward<UnaryPredicate>(p), std::forward<T>(new_value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename UnaryPredicate, typename T>
|
||||||
|
decltype(auto) replace_if(Cont&& cont, UnaryPredicate&& p, T&& new_value)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::replace_if(begin(cont), end(cont), std::forward<UnaryPredicate>(p),
|
||||||
|
std::forward<T>(new_value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont>
|
||||||
|
decltype(auto) reverse(Cont&& cont)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::reverse(begin(cont), end(cont));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename OutputIterator>
|
||||||
|
decltype(auto) reverse_copy(Cont&& cont, OutputIterator&& first)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::reverse_copy(begin(cont), end(cont), std::forward<OutputIterator>(first));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename ForwardIterator>
|
||||||
|
decltype(auto) rotate(Cont&& cont, ForwardIterator&& new_first)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::rotate(begin(cont), std::forward<ForwardIterator>(new_first), end(cont));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename ForwardIterator, typename OutputIterator>
|
||||||
|
decltype(auto) rotate_copy(Cont&& cont, ForwardIterator&& new_first, OutputIterator&& first)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::rotate_copy(begin(cont), std::forward<ForwardIterator>(new_first), end(cont),
|
||||||
|
std::forward<OutputIterator>(first));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename Cont2>
|
||||||
|
decltype(auto) search(Cont&& cont, Cont2&& cont2)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::search(begin(cont), end(cont), begin(cont2), end(cont2));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename Cont2, typename BinaryPredicate>
|
||||||
|
decltype(auto) search(Cont&& cont, Cont2&& cont2, BinaryPredicate&& p)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::search(begin(cont), end(cont), begin(cont2), end(cont2),
|
||||||
|
std::forward<BinaryPredicate>(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename Size, typename T>
|
||||||
|
decltype(auto) search_n(Cont&& cont, Size count, T&& value)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::search_n(begin(cont), end(cont), count, std::forward<T>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename Size, typename T, typename BinaryPredicate>
|
||||||
|
decltype(auto) search_n(Cont&& cont, Size count, T&& value, BinaryPredicate&& p)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::search_n(begin(cont), end(cont), count, std::forward<T>(value),
|
||||||
|
std::forward<BinaryPredicate>(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename Cont2, typename OutputIterator>
|
||||||
|
decltype(auto) set_difference(Cont&& cont, Cont2&& cont2, OutputIterator&& first)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::set_difference(begin(cont), end(cont), begin(cont2), end(cont2),
|
||||||
|
std::forward<OutputIterator>(first));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename Cont2, typename OutputIterator, typename Compare>
|
||||||
|
decltype(auto) set_difference(Cont&& cont, Cont2&& cont2, OutputIterator&& first, Compare&& comp)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::set_difference(begin(cont), end(cont), begin(cont2), end(cont2),
|
||||||
|
std::forward<OutputIterator>(first), std::forward<Compare>(comp));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename Cont2, typename OutputIterator>
|
||||||
|
decltype(auto) set_intersection(Cont&& cont, Cont2&& cont2, OutputIterator&& first)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::set_intersection(begin(cont), end(cont), begin(cont2), end(cont2),
|
||||||
|
std::forward<OutputIterator>(first));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename Cont2, typename OutputIterator, typename Compare>
|
||||||
|
decltype(auto) set_intersection(Cont&& cont,
|
||||||
|
Cont2&& cont2,
|
||||||
|
OutputIterator&& first,
|
||||||
|
Compare&& comp)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::set_intersection(begin(cont), end(cont), begin(cont2), end(cont2),
|
||||||
|
std::forward<OutputIterator>(first), std::forward<Compare>(comp));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename Cont2, typename OutputIterator>
|
||||||
|
decltype(auto) set_symmetric_difference(Cont&& cont, Cont2&& cont2, OutputIterator&& first)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::set_symmetric_difference(begin(cont), end(cont), begin(cont2), end(cont2),
|
||||||
|
std::forward<OutputIterator>(first));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename Cont2, typename OutputIterator, typename Compare>
|
||||||
|
decltype(auto) set_symmetric_difference(Cont&& cont,
|
||||||
|
Cont2&& cont2,
|
||||||
|
OutputIterator&& first,
|
||||||
|
Compare&& comp)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::set_symmetric_difference(begin(cont), end(cont), begin(cont2), end(cont2),
|
||||||
|
std::forward<OutputIterator>(first),
|
||||||
|
std::forward<Compare>(comp));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename Cont2, typename OutputIterator>
|
||||||
|
decltype(auto) set_union(Cont&& cont, Cont2&& cont2, OutputIterator&& first)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::set_union(begin(cont), end(cont), begin(cont2), end(cont2),
|
||||||
|
std::forward<OutputIterator>(first));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename Cont2, typename OutputIterator, typename Compare>
|
||||||
|
decltype(auto) set_union(Cont&& cont, Cont2&& cont2, OutputIterator&& first, Compare&& comp)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::set_union(begin(cont), end(cont), begin(cont2), end(cont2),
|
||||||
|
std::forward<OutputIterator>(first), std::forward<Compare>(comp));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename UniformRandomNumberGenerator>
|
||||||
|
decltype(auto) shuffle(Cont&& cont, UniformRandomNumberGenerator&& g)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::shuffle(begin(cont), end(cont), std::forward<UniformRandomNumberGenerator>(g));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont>
|
||||||
|
decltype(auto) sort(Cont&& cont)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::sort(begin(cont), end(cont));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename Compare>
|
||||||
|
decltype(auto) sort(Cont&& cont, Compare&& comp)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::sort(begin(cont), end(cont), std::forward<Compare>(comp));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont>
|
||||||
|
decltype(auto) sort_heap(Cont&& cont)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::sort_heap(begin(cont), end(cont));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename Compare>
|
||||||
|
decltype(auto) sort_heap(Cont&& cont, Compare&& comp)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::sort_heap(begin(cont), end(cont), std::forward<Compare>(comp));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename UnaryPredicate>
|
||||||
|
decltype(auto) stable_partition(Cont&& cont, UnaryPredicate&& p)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::stable_partition(begin(cont), end(cont), std::forward<UnaryPredicate>(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont>
|
||||||
|
decltype(auto) stable_sort(Cont&& cont)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::stable_sort(begin(cont), end(cont));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename Compare>
|
||||||
|
decltype(auto) stable_sort(Cont&& cont, Compare&& comp)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::stable_sort(begin(cont), end(cont), std::forward<Compare>(comp));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename ForwardIterator>
|
||||||
|
decltype(auto) swap_ranges(Cont&& cont, ForwardIterator&& first)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::swap_ranges(begin(cont), end(cont), std::forward<ForwardIterator>(first));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename Cont2, typename F>
|
||||||
|
auto transform(Cont&& cont, Cont2&& cont2, F&& f) -> decltype(begin(cont2))
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::transform(begin(cont), end(cont), begin(cont2), std::forward<F>(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename Iter, typename F>
|
||||||
|
decltype(auto) transform(Cont&& cont, Iter&& iter, F&& f)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::transform(begin(cont), end(cont), std::forward<Iter>(iter), std::forward<F>(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename Cont2, typename Cont3, typename BinaryPredicate>
|
||||||
|
auto transform(Cont&& cont, Cont2&& cont2, Cont3&& cont3, BinaryPredicate&& f)
|
||||||
|
-> decltype(begin(cont2), begin(cont3))
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::transform(begin(cont), end(cont), begin(cont2), begin(cont3),
|
||||||
|
std::forward<BinaryPredicate>(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename InputIterator, typename Cont3, typename BinaryPredicate>
|
||||||
|
auto transform(Cont&& cont, InputIterator&& iter, Cont3&& cont3, BinaryPredicate&& f)
|
||||||
|
-> decltype(begin(cont), begin(cont3))
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::transform(begin(cont), end(cont), std::forward<InputIterator>(iter), begin(cont3),
|
||||||
|
std::forward<BinaryPredicate>(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename Cont2, typename InputIterator, typename BinaryPredicate>
|
||||||
|
auto transform(Cont&& cont, Cont2&& cont2, InputIterator&& iter, BinaryPredicate&& f)
|
||||||
|
-> decltype(begin(cont), begin(cont2), iter)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::transform(begin(cont), end(cont), begin(cont2), std::forward<InputIterator>(iter),
|
||||||
|
std::forward<BinaryPredicate>(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename InputIterator, typename OutputIterator, typename BinaryOperation>
|
||||||
|
decltype(auto) transform(Cont&& cont,
|
||||||
|
InputIterator&& firstIn,
|
||||||
|
OutputIterator&& firstOut,
|
||||||
|
BinaryOperation&& op)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::transform(begin(cont), end(cont), std::forward<InputIterator>(firstIn),
|
||||||
|
std::forward<OutputIterator>(firstOut),
|
||||||
|
std::forward<BinaryOperation>(op));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont>
|
||||||
|
decltype(auto) unique(Cont&& cont)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::unique(begin(cont), end(cont));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename BinaryPredicate>
|
||||||
|
decltype(auto) unique(Cont&& cont, BinaryPredicate&& p)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::unique(begin(cont), end(cont), std::forward<BinaryPredicate>(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename OutputIterator>
|
||||||
|
decltype(auto) unique_copy(Cont&& cont, OutputIterator&& first)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::unique_copy(begin(cont), end(cont), std::forward<OutputIterator>(first));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename OutputIterator, typename BinaryPredicate>
|
||||||
|
decltype(auto) unique_copy(Cont&& cont, OutputIterator&& first, BinaryPredicate&& p)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::unique_copy(begin(cont), end(cont), std::forward<OutputIterator>(first),
|
||||||
|
std::forward<BinaryPredicate>(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename T>
|
||||||
|
decltype(auto) upper_bound(Cont&& cont, T&& value)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::upper_bound(begin(cont), end(cont), std::forward<T>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename T, typename Compare>
|
||||||
|
decltype(auto) upper_bound(Cont&& cont, T&& value, Compare&& comp)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::upper_bound(begin(cont), end(cont), std::forward<T>(value),
|
||||||
|
std::forward<Compare>(comp));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename OutputIterator>
|
||||||
|
decltype(auto) copy(Cont&& cont, OutputIterator&& first)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::copy(begin(cont), end(cont), std::forward<OutputIterator>(first));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename OutputIterator, typename UnaryPredicate>
|
||||||
|
decltype(auto) copy_if(Cont&& cont, OutputIterator&& first, UnaryPredicate&& p)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::copy_if(begin(cont), end(cont), std::forward<OutputIterator>(first),
|
||||||
|
std::forward<UnaryPredicate>(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename T>
|
||||||
|
decltype(auto) fill(Cont&& cont, T&& value)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::fill(begin(cont), end(cont), std::forward<T>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename T>
|
||||||
|
decltype(auto) fill_n(Cont&& cont, std::size_t n, T&& value)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::fill_n(begin(cont), n, std::forward<T>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename UnaryPredicate>
|
||||||
|
decltype(auto) any_of(Cont&& cont, UnaryPredicate&& p)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::any_of(begin(cont), end(cont), std::forward<UnaryPredicate>(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename UnaryPredicate>
|
||||||
|
decltype(auto) all_of(Cont&& cont, UnaryPredicate&& p)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::all_of(begin(cont), end(cont), std::forward<UnaryPredicate>(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename UnaryPredicate>
|
||||||
|
decltype(auto) none_of(Cont&& cont, UnaryPredicate&& p)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::none_of(begin(cont), end(cont), std::forward<UnaryPredicate>(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont>
|
||||||
|
decltype(auto) max_element(Cont&& cont)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::max_element(begin(cont), end(cont));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont>
|
||||||
|
decltype(auto) min_element(Cont&& cont)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::min_element(begin(cont), end(cont));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename Compare>
|
||||||
|
decltype(auto) min_element(Cont&& cont, Compare&& f)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::min_element(begin(cont), end(cont), std::forward<Compare>(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename Compare>
|
||||||
|
decltype(auto) max_element(Cont&& cont, Compare&& f)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::max_element(begin(cont), end(cont), std::forward<Compare>(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename T>
|
||||||
|
decltype(auto) find(Cont&& cont, T&& t)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::find(begin(cont), end(cont), std::forward<T>(t));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Cont, typename UnaryPredicate>
|
||||||
|
decltype(auto) find_if(Cont&& cont, UnaryPredicate&& f)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::find_if(begin(cont), end(cont), std::forward<UnaryPredicate>(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace waybar::util
|
|
@ -0,0 +1,86 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <ctime>
|
||||||
|
#include <functional>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
namespace waybar::chrono {
|
||||||
|
|
||||||
|
using namespace std::chrono;
|
||||||
|
|
||||||
|
using clock = std::chrono::system_clock;
|
||||||
|
using duration = clock::duration;
|
||||||
|
using time_point = std::chrono::time_point<clock, duration>;
|
||||||
|
|
||||||
|
inline struct timespec to_timespec(time_point t) noexcept
|
||||||
|
{
|
||||||
|
long secs = duration_cast<seconds>(t.time_since_epoch()).count();
|
||||||
|
long nsc = duration_cast<nanoseconds>(t.time_since_epoch() % seconds(1)).count();
|
||||||
|
return {secs, nsc};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline time_point to_time_point(struct timespec t) noexcept
|
||||||
|
{
|
||||||
|
return time_point(duration_cast<duration>(seconds(t.tv_sec) + nanoseconds(t.tv_nsec)));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace waybar::util {
|
||||||
|
|
||||||
|
struct SleeperThread {
|
||||||
|
SleeperThread() = default;
|
||||||
|
|
||||||
|
SleeperThread(std::function<void()> func)
|
||||||
|
: thread{[this, func] {
|
||||||
|
do {
|
||||||
|
func();
|
||||||
|
} while (do_run);
|
||||||
|
}}
|
||||||
|
{}
|
||||||
|
|
||||||
|
SleeperThread& operator=(std::function<void()> func)
|
||||||
|
{
|
||||||
|
thread = std::thread([this, func] {
|
||||||
|
do {
|
||||||
|
func();
|
||||||
|
} while (do_run);
|
||||||
|
});
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
auto sleep_for(chrono::duration dur)
|
||||||
|
{
|
||||||
|
auto lock = std::unique_lock(mutex);
|
||||||
|
return condvar.wait_for(lock, dur);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto sleep_until(chrono::time_point time)
|
||||||
|
{
|
||||||
|
auto lock = std::unique_lock(mutex);
|
||||||
|
return condvar.wait_until(lock, time);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto wake_up()
|
||||||
|
{
|
||||||
|
condvar.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
~SleeperThread()
|
||||||
|
{
|
||||||
|
do_run = false;
|
||||||
|
condvar.notify_all();
|
||||||
|
thread.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::thread thread;
|
||||||
|
std::condition_variable condvar;
|
||||||
|
std::mutex mutex;
|
||||||
|
bool do_run = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,581 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <memory>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <vector>
|
||||||
|
#include "algorithm.hpp"
|
||||||
|
|
||||||
|
namespace waybar::util {
|
||||||
|
|
||||||
|
/// An iterator wrapper that dereferences twice.
|
||||||
|
template<typename Iter>
|
||||||
|
struct double_iterator {
|
||||||
|
using wrapped = Iter;
|
||||||
|
|
||||||
|
using value_type = std::decay_t<decltype(*std::declval<typename wrapped::value_type>())>;
|
||||||
|
using difference_type = typename wrapped::difference_type;
|
||||||
|
using reference = value_type&;
|
||||||
|
using pointer = value_type*;
|
||||||
|
using iterator_category = std::random_access_iterator_tag;
|
||||||
|
|
||||||
|
using self_t = double_iterator<Iter>;
|
||||||
|
|
||||||
|
double_iterator(wrapped w) : _iter(std::move(w)) {}
|
||||||
|
double_iterator() : _iter() {}
|
||||||
|
|
||||||
|
reference operator*() const
|
||||||
|
{
|
||||||
|
return (**_iter);
|
||||||
|
}
|
||||||
|
pointer operator->() const
|
||||||
|
{
|
||||||
|
return &(**_iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
self_t& operator++()
|
||||||
|
{
|
||||||
|
_iter.operator++();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
self_t operator++(int i)
|
||||||
|
{
|
||||||
|
return _iter.operator++(i);
|
||||||
|
}
|
||||||
|
self_t& operator--()
|
||||||
|
{
|
||||||
|
_iter.operator--();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
self_t operator--(int i)
|
||||||
|
{
|
||||||
|
return _iter.operator--(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto operator==(const self_t& rhs) const noexcept
|
||||||
|
{
|
||||||
|
return _iter == rhs._iter;
|
||||||
|
}
|
||||||
|
auto operator!=(const self_t& rhs) const noexcept
|
||||||
|
{
|
||||||
|
return _iter != rhs._iter;
|
||||||
|
}
|
||||||
|
auto operator<(const self_t& rhs) const noexcept
|
||||||
|
{
|
||||||
|
return _iter < rhs._iter;
|
||||||
|
}
|
||||||
|
auto operator>(const self_t& rhs) const noexcept
|
||||||
|
{
|
||||||
|
return _iter > rhs._iter;
|
||||||
|
}
|
||||||
|
auto operator<=(const self_t& rhs) const noexcept
|
||||||
|
{
|
||||||
|
return _iter <= rhs._iter;
|
||||||
|
}
|
||||||
|
auto operator>=(const self_t& rhs) const noexcept
|
||||||
|
{
|
||||||
|
return _iter >= rhs._iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
self_t operator+(difference_type d) const noexcept
|
||||||
|
{
|
||||||
|
return _iter + d;
|
||||||
|
}
|
||||||
|
self_t operator-(difference_type d) const noexcept
|
||||||
|
{
|
||||||
|
return _iter - d;
|
||||||
|
}
|
||||||
|
auto operator-(const self_t& rhs) const noexcept
|
||||||
|
{
|
||||||
|
return _iter - rhs._iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
self_t& operator+=(difference_type d)
|
||||||
|
{
|
||||||
|
_iter += d;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
self_t& operator-=(difference_type d)
|
||||||
|
{
|
||||||
|
_iter -= d;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
operator wrapped&()
|
||||||
|
{
|
||||||
|
return _iter;
|
||||||
|
}
|
||||||
|
operator const wrapped&() const
|
||||||
|
{
|
||||||
|
return _iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapped& data()
|
||||||
|
{
|
||||||
|
return _iter;
|
||||||
|
}
|
||||||
|
const wrapped& data() const
|
||||||
|
{
|
||||||
|
return _iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
wrapped _iter;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Iter>
|
||||||
|
auto operator+(typename double_iterator<Iter>::difference_type diff, double_iterator<Iter> iter)
|
||||||
|
{
|
||||||
|
return iter + diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// To avoid clients being moved, they are stored in unique_ptrs, which are
|
||||||
|
/// moved around in a vector. This class is purely for convenience, to still
|
||||||
|
/// have iterator semantics, and a few other utility functions
|
||||||
|
template<typename T>
|
||||||
|
struct ptr_vec {
|
||||||
|
using value_type = T;
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<value_type>> _order;
|
||||||
|
|
||||||
|
using iterator = double_iterator<typename decltype(_order)::iterator>;
|
||||||
|
using const_iterator = double_iterator<typename decltype(_order)::const_iterator>;
|
||||||
|
|
||||||
|
using reverse_iterator = double_iterator<typename decltype(_order)::reverse_iterator>;
|
||||||
|
using const_reverse_iterator =
|
||||||
|
double_iterator<typename decltype(_order)::const_reverse_iterator>;
|
||||||
|
|
||||||
|
value_type& push_back(const value_type& v)
|
||||||
|
{
|
||||||
|
auto ptr = std::make_unique<value_type>(v);
|
||||||
|
auto res = ptr.get();
|
||||||
|
_order.push_back(std::move(ptr));
|
||||||
|
return *res;
|
||||||
|
}
|
||||||
|
|
||||||
|
value_type& push_back(value_type&& v)
|
||||||
|
{
|
||||||
|
auto ptr = std::make_unique<value_type>(std::move(v));
|
||||||
|
auto res = ptr.get();
|
||||||
|
_order.push_back(std::move(ptr));
|
||||||
|
return *res;
|
||||||
|
}
|
||||||
|
|
||||||
|
value_type& push_back(std::unique_ptr<value_type> ptr)
|
||||||
|
{
|
||||||
|
auto res = ptr.get();
|
||||||
|
_order.push_back(std::move(ptr));
|
||||||
|
return *res;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
value_type& emplace_back(Args&&... args)
|
||||||
|
{
|
||||||
|
return push_back(std::make_unique<value_type>(std::forward<Args>(args)...));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<value_type> erase(const value_type& v)
|
||||||
|
{
|
||||||
|
auto iter =
|
||||||
|
std::find_if(_order.begin(), _order.end(), [&v](auto&& uptr) { return uptr.get() == &v; });
|
||||||
|
if (iter != _order.end()) {
|
||||||
|
auto uptr = std::move(*iter);
|
||||||
|
_order.erase(iter);
|
||||||
|
return uptr;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator rotate_to_back(const value_type& v)
|
||||||
|
{
|
||||||
|
auto iter =
|
||||||
|
std::find_if(_order.begin(), _order.end(), [&v](auto&& uptr) { return uptr.get() == &v; });
|
||||||
|
return rotate_to_back(iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator rotate_to_back(iterator iter)
|
||||||
|
{
|
||||||
|
if (iter != _order.end()) {
|
||||||
|
{
|
||||||
|
return std::rotate(iter.data(), iter.data() + 1, _order.end());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator rotate_to_front(const value_type& v)
|
||||||
|
{
|
||||||
|
auto iter =
|
||||||
|
std::find_if(_order.begin(), _order.end(), [&v](auto&& uptr) { return uptr.get() == &v; });
|
||||||
|
return rotate_to_front(iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator rotate_to_front(iterator iter)
|
||||||
|
{
|
||||||
|
if (iter != _order.end()) {
|
||||||
|
{
|
||||||
|
return std::rotate(_order.begin(), iter.data(), iter.data() + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t size() const noexcept
|
||||||
|
{
|
||||||
|
return _order.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() const noexcept
|
||||||
|
{
|
||||||
|
return _order.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t capacity() const noexcept
|
||||||
|
{
|
||||||
|
return _order.capacity();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t max_size() const noexcept
|
||||||
|
{
|
||||||
|
return _order.max_size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void reserve(std::size_t new_cap)
|
||||||
|
{
|
||||||
|
_order.reserve(new_cap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void shrink_to_fit()
|
||||||
|
{
|
||||||
|
_order.shrink_to_fit();
|
||||||
|
}
|
||||||
|
|
||||||
|
value_type& operator[](std::size_t n)
|
||||||
|
{
|
||||||
|
return *_order[n];
|
||||||
|
}
|
||||||
|
|
||||||
|
const value_type& operator[](std::size_t n) const
|
||||||
|
{
|
||||||
|
return *_order[n];
|
||||||
|
}
|
||||||
|
|
||||||
|
value_type& at(std::size_t n)
|
||||||
|
{
|
||||||
|
return *_order.at(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
const value_type& at(std::size_t n) const
|
||||||
|
{
|
||||||
|
return *_order.at(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator begin()
|
||||||
|
{
|
||||||
|
return _order.begin();
|
||||||
|
}
|
||||||
|
iterator end()
|
||||||
|
{
|
||||||
|
return _order.end();
|
||||||
|
}
|
||||||
|
const_iterator begin() const
|
||||||
|
{
|
||||||
|
return _order.begin();
|
||||||
|
}
|
||||||
|
const_iterator end() const
|
||||||
|
{
|
||||||
|
return _order.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
reverse_iterator rbegin()
|
||||||
|
{
|
||||||
|
return _order.rbegin();
|
||||||
|
}
|
||||||
|
reverse_iterator rend()
|
||||||
|
{
|
||||||
|
return _order.rend();
|
||||||
|
}
|
||||||
|
const_reverse_iterator rbegin() const
|
||||||
|
{
|
||||||
|
return _order.rbegin();
|
||||||
|
}
|
||||||
|
const_reverse_iterator rend() const
|
||||||
|
{
|
||||||
|
return _order.rend();
|
||||||
|
}
|
||||||
|
|
||||||
|
value_type& front()
|
||||||
|
{
|
||||||
|
return *_order.front();
|
||||||
|
}
|
||||||
|
|
||||||
|
value_type& back()
|
||||||
|
{
|
||||||
|
return *_order.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
const value_type& front() const
|
||||||
|
{
|
||||||
|
return *_order.front();
|
||||||
|
}
|
||||||
|
|
||||||
|
const value_type& back() const
|
||||||
|
{
|
||||||
|
return *_order.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<value_type>>& underlying() {
|
||||||
|
return _order;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, typename T2>
|
||||||
|
std::unique_ptr<T> erase_this(ptr_vec<T>& vec, T2* el)
|
||||||
|
{
|
||||||
|
return vec.erase(*el);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename T2>
|
||||||
|
std::unique_ptr<T> erase_this(ptr_vec<T>& vec, T2& el)
|
||||||
|
{
|
||||||
|
return vec.erase(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct non_null_ptr {
|
||||||
|
non_null_ptr() = delete;
|
||||||
|
constexpr non_null_ptr(T* ptr) : _ptr(ptr)
|
||||||
|
{
|
||||||
|
assert(ptr != nullptr);
|
||||||
|
}
|
||||||
|
non_null_ptr(std::nullptr_t) = delete;
|
||||||
|
|
||||||
|
constexpr non_null_ptr(const non_null_ptr&) = default;
|
||||||
|
constexpr non_null_ptr(non_null_ptr&&) = default;
|
||||||
|
constexpr non_null_ptr& operator=(const non_null_ptr&) = default;
|
||||||
|
constexpr non_null_ptr& operator=(non_null_ptr&&) = default;
|
||||||
|
|
||||||
|
constexpr T& operator*() const noexcept
|
||||||
|
{
|
||||||
|
return *_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr T* operator->() const noexcept
|
||||||
|
{
|
||||||
|
return _ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr operator T*() noexcept
|
||||||
|
{
|
||||||
|
return _ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr operator T* const() const noexcept
|
||||||
|
{
|
||||||
|
return _ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
T* _ptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct ref_vec {
|
||||||
|
using value_type = T;
|
||||||
|
|
||||||
|
std::vector<value_type*> _order;
|
||||||
|
|
||||||
|
using iterator = double_iterator<typename decltype(_order)::iterator>;
|
||||||
|
using const_iterator = double_iterator<typename decltype(_order)::const_iterator>;
|
||||||
|
|
||||||
|
using reverse_iterator = double_iterator<typename decltype(_order)::reverse_iterator>;
|
||||||
|
using const_reverse_iterator =
|
||||||
|
double_iterator<typename decltype(_order)::const_reverse_iterator>;
|
||||||
|
|
||||||
|
ref_vec() = default;
|
||||||
|
|
||||||
|
ref_vec(std::initializer_list<value_type*> lst) : _order {lst} { };
|
||||||
|
|
||||||
|
template<typename InputIter, typename = std::enable_if_t<std::is_same_v<decltype(*std::declval<InputIter>()), value_type&>>>
|
||||||
|
ref_vec(InputIter iter1, InputIter iter2) {
|
||||||
|
_order.reserve(std::distance(iter1, iter2));
|
||||||
|
std::transform(iter1, iter2, std::back_inserter(_order), [] (auto& v) {return &v; });
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Range, typename = std::enable_if_t<std::is_same_v<decltype(*std::declval<Range>().begin()), value_type&>>>
|
||||||
|
ref_vec(Range&& rng) : ref_vec (std::begin(rng), std::end(rng)) { }
|
||||||
|
|
||||||
|
value_type& push_back(value_type& v)
|
||||||
|
{
|
||||||
|
_order.push_back(&v);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
value_type& push_back(non_null_ptr<value_type> ptr)
|
||||||
|
{
|
||||||
|
_order.push_back(ptr);
|
||||||
|
return *ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
value_type& emplace_back(value_type& v)
|
||||||
|
{
|
||||||
|
return push_back(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<value_type> erase(const value_type& v)
|
||||||
|
{
|
||||||
|
auto iter =
|
||||||
|
std::find_if(_order.begin(), _order.end(), [&v](auto&& ptr) { return ptr == &v; });
|
||||||
|
if (iter != _order.end()) {
|
||||||
|
auto uptr = std::move(*iter);
|
||||||
|
_order.erase(iter);
|
||||||
|
return uptr;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator rotate_to_back(const value_type& v)
|
||||||
|
{
|
||||||
|
auto iter =
|
||||||
|
std::find_if(_order.begin(), _order.end(), [&v](auto&& ptr) { return ptr == &v; });
|
||||||
|
return rotate_to_back(iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator rotate_to_back(iterator iter)
|
||||||
|
{
|
||||||
|
if (iter != _order.end()) {
|
||||||
|
{
|
||||||
|
return std::rotate(iter.data(), iter.data() + 1, _order.end());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator rotate_to_front(const value_type& v)
|
||||||
|
{
|
||||||
|
auto iter =
|
||||||
|
std::find_if(_order.begin(), _order.end(), [&v](auto&& ptr) { return ptr == &v; });
|
||||||
|
return rotate_to_front(iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator rotate_to_front(iterator iter)
|
||||||
|
{
|
||||||
|
if (iter != _order.end()) {
|
||||||
|
{
|
||||||
|
return std::rotate(_order.begin(), iter.data(), iter.data() + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t size() const noexcept
|
||||||
|
{
|
||||||
|
return _order.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() const noexcept
|
||||||
|
{
|
||||||
|
return _order.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t capacity() const noexcept
|
||||||
|
{
|
||||||
|
return _order.capacity();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t max_size() const noexcept
|
||||||
|
{
|
||||||
|
return _order.max_size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void reserve(std::size_t new_cap)
|
||||||
|
{
|
||||||
|
_order.reserve(new_cap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void shrink_to_fit()
|
||||||
|
{
|
||||||
|
_order.shrink_to_fit();
|
||||||
|
}
|
||||||
|
|
||||||
|
value_type& operator[](std::size_t n)
|
||||||
|
{
|
||||||
|
return *_order[n];
|
||||||
|
}
|
||||||
|
|
||||||
|
const value_type& operator[](std::size_t n) const
|
||||||
|
{
|
||||||
|
return *_order[n];
|
||||||
|
}
|
||||||
|
|
||||||
|
value_type& at(std::size_t n)
|
||||||
|
{
|
||||||
|
return *_order.at(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
const value_type& at(std::size_t n) const
|
||||||
|
{
|
||||||
|
return *_order.at(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator begin()
|
||||||
|
{
|
||||||
|
return _order.begin();
|
||||||
|
}
|
||||||
|
iterator end()
|
||||||
|
{
|
||||||
|
return _order.end();
|
||||||
|
}
|
||||||
|
const_iterator begin() const
|
||||||
|
{
|
||||||
|
return _order.begin();
|
||||||
|
}
|
||||||
|
const_iterator end() const
|
||||||
|
{
|
||||||
|
return _order.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
reverse_iterator rbegin()
|
||||||
|
{
|
||||||
|
return _order.rbegin();
|
||||||
|
}
|
||||||
|
reverse_iterator rend()
|
||||||
|
{
|
||||||
|
return _order.rend();
|
||||||
|
}
|
||||||
|
const_reverse_iterator rbegin() const
|
||||||
|
{
|
||||||
|
return _order.rbegin();
|
||||||
|
}
|
||||||
|
const_reverse_iterator rend() const
|
||||||
|
{
|
||||||
|
return _order.rend();
|
||||||
|
}
|
||||||
|
|
||||||
|
value_type& front()
|
||||||
|
{
|
||||||
|
return *_order.front();
|
||||||
|
}
|
||||||
|
|
||||||
|
value_type& back()
|
||||||
|
{
|
||||||
|
return *_order.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
const value_type& front() const
|
||||||
|
{
|
||||||
|
return *_order.front();
|
||||||
|
}
|
||||||
|
|
||||||
|
const value_type& back() const
|
||||||
|
{
|
||||||
|
return *_order.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<value_type*>& underlying() {
|
||||||
|
return _order;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace waybar::util
|
|
@ -0,0 +1,49 @@
|
||||||
|
project('waybar', 'cpp', 'c', default_options : ['cpp_std=c++17'])
|
||||||
|
|
||||||
|
cpp_args = []
|
||||||
|
cpp_link_args = []
|
||||||
|
|
||||||
|
if false # libc++
|
||||||
|
cpp_args += ['-stdlib=libc++']
|
||||||
|
cpp_link_args += ['-stdlib=libc++', '-lc++abi']
|
||||||
|
|
||||||
|
cpp_link_args += ['-lc++fs']
|
||||||
|
else
|
||||||
|
# TODO: For std::filesystem in libstdc++. Still unstable? Or why is it not in libstdc++ proper yet?
|
||||||
|
cpp_link_args += ['-lstdc++fs']
|
||||||
|
endif
|
||||||
|
|
||||||
|
add_global_arguments(cpp_args, language : 'cpp')
|
||||||
|
add_global_link_arguments(cpp_link_args, language : 'cpp')
|
||||||
|
|
||||||
|
thread_dep = dependency('threads')
|
||||||
|
libinput = dependency('libinput')
|
||||||
|
fmt = dependency('fmt', fallback: ['fmtlib', 'fmt_dep'])
|
||||||
|
wayland_client = dependency('wayland-client')
|
||||||
|
wayland_cursor = dependency('wayland-cursor')
|
||||||
|
wayland_protos = dependency('wayland-protocols')
|
||||||
|
wlroots = dependency('wlroots', fallback: ['wlroots', 'wlroots'])
|
||||||
|
gtkmm = dependency('gtkmm-3.0')
|
||||||
|
jsoncpp = dependency('jsoncpp')
|
||||||
|
sigcpp = dependency('sigc++-2.0')
|
||||||
|
|
||||||
|
subdir('protocol')
|
||||||
|
|
||||||
|
executable(
|
||||||
|
'waybar',
|
||||||
|
run_command('find', './src', '-name', '*.cpp').stdout().strip().split('\n'),
|
||||||
|
dependencies: [
|
||||||
|
thread_dep,
|
||||||
|
wlroots,
|
||||||
|
client_protos,
|
||||||
|
wayland_client,
|
||||||
|
fmt,
|
||||||
|
sigcpp,
|
||||||
|
jsoncpp,
|
||||||
|
libinput,
|
||||||
|
wayland_cursor,
|
||||||
|
gtkmm,
|
||||||
|
],
|
||||||
|
include_directories: [include_directories('include')],
|
||||||
|
install: true,
|
||||||
|
)
|
|
@ -0,0 +1,47 @@
|
||||||
|
wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir')
|
||||||
|
|
||||||
|
wayland_scanner = find_program('wayland-scanner')
|
||||||
|
|
||||||
|
# should check wayland_scanner's version, but it is hard to get
|
||||||
|
if wayland_client.version().version_compare('>=1.14.91')
|
||||||
|
code_type = 'private-code'
|
||||||
|
else
|
||||||
|
code_type = 'code'
|
||||||
|
endif
|
||||||
|
|
||||||
|
wayland_scanner_code = generator(
|
||||||
|
wayland_scanner,
|
||||||
|
output: '@BASENAME@-protocol.c',
|
||||||
|
arguments: [code_type, '@INPUT@', '@OUTPUT@'],
|
||||||
|
)
|
||||||
|
|
||||||
|
wayland_scanner_client = generator(
|
||||||
|
wayland_scanner,
|
||||||
|
output: '@BASENAME@-client-protocol.h',
|
||||||
|
arguments: ['client-header', '@INPUT@', '@OUTPUT@'],
|
||||||
|
)
|
||||||
|
|
||||||
|
client_protocols = [
|
||||||
|
[wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
|
||||||
|
['wlr-layer-shell-unstable-v1.xml'],
|
||||||
|
]
|
||||||
|
|
||||||
|
client_protos_src = []
|
||||||
|
client_protos_headers = []
|
||||||
|
|
||||||
|
foreach p : client_protocols
|
||||||
|
xml = join_paths(p)
|
||||||
|
client_protos_src += wayland_scanner_code.process(xml)
|
||||||
|
client_protos_headers += wayland_scanner_client.process(xml)
|
||||||
|
endforeach
|
||||||
|
|
||||||
|
lib_client_protos = static_library(
|
||||||
|
'client_protos',
|
||||||
|
client_protos_src + client_protos_headers,
|
||||||
|
dependencies: [wayland_client]
|
||||||
|
) # for the include directory
|
||||||
|
|
||||||
|
client_protos = declare_dependency(
|
||||||
|
link_with: lib_client_protos,
|
||||||
|
sources: client_protos_headers,
|
||||||
|
)
|
|
@ -0,0 +1,285 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<protocol name="wlr_layer_shell_unstable_v1">
|
||||||
|
<copyright>
|
||||||
|
Copyright © 2017 Drew DeVault
|
||||||
|
|
||||||
|
Permission to use, copy, modify, distribute, and sell this
|
||||||
|
software and its documentation for any purpose is hereby granted
|
||||||
|
without fee, provided that the above copyright notice appear in
|
||||||
|
all copies and that both that copyright notice and this permission
|
||||||
|
notice appear in supporting documentation, and that the name of
|
||||||
|
the copyright holders not be used in advertising or publicity
|
||||||
|
pertaining to distribution of the software without specific,
|
||||||
|
written prior permission. The copyright holders make no
|
||||||
|
representations about the suitability of this software for any
|
||||||
|
purpose. It is provided "as is" without express or implied
|
||||||
|
warranty.
|
||||||
|
|
||||||
|
THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
|
||||||
|
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
|
||||||
|
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
||||||
|
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||||
|
THIS SOFTWARE.
|
||||||
|
</copyright>
|
||||||
|
|
||||||
|
<interface name="zwlr_layer_shell_v1" version="1">
|
||||||
|
<description summary="create surfaces that are layers of the desktop">
|
||||||
|
Clients can use this interface to assign the surface_layer role to
|
||||||
|
wl_surfaces. Such surfaces are assigned to a "layer" of the output and
|
||||||
|
rendered with a defined z-depth respective to each other. They may also be
|
||||||
|
anchored to the edges and corners of a screen and specify input handling
|
||||||
|
semantics. This interface should be suitable for the implementation of
|
||||||
|
many desktop shell components, and a broad number of other applications
|
||||||
|
that interact with the desktop.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<request name="get_layer_surface">
|
||||||
|
<description summary="create a layer_surface from a surface">
|
||||||
|
Create a layer surface for an existing surface. This assigns the role of
|
||||||
|
layer_surface, or raises a protocol error if another role is already
|
||||||
|
assigned.
|
||||||
|
|
||||||
|
Creating a layer surface from a wl_surface which has a buffer attached
|
||||||
|
or committed is a client error, and any attempts by a client to attach
|
||||||
|
or manipulate a buffer prior to the first layer_surface.configure call
|
||||||
|
must also be treated as errors.
|
||||||
|
|
||||||
|
You may pass NULL for output to allow the compositor to decide which
|
||||||
|
output to use. Generally this will be the one that the user most
|
||||||
|
recently interacted with.
|
||||||
|
|
||||||
|
Clients can specify a namespace that defines the purpose of the layer
|
||||||
|
surface.
|
||||||
|
</description>
|
||||||
|
<arg name="id" type="new_id" interface="zwlr_layer_surface_v1"/>
|
||||||
|
<arg name="surface" type="object" interface="wl_surface"/>
|
||||||
|
<arg name="output" type="object" interface="wl_output" allow-null="true"/>
|
||||||
|
<arg name="layer" type="uint" enum="layer" summary="layer to add this surface to"/>
|
||||||
|
<arg name="np" type="string" summary="namespace for the layer surface"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<enum name="error">
|
||||||
|
<entry name="role" value="0" summary="wl_surface has another role"/>
|
||||||
|
<entry name="invalid_layer" value="1" summary="layer value is invalid"/>
|
||||||
|
<entry name="already_constructed" value="2" summary="wl_surface has a buffer attached or committed"/>
|
||||||
|
</enum>
|
||||||
|
|
||||||
|
<enum name="layer">
|
||||||
|
<description summary="available layers for surfaces">
|
||||||
|
These values indicate which layers a surface can be rendered in. They
|
||||||
|
are ordered by z depth, bottom-most first. Traditional shell surfaces
|
||||||
|
will typically be rendered between the bottom and top layers.
|
||||||
|
Fullscreen shell surfaces are typically rendered at the top layer.
|
||||||
|
Multiple surfaces can share a single layer, and ordering within a
|
||||||
|
single layer is undefined.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<entry name="background" value="0"/>
|
||||||
|
<entry name="bottom" value="1"/>
|
||||||
|
<entry name="top" value="2"/>
|
||||||
|
<entry name="overlay" value="3"/>
|
||||||
|
</enum>
|
||||||
|
</interface>
|
||||||
|
|
||||||
|
<interface name="zwlr_layer_surface_v1" version="1">
|
||||||
|
<description summary="layer metadata interface">
|
||||||
|
An interface that may be implemented by a wl_surface, for surfaces that
|
||||||
|
are designed to be rendered as a layer of a stacked desktop-like
|
||||||
|
environment.
|
||||||
|
|
||||||
|
Layer surface state (size, anchor, exclusive zone, margin, interactivity)
|
||||||
|
is double-buffered, and will be applied at the time wl_surface.commit of
|
||||||
|
the corresponding wl_surface is called.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<request name="set_size">
|
||||||
|
<description summary="sets the size of the surface">
|
||||||
|
Sets the size of the surface in surface-local coordinates. The
|
||||||
|
compositor will display the surface centered with respect to its
|
||||||
|
anchors.
|
||||||
|
|
||||||
|
If you pass 0 for either value, the compositor will assign it and
|
||||||
|
inform you of the assignment in the configure event. You must set your
|
||||||
|
anchor to opposite edges in the dimensions you omit; not doing so is a
|
||||||
|
protocol error. Both values are 0 by default.
|
||||||
|
|
||||||
|
Size is double-buffered, see wl_surface.commit.
|
||||||
|
</description>
|
||||||
|
<arg name="width" type="uint"/>
|
||||||
|
<arg name="height" type="uint"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="set_anchor">
|
||||||
|
<description summary="configures the anchor point of the surface">
|
||||||
|
Requests that the compositor anchor the surface to the specified edges
|
||||||
|
and corners. If two orthoginal edges are specified (e.g. 'top' and
|
||||||
|
'left'), then the anchor point will be the intersection of the edges
|
||||||
|
(e.g. the top left corner of the output); otherwise the anchor point
|
||||||
|
will be centered on that edge, or in the center if none is specified.
|
||||||
|
|
||||||
|
Anchor is double-buffered, see wl_surface.commit.
|
||||||
|
</description>
|
||||||
|
<arg name="anchor" type="uint" enum="anchor"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="set_exclusive_zone">
|
||||||
|
<description summary="configures the exclusive geometry of this surface">
|
||||||
|
Requests that the compositor avoids occluding an area of the surface
|
||||||
|
with other surfaces. The compositor's use of this information is
|
||||||
|
implementation-dependent - do not assume that this region will not
|
||||||
|
actually be occluded.
|
||||||
|
|
||||||
|
A positive value is only meaningful if the surface is anchored to an
|
||||||
|
edge, rather than a corner. The zone is the number of surface-local
|
||||||
|
coordinates from the edge that are considered exclusive.
|
||||||
|
|
||||||
|
Surfaces that do not wish to have an exclusive zone may instead specify
|
||||||
|
how they should interact with surfaces that do. If set to zero, the
|
||||||
|
surface indicates that it would like to be moved to avoid occluding
|
||||||
|
surfaces with a positive excluzive zone. If set to -1, the surface
|
||||||
|
indicates that it would not like to be moved to accomodate for other
|
||||||
|
surfaces, and the compositor should extend it all the way to the edges
|
||||||
|
it is anchored to.
|
||||||
|
|
||||||
|
For example, a panel might set its exclusive zone to 10, so that
|
||||||
|
maximized shell surfaces are not shown on top of it. A notification
|
||||||
|
might set its exclusive zone to 0, so that it is moved to avoid
|
||||||
|
occluding the panel, but shell surfaces are shown underneath it. A
|
||||||
|
wallpaper or lock screen might set their exclusive zone to -1, so that
|
||||||
|
they stretch below or over the panel.
|
||||||
|
|
||||||
|
The default value is 0.
|
||||||
|
|
||||||
|
Exclusive zone is double-buffered, see wl_surface.commit.
|
||||||
|
</description>
|
||||||
|
<arg name="zone" type="int"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="set_margin">
|
||||||
|
<description summary="sets a margin from the anchor point">
|
||||||
|
Requests that the surface be placed some distance away from the anchor
|
||||||
|
point on the output, in surface-local coordinates. Setting this value
|
||||||
|
for edges you are not anchored to has no effect.
|
||||||
|
|
||||||
|
The exclusive zone includes the margin.
|
||||||
|
|
||||||
|
Margin is double-buffered, see wl_surface.commit.
|
||||||
|
</description>
|
||||||
|
<arg name="top" type="int"/>
|
||||||
|
<arg name="right" type="int"/>
|
||||||
|
<arg name="bottom" type="int"/>
|
||||||
|
<arg name="left" type="int"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="set_keyboard_interactivity">
|
||||||
|
<description summary="requests keyboard events">
|
||||||
|
Set to 1 to request that the seat send keyboard events to this layer
|
||||||
|
surface. For layers below the shell surface layer, the seat will use
|
||||||
|
normal focus semantics. For layers above the shell surface layers, the
|
||||||
|
seat will always give exclusive keyboard focus to the top-most layer
|
||||||
|
which has keyboard interactivity set to true.
|
||||||
|
|
||||||
|
Layer surfaces receive pointer, touch, and tablet events normally. If
|
||||||
|
you do not want to receive them, set the input region on your surface
|
||||||
|
to an empty region.
|
||||||
|
|
||||||
|
Events is double-buffered, see wl_surface.commit.
|
||||||
|
</description>
|
||||||
|
<arg name="keyboard_interactivity" type="uint"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="get_popup">
|
||||||
|
<description summary="assign this layer_surface as an xdg_popup parent">
|
||||||
|
This assigns an xdg_popup's parent to this layer_surface. This popup
|
||||||
|
should have been created via xdg_surface::get_popup with the parent set
|
||||||
|
to NULL, and this request must be invoked before committing the popup's
|
||||||
|
initial state.
|
||||||
|
|
||||||
|
See the documentation of xdg_popup for more details about what an
|
||||||
|
xdg_popup is and how it is used.
|
||||||
|
</description>
|
||||||
|
<arg name="popup" type="object" interface="xdg_popup"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="ack_configure">
|
||||||
|
<description summary="ack a configure event">
|
||||||
|
When a configure event is received, if a client commits the
|
||||||
|
surface in response to the configure event, then the client
|
||||||
|
must make an ack_configure request sometime before the commit
|
||||||
|
request, passing along the serial of the configure event.
|
||||||
|
|
||||||
|
If the client receives multiple configure events before it
|
||||||
|
can respond to one, it only has to ack the last configure event.
|
||||||
|
|
||||||
|
A client is not required to commit immediately after sending
|
||||||
|
an ack_configure request - it may even ack_configure several times
|
||||||
|
before its next surface commit.
|
||||||
|
|
||||||
|
A client may send multiple ack_configure requests before committing, but
|
||||||
|
only the last request sent before a commit indicates which configure
|
||||||
|
event the client really is responding to.
|
||||||
|
</description>
|
||||||
|
<arg name="serial" type="uint" summary="the serial from the configure event"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="destroy" type="destructor">
|
||||||
|
<description summary="destroy the layer_surface">
|
||||||
|
This request destroys the layer surface.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<event name="configure">
|
||||||
|
<description summary="suggest a surface change">
|
||||||
|
The configure event asks the client to resize its surface.
|
||||||
|
|
||||||
|
Clients should arrange their surface for the new states, and then send
|
||||||
|
an ack_configure request with the serial sent in this configure event at
|
||||||
|
some point before committing the new surface.
|
||||||
|
|
||||||
|
The client is free to dismiss all but the last configure event it
|
||||||
|
received.
|
||||||
|
|
||||||
|
The width and height arguments specify the size of the window in
|
||||||
|
surface-local coordinates.
|
||||||
|
|
||||||
|
The size is a hint, in the sense that the client is free to ignore it if
|
||||||
|
it doesn't resize, pick a smaller size (to satisfy aspect ratio or
|
||||||
|
resize in steps of NxM pixels). If the client picks a smaller size and
|
||||||
|
is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the
|
||||||
|
surface will be centered on this axis.
|
||||||
|
|
||||||
|
If the width or height arguments are zero, it means the client should
|
||||||
|
decide its own window dimension.
|
||||||
|
</description>
|
||||||
|
<arg name="serial" type="uint"/>
|
||||||
|
<arg name="width" type="uint"/>
|
||||||
|
<arg name="height" type="uint"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="closed">
|
||||||
|
<description summary="surface should be closed">
|
||||||
|
The closed event is sent by the compositor when the surface will no
|
||||||
|
longer be shown. The output may have been destroyed or the user may
|
||||||
|
have asked for it to be removed. Further changes to the surface will be
|
||||||
|
ignored. The client should destroy the resource after receiving this
|
||||||
|
event, and create a new surface if they so choose.
|
||||||
|
</description>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<enum name="error">
|
||||||
|
<entry name="invalid_surface_state" value="0" summary="provided surface state is invalid"/>
|
||||||
|
<entry name="invalid_size" value="1" summary="size is invalid"/>
|
||||||
|
<entry name="invalid_anchor" value="2" summary="anchor bitfield is invalid"/>
|
||||||
|
</enum>
|
||||||
|
|
||||||
|
<enum name="anchor" bitfield="true">
|
||||||
|
<entry name="top" value="1" summary="the top edge of the anchor rectangle"/>
|
||||||
|
<entry name="bottom" value="2" summary="the bottom edge of the anchor rectangle"/>
|
||||||
|
<entry name="left" value="4" summary="the left edge of the anchor rectangle"/>
|
||||||
|
<entry name="right" value="8" summary="the right edge of the anchor rectangle"/>
|
||||||
|
</enum>
|
||||||
|
</interface>
|
||||||
|
</protocol>
|
|
@ -0,0 +1,45 @@
|
||||||
|
* {
|
||||||
|
border: none;
|
||||||
|
border-radius: 0;
|
||||||
|
font-family: Roboto, Helvetica, Arial, sans-serif;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
window {
|
||||||
|
background: rgba(43, 48, 59, 0.5);
|
||||||
|
border-bottom: 3px solid rgba(100, 114, 125, 0.5);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.focused-window-title {
|
||||||
|
padding: 0px 10px;
|
||||||
|
min-height: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-selector button {
|
||||||
|
padding: 0 5px;
|
||||||
|
background: transparent;
|
||||||
|
color: white;
|
||||||
|
border-bottom: 3px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-selector button.current {
|
||||||
|
background: #64727D;
|
||||||
|
border-bottom: 3px solid white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clock-widget {
|
||||||
|
background-color: #64727D;
|
||||||
|
padding: 0 10px;
|
||||||
|
margin: 0 5px;
|
||||||
|
border-bottom: 2px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.battery-status {
|
||||||
|
padding: 0 10px;
|
||||||
|
border-bottom: 2px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.battery-status.battery-charging {
|
||||||
|
background-color: #26A65B;
|
||||||
|
}
|
|
@ -0,0 +1,169 @@
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <gdk/gdkwayland.h>
|
||||||
|
#include <thread>
|
||||||
|
#include "bar.hpp"
|
||||||
|
#include "client.hpp"
|
||||||
|
#include "util/chrono.hpp"
|
||||||
|
#include "modules/clock.hpp"
|
||||||
|
#include "modules/workspaces.hpp"
|
||||||
|
#include "modules/battery.hpp"
|
||||||
|
|
||||||
|
static void handle_geometry(void *data, struct wl_output *wl_output, int32_t x,
|
||||||
|
int32_t y, int32_t physical_width, int32_t physical_height, int32_t subpixel,
|
||||||
|
const char *make, const char *model, int32_t transform)
|
||||||
|
{
|
||||||
|
// Nothing here
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_mode(void *data, struct wl_output *wl_output, uint32_t f,
|
||||||
|
int32_t w, int32_t h, int32_t refresh)
|
||||||
|
{
|
||||||
|
auto o = reinterpret_cast<waybar::Bar *>(data);
|
||||||
|
std::cout << fmt::format("Bar width configured: {}", w) << std::endl;
|
||||||
|
o->set_width(w);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_done(void *data, struct wl_output *)
|
||||||
|
{
|
||||||
|
// Nothing here
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_scale(void *data, struct wl_output *wl_output,
|
||||||
|
int32_t factor)
|
||||||
|
{
|
||||||
|
// Nothing here
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wl_output_listener outputListener = {
|
||||||
|
.geometry = handle_geometry,
|
||||||
|
.mode = handle_mode,
|
||||||
|
.done = handle_done,
|
||||||
|
.scale = handle_scale,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void layer_surface_handle_configure(
|
||||||
|
void *data, struct zwlr_layer_surface_v1 *surface, uint32_t serial,
|
||||||
|
uint32_t width, uint32_t height)
|
||||||
|
{
|
||||||
|
auto o = reinterpret_cast<waybar::Bar *>(data);
|
||||||
|
o->window.show_all();
|
||||||
|
zwlr_layer_surface_v1_ack_configure(surface, serial);
|
||||||
|
if (o->client.height != height)
|
||||||
|
{
|
||||||
|
height = o->client.height;
|
||||||
|
std::cout << fmt::format("New Height: {}", height) << std::endl;
|
||||||
|
zwlr_layer_surface_v1_set_size(surface, width, height);
|
||||||
|
zwlr_layer_surface_v1_set_exclusive_zone(surface, o->visible ? height : 0);
|
||||||
|
wl_surface_commit(o->surface);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void layer_surface_handle_closed(void *data,
|
||||||
|
struct zwlr_layer_surface_v1 *surface)
|
||||||
|
{
|
||||||
|
auto o = reinterpret_cast<waybar::Bar *>(data);
|
||||||
|
zwlr_layer_surface_v1_destroy(o->layer_surface);
|
||||||
|
o->layer_surface = NULL;
|
||||||
|
wl_surface_destroy(o->surface);
|
||||||
|
o->surface = NULL;
|
||||||
|
o->window.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct zwlr_layer_surface_v1_listener layerSurfaceListener = {
|
||||||
|
.configure = layer_surface_handle_configure,
|
||||||
|
.closed = layer_surface_handle_closed,
|
||||||
|
};
|
||||||
|
|
||||||
|
waybar::Bar::Bar(Client &client, std::unique_ptr<struct wl_output *> &&p_output)
|
||||||
|
: client(client), window{Gtk::WindowType::WINDOW_TOPLEVEL},
|
||||||
|
output(std::move(p_output))
|
||||||
|
{
|
||||||
|
wl_output_add_listener(*output, &outputListener, this);
|
||||||
|
window.set_title("waybar");
|
||||||
|
window.set_decorated(false);
|
||||||
|
// window.set_resizable(false);
|
||||||
|
setup_css();
|
||||||
|
setup_widgets();
|
||||||
|
gtk_widget_realize(GTK_WIDGET(window.gobj()));
|
||||||
|
GdkWindow *gdkWindow = gtk_widget_get_window(GTK_WIDGET(window.gobj()));
|
||||||
|
gdk_wayland_window_set_use_custom_surface(gdkWindow);
|
||||||
|
surface = gdk_wayland_window_get_wl_surface(gdkWindow);
|
||||||
|
layer_surface = zwlr_layer_shell_v1_get_layer_surface(
|
||||||
|
client.layer_shell, surface, *output, ZWLR_LAYER_SHELL_V1_LAYER_TOP,
|
||||||
|
"waybar");
|
||||||
|
zwlr_layer_surface_v1_set_anchor(layer_surface,
|
||||||
|
ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
|
||||||
|
ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT);
|
||||||
|
zwlr_layer_surface_v1_set_size(layer_surface, width, client.height);
|
||||||
|
zwlr_layer_surface_v1_add_listener(layer_surface, &layerSurfaceListener,
|
||||||
|
this);
|
||||||
|
wl_surface_commit(surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto waybar::Bar::setup_css() -> void
|
||||||
|
{
|
||||||
|
css_provider = Gtk::CssProvider::create();
|
||||||
|
style_context = Gtk::StyleContext::create();
|
||||||
|
|
||||||
|
// load our css file, wherever that may be hiding
|
||||||
|
if (css_provider->load_from_path(client.css_file))
|
||||||
|
{
|
||||||
|
Glib::RefPtr<Gdk::Screen> screen = window.get_screen();
|
||||||
|
style_context->add_provider_for_screen(screen, css_provider,
|
||||||
|
GTK_STYLE_PROVIDER_PRIORITY_USER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto waybar::Bar::set_width(int width) -> void
|
||||||
|
{
|
||||||
|
this->width = width;
|
||||||
|
window.set_size_request(width);
|
||||||
|
window.resize(width, client.height);
|
||||||
|
zwlr_layer_surface_v1_set_size(layer_surface, width, 40);
|
||||||
|
wl_surface_commit(surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto waybar::Bar::toggle() -> void
|
||||||
|
{
|
||||||
|
visible = !visible;
|
||||||
|
auto zone = visible ? client.height : 0;
|
||||||
|
zwlr_layer_surface_v1_set_exclusive_zone(layer_surface, zone);
|
||||||
|
wl_surface_commit(surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto waybar::Bar::setup_widgets() -> void
|
||||||
|
{
|
||||||
|
auto &left = *Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 0));
|
||||||
|
auto ¢er = *Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 0));
|
||||||
|
auto &right = *Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 0));
|
||||||
|
|
||||||
|
auto &box1 = *Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 0));
|
||||||
|
window.add(box1);
|
||||||
|
box1.set_homogeneous(true);
|
||||||
|
box1.pack_start(left, true, true);
|
||||||
|
box1.pack_start(center, false, false);
|
||||||
|
box1.pack_end(right, true, true);
|
||||||
|
|
||||||
|
auto &focused_window = *Gtk::manage(new Gtk::Label());
|
||||||
|
focused_window.get_style_context()->add_class("focused-window-title");
|
||||||
|
client.signals.focused_window_name.connect(
|
||||||
|
[&focused_window](std::string focused_window_name) {
|
||||||
|
if (focused_window_name.size() > 70)
|
||||||
|
{
|
||||||
|
focused_window_name.erase(67);
|
||||||
|
focused_window_name += "...";
|
||||||
|
}
|
||||||
|
focused_window.set_text(focused_window_name);
|
||||||
|
});
|
||||||
|
|
||||||
|
focused_window.set_hexpand(false);
|
||||||
|
|
||||||
|
auto &clock = *new waybar::modules::Clock();
|
||||||
|
auto &workspace_selector = *new waybar::modules::WorkspaceSelector(*this);
|
||||||
|
auto &battery = *new waybar::modules::Battery();
|
||||||
|
|
||||||
|
left.pack_start(workspace_selector, false, true, 0);
|
||||||
|
// center.pack_start(workspace_selector, true, false, 10);
|
||||||
|
right.pack_end(clock, false, false, 0);
|
||||||
|
right.pack_end(battery, false, false, 0);
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
#include "client.hpp"
|
||||||
|
|
||||||
|
static void handle_global(void *data, struct wl_registry *registry,
|
||||||
|
uint32_t name, const char *interface, uint32_t version)
|
||||||
|
{
|
||||||
|
auto o = reinterpret_cast<waybar::Client *>(data);
|
||||||
|
if (!strcmp(interface, zwlr_layer_shell_v1_interface.name)) {
|
||||||
|
o->layer_shell = (zwlr_layer_shell_v1 *)wl_registry_bind(registry, name,
|
||||||
|
&zwlr_layer_shell_v1_interface, version);
|
||||||
|
} else if (!strcmp(interface, wl_output_interface.name)) {
|
||||||
|
auto output = std::make_unique<struct wl_output *>();
|
||||||
|
*output = (struct wl_output *)wl_registry_bind(registry, name,
|
||||||
|
&wl_output_interface, version);
|
||||||
|
o->bars.emplace_back(*o, std::move(output));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_global_remove(void *data,
|
||||||
|
struct wl_registry *registry, uint32_t name)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wl_registry_listener registry_listener = {
|
||||||
|
.global = handle_global,
|
||||||
|
.global_remove = handle_global_remove,
|
||||||
|
};
|
||||||
|
|
||||||
|
waybar::Client::Client(int argc, char* argv[])
|
||||||
|
: gtk_main(argc, argv),
|
||||||
|
gdk_display(Gdk::Display::get_default()),
|
||||||
|
wlDisplay(gdk_wayland_display_get_wl_display(gdk_display->gobj()))
|
||||||
|
{}
|
||||||
|
|
||||||
|
void waybar::Client::bind_interfaces()
|
||||||
|
{
|
||||||
|
registry = wl_display_get_registry(wlDisplay);
|
||||||
|
wl_registry_add_listener(registry, ®istry_listener, this);
|
||||||
|
wl_display_roundtrip(wlDisplay);
|
||||||
|
}
|
||||||
|
|
||||||
|
int waybar::Client::main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
bind_interfaces();
|
||||||
|
gtk_main.run();
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
#include <string>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
#include "ipc/client.hpp"
|
||||||
|
|
||||||
|
static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'};
|
||||||
|
static const size_t ipc_header_size = sizeof(ipc_magic)+8;
|
||||||
|
|
||||||
|
std::string get_socketpath(void) {
|
||||||
|
const char *env = getenv("SWAYSOCK");
|
||||||
|
if (env) return std::string(env);
|
||||||
|
std::string str;
|
||||||
|
{
|
||||||
|
std::string str_buf;
|
||||||
|
FILE* in;
|
||||||
|
char buf[512] = { 0 };
|
||||||
|
if (!(in = popen("sway --get-socketpath 2>/dev/null", "r"))) {
|
||||||
|
throw std::runtime_error("Failed to get socket path");
|
||||||
|
}
|
||||||
|
while (fgets(buf, sizeof(buf), in) != nullptr) {
|
||||||
|
str_buf.append(buf, sizeof(buf));
|
||||||
|
}
|
||||||
|
pclose(in);
|
||||||
|
str = str_buf;
|
||||||
|
}
|
||||||
|
if (str.back() == '\n') {
|
||||||
|
str.pop_back();
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ipc_open_socket(std::string socket_path) {
|
||||||
|
struct sockaddr_un addr;
|
||||||
|
int socketfd;
|
||||||
|
if ((socketfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
|
||||||
|
throw std::runtime_error("Unable to open Unix socket");
|
||||||
|
}
|
||||||
|
addr.sun_family = AF_UNIX;
|
||||||
|
strncpy(addr.sun_path, socket_path.c_str(), sizeof(addr.sun_path) - 1);
|
||||||
|
addr.sun_path[sizeof(addr.sun_path) - 1] = 0;
|
||||||
|
int l = sizeof(struct sockaddr_un);
|
||||||
|
if (connect(socketfd, (struct sockaddr *)&addr, l) == -1) {
|
||||||
|
throw std::runtime_error("Unable to connect to " + socket_path);
|
||||||
|
}
|
||||||
|
return socketfd;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ipc_response ipc_recv_response(int socketfd) {
|
||||||
|
char data[ipc_header_size];
|
||||||
|
uint32_t *data32 = (uint32_t *)(data + sizeof(ipc_magic));
|
||||||
|
|
||||||
|
size_t total = 0;
|
||||||
|
while (total < ipc_header_size) {
|
||||||
|
ssize_t received = recv(socketfd, data + total, ipc_header_size - total, 0);
|
||||||
|
if (received <= 0) {
|
||||||
|
throw std::runtime_error("Unable to receive IPC response");
|
||||||
|
}
|
||||||
|
total += received;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ipc_response response;
|
||||||
|
|
||||||
|
total = 0;
|
||||||
|
response.size = data32[0];
|
||||||
|
response.type = data32[1];
|
||||||
|
char payload[response.size + 1];
|
||||||
|
|
||||||
|
while (total < response.size) {
|
||||||
|
ssize_t received = recv(socketfd, payload + total, response.size - total, 0);
|
||||||
|
if (received < 0) {
|
||||||
|
throw std::runtime_error("Unable to receive IPC response");
|
||||||
|
}
|
||||||
|
total += received;
|
||||||
|
}
|
||||||
|
payload[response.size] = '\0';
|
||||||
|
response.payload = std::string(payload);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ipc_single_command(int socketfd, uint32_t type, const char *payload, uint32_t *len) {
|
||||||
|
char data[ipc_header_size];
|
||||||
|
uint32_t *data32 = (uint32_t *)(data + sizeof(ipc_magic));
|
||||||
|
memcpy(data, ipc_magic, sizeof(ipc_magic));
|
||||||
|
data32[0] = *len;
|
||||||
|
data32[1] = type;
|
||||||
|
|
||||||
|
if (send(socketfd, data, ipc_header_size, 0) == -1) {
|
||||||
|
throw std::runtime_error("Unable to send IPC header");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (send(socketfd, payload, *len, 0) == -1) {
|
||||||
|
throw std::runtime_error("Unable to send IPC payload");
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ipc_response resp = ipc_recv_response(socketfd);
|
||||||
|
std::string response = resp.payload;
|
||||||
|
*len = resp.size;
|
||||||
|
return response;
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
#include <gtkmm.h>
|
||||||
|
#include <wayland-client.hpp>
|
||||||
|
#include <gdk/gdkwayland.h>
|
||||||
|
#include <csignal>
|
||||||
|
#include "client.hpp"
|
||||||
|
|
||||||
|
namespace waybar {
|
||||||
|
static Client* client;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
waybar::Client c(argc, argv);
|
||||||
|
waybar::client = &c;
|
||||||
|
std::signal(SIGUSR1, [] (int signal) {
|
||||||
|
for (auto& bar : waybar::client->bars) {
|
||||||
|
bar.toggle();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return c.main(argc, argv);
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
std::cerr << e.what() << std::endl;
|
||||||
|
return 1;
|
||||||
|
} catch (const Glib::Exception& e) {
|
||||||
|
std::cerr << e.what().c_str() << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
#include "modules/battery.hpp"
|
||||||
|
|
||||||
|
waybar::modules::Battery::Battery()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
for (auto &node : fs::directory_iterator(_data_dir)) {
|
||||||
|
if (fs::is_directory(node) && fs::exists(node / "charge_now") &&
|
||||||
|
fs::exists(node / "charge_full")) {
|
||||||
|
_batteries.push_back(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (fs::filesystem_error &e) {
|
||||||
|
std::cerr << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
_label.get_style_context()->add_class("battery-status");
|
||||||
|
|
||||||
|
_thread = [this] {
|
||||||
|
update();
|
||||||
|
_thread.sleep_for(chrono::minutes(1));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto waybar::modules::Battery::update() -> void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
for (auto &bat : _batteries) {
|
||||||
|
int full, now;
|
||||||
|
std::string status;
|
||||||
|
std::ifstream(bat / "charge_now") >> now;
|
||||||
|
std::ifstream(bat / "charge_full") >> full;
|
||||||
|
std::ifstream(bat / "status") >> status;
|
||||||
|
if (status == "Charging") {
|
||||||
|
_label.get_style_context()->add_class("battery-charging");
|
||||||
|
} else {
|
||||||
|
_label.get_style_context()->remove_class("battery-charging");
|
||||||
|
}
|
||||||
|
int pct = float(now) / float(full) * 100.f;
|
||||||
|
_label.set_text_with_mnemonic(fmt::format("{}% {}", pct, ""));
|
||||||
|
}
|
||||||
|
} catch (std::exception &e) {
|
||||||
|
std::cerr << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
waybar::modules::Battery::operator Gtk::Widget &()
|
||||||
|
{
|
||||||
|
return _label;
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
#include "modules/clock.hpp"
|
||||||
|
|
||||||
|
waybar::modules::Clock::Clock()
|
||||||
|
{
|
||||||
|
_label.get_style_context()->add_class("clock-widget");
|
||||||
|
_thread = [this] {
|
||||||
|
auto now = waybar::chrono::clock::now();
|
||||||
|
auto t = std::time(nullptr);
|
||||||
|
auto localtime = std::localtime(&t);
|
||||||
|
_label.set_text(
|
||||||
|
fmt::format("{:02}:{:02}", localtime->tm_hour, localtime->tm_min));
|
||||||
|
auto timeout =
|
||||||
|
std::chrono::floor<std::chrono::minutes>(now + std::chrono::minutes(1));
|
||||||
|
_thread.sleep_until(timeout);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
waybar::modules::Clock::operator Gtk::Widget &() {
|
||||||
|
return _label;
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
#include "modules/workspaces.hpp"
|
||||||
|
#include "ipc/client.hpp"
|
||||||
|
|
||||||
|
waybar::modules::WorkspaceSelector::WorkspaceSelector(Bar &bar)
|
||||||
|
: _bar(bar), _box(Gtk::manage(new Gtk::Box))
|
||||||
|
{
|
||||||
|
_box->get_style_context()->add_class("workspace-selector");
|
||||||
|
std::string socketPath = get_socketpath();
|
||||||
|
_ipcSocketfd = ipc_open_socket(socketPath);
|
||||||
|
_ipcEventSocketfd = ipc_open_socket(socketPath);
|
||||||
|
const char *subscribe = "[ \"workspace\", \"mode\" ]";
|
||||||
|
uint32_t len = strlen(subscribe);
|
||||||
|
ipc_single_command(_ipcEventSocketfd, IPC_SUBSCRIBE, subscribe, &len);
|
||||||
|
_thread = [this] {
|
||||||
|
update();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto waybar::modules::WorkspaceSelector::update() -> void
|
||||||
|
{
|
||||||
|
Json::Value workspaces = _getWorkspaces();
|
||||||
|
for (auto it = _buttons.begin(); it != _buttons.end(); ++it) {
|
||||||
|
auto ws = std::find_if(workspaces.begin(), workspaces.end(),
|
||||||
|
[it](auto node) -> bool { return node["num"].asInt() == it->first; });
|
||||||
|
if (ws == workspaces.end()) {
|
||||||
|
it->second.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto node : workspaces) {
|
||||||
|
auto it = _buttons.find(node["num"].asInt());
|
||||||
|
if (it == _buttons.end()) {
|
||||||
|
_addWorkspace(node);
|
||||||
|
} else {
|
||||||
|
auto styleContext = it->second.get_style_context();
|
||||||
|
bool isCurrent = node["focused"].asBool();
|
||||||
|
if (styleContext->has_class("current") && !isCurrent) {
|
||||||
|
styleContext->remove_class("current");
|
||||||
|
} else if (!styleContext->has_class("current") && isCurrent) {
|
||||||
|
styleContext->add_class("current");
|
||||||
|
}
|
||||||
|
it->second.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void waybar::modules::WorkspaceSelector::_addWorkspace(Json::Value node)
|
||||||
|
{
|
||||||
|
auto pair = _buttons.emplace(node["num"].asInt(), node["name"].asString());
|
||||||
|
auto &button = pair.first->second;
|
||||||
|
button.set_relief(Gtk::RELIEF_NONE);
|
||||||
|
button.signal_clicked().connect([this, pair] {
|
||||||
|
auto value = fmt::format("workspace \"{}\"", pair.first->first);
|
||||||
|
uint32_t size = value.size();
|
||||||
|
ipc_single_command(_ipcSocketfd, IPC_COMMAND, value.c_str(), &size);
|
||||||
|
});
|
||||||
|
_box->pack_start(button, false, false, 0);
|
||||||
|
if (node["focused"].asBool()) {
|
||||||
|
button.get_style_context()->add_class("current");
|
||||||
|
}
|
||||||
|
button.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value waybar::modules::WorkspaceSelector::_getWorkspaces()
|
||||||
|
{
|
||||||
|
uint32_t len = 0;
|
||||||
|
Json::Value root;
|
||||||
|
Json::CharReaderBuilder builder;
|
||||||
|
Json::CharReader* reader = builder.newCharReader();
|
||||||
|
std::string err;
|
||||||
|
std::string str = ipc_single_command(_ipcSocketfd, IPC_GET_WORKSPACES,
|
||||||
|
nullptr, &len);
|
||||||
|
bool res = reader->parse(str.c_str(), str.c_str() + str.size(), &root, &err);
|
||||||
|
delete reader;
|
||||||
|
if (!res) {
|
||||||
|
std::cerr << err << std::endl;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
waybar::modules::WorkspaceSelector::operator Gtk::Widget &() {
|
||||||
|
return *_box;
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
[wrap-file]
|
||||||
|
directory = fmt-4.1.0
|
||||||
|
|
||||||
|
source_url = https://github.com/fmtlib/fmt/archive/4.1.0.tar.gz
|
||||||
|
source_filename = fmt-4.1.0.tar.gz
|
||||||
|
source_hash = 46628a2f068d0e33c716be0ed9dcae4370242df135aed663a180b9fd8e36733d
|
||||||
|
|
||||||
|
patch_url = https://wrapdb.mesonbuild.com/v1/projects/fmt/4.1.0/1/get_zip
|
||||||
|
patch_filename = fmt-4.1.0-1-wrap.zip
|
||||||
|
patch_hash = 741931f01e558491724fc1c67bff996d1df79c0277626fc463de138052c9ecc0
|
Loading…
Reference in New Issue