#pragma once #include #include #include "common.hpp" #include "components/logger.hpp" POLYBAR_NS /** * Runs any libuv function with an integer error code return value and throws an * exception on error. */ #define UV(fun, ...) \ do { \ int res = fun(__VA_ARGS__); \ if (res < 0) { \ throw std::runtime_error( \ __FILE__ ":"s + std::to_string(__LINE__) + ": libuv error for '" #fun "': "s + uv_strerror(res)); \ } \ } while (0); template struct cb_helper { std::function func; static void callback(H* context, Args... args) { const auto unpackedThis = static_cast(context->data); return unpackedThis->func(std::forward(args)...); } }; /** * \tparam H Type of the handle * \tparam I Type of the handle passed to the callback. Often the same as H, but not always (e.g. uv_read_start) */ template struct UVHandleGeneric { UVHandleGeneric(std::function fun) { handle = std::make_unique(); cb = cb_helper{fun}; handle->data = &cb; } std::unique_ptr handle; cb_helper cb; }; template struct UVHandle : public UVHandleGeneric { UVHandle(std::function fun) : UVHandleGeneric(fun) {} }; struct SignalHandle : public UVHandle { SignalHandle(uv_loop_t* loop, std::function fun) : UVHandle(fun) { UV(uv_signal_init, loop, handle.get()); } void start(int signum) { UV(uv_signal_start, handle.get(), cb.callback, signum); } }; struct PollHandle : public UVHandle { // TODO wrap callback and handle status PollHandle(uv_loop_t* loop, int fd, std::function fun) : UVHandle(fun) { UV(uv_poll_init, loop, handle.get(), fd); } void start(int events) { UV(uv_poll_start, handle.get(), events, &cb.callback); } }; struct FSEventHandle : public UVHandle { // TODO wrap callback and handle status FSEventHandle(uv_loop_t* loop, std::function fun) : UVHandle(fun) { UV(uv_fs_event_init, loop, handle.get()); } void start(const string& path) { UV(uv_fs_event_start, handle.get(), &cb.callback, path.c_str(), 0); } }; struct PipeHandle : public UVHandleGeneric { std::function func; int fd; PipeHandle(uv_loop_t* loop, std::function fun) : UVHandleGeneric([&](ssize_t nread, const uv_buf_t* buf) { read_cb(nread, buf); }), func(fun) { UV(uv_pipe_init, loop, handle.get(), false); } void start(int fd) { this->fd = fd; UV(uv_pipe_open, handle.get(), fd); UV(uv_read_start, (uv_stream_t*)handle.get(), alloc_cb, &cb.callback); } static void alloc_cb(uv_handle_t*, size_t, uv_buf_t* buf) { buf->base = new char[BUFSIZ]; buf->len = BUFSIZ; } void read_cb(ssize_t nread, const uv_buf_t* buf) { auto log = logger::make(); if (nread > 0) { string payload = string(buf->base, nread); log.notice("Bytes read: %d: '%s'", nread, payload); func(payload); } else if (nread < 0) { if (nread != UV_EOF) { log.err("Read error: %s", uv_err_name(nread)); uv_close((uv_handle_t*)handle.get(), nullptr); } else { // TODO this causes constant EOFs start(this->fd); } } if (buf->base) { delete[] buf->base; } } }; struct TimerHandle : public UVHandle { TimerHandle(uv_loop_t* loop, std::function fun) : UVHandle(fun) { UV(uv_timer_init, loop, handle.get()); } void start(uint64_t timeout, uint64_t repeat) { UV(uv_timer_start, handle.get(), &cb.callback, timeout, repeat); } }; class eventloop { public: eventloop(); ~eventloop(); void run(); void stop(); /** * TODO make protected */ uv_loop_t* get() const { return m_loop.get(); } void signal_handler(int signum, std::function fun) { auto handle = std::make_unique(get(), fun); handle->start(signum); m_sig_handles.push_back(std::move(handle)); } void poll_handler(int events, int fd, std::function fun) { auto handle = std::make_unique(get(), fd, fun); handle->start(events); m_poll_handles.push_back(std::move(handle)); } void fs_event_handler(const string& path, std::function fun) { auto handle = std::make_unique(get(), fun); handle->start(path); m_fs_event_handles.push_back(std::move(handle)); } protected: template static void generic_cb(H* handle, Args&&... args) { (static_cast(handle->data).*F)(std::forward(args)...); } private: std::unique_ptr m_loop{nullptr}; vector> m_sig_handles; vector> m_poll_handles; vector> m_fs_event_handles; }; POLYBAR_NS_END