feat: init repo

This commit is contained in:
Alexis
2018-08-08 23:54:58 +02:00
parent 53ff4e0d60
commit fcb6a9aa8b
22 changed files with 2634 additions and 0 deletions

169
src/bar.cpp Normal file
View File

@ -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 &center = *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);
}

47
src/client.cpp Normal file
View File

@ -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, &registry_listener, this);
wl_display_roundtrip(wlDisplay);
}
int waybar::Client::main(int argc, char* argv[])
{
bind_interfaces();
gtk_main.run();
return 0;
}

101
src/ipc/client.cpp Normal file
View File

@ -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;
}

30
src/main.cpp Normal file
View File

@ -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;
}
}

49
src/modules/battery.cpp Normal file
View File

@ -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;
}

20
src/modules/clock.cpp Normal file
View File

@ -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;
}

View File

@ -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;
}