diff --git a/include/hyprutils/utils/FileDescriptor.hpp b/include/hyprutils/utils/FileDescriptor.hpp new file mode 100644 index 0000000..c03aeab --- /dev/null +++ b/include/hyprutils/utils/FileDescriptor.hpp @@ -0,0 +1,33 @@ +#pragma once + +namespace Hyprutils { + namespace Utils { + class CFileDescriptor { + public: + CFileDescriptor() = default; + explicit CFileDescriptor(int fd); + CFileDescriptor(CFileDescriptor&&); + CFileDescriptor& operator=(CFileDescriptor&&); + ~CFileDescriptor(); + + bool operator==(const CFileDescriptor& rhs) const { + return m_fd == rhs.m_fd; + } + + bool isValid() const; + int get() const; + int take(); + void reset(); + CFileDescriptor duplicate() const; + + bool isReadable() const; + bool isClosed() const; + + static bool isReadable(int fd); + static bool isClosed(int fd); + + private: + int m_fd = -1; + }; + }; +}; diff --git a/src/utils/FileDescriptor.cpp b/src/utils/FileDescriptor.cpp new file mode 100644 index 0000000..145b02b --- /dev/null +++ b/src/utils/FileDescriptor.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +using namespace Hyprutils::Utils; + +CFileDescriptor::CFileDescriptor(int fd) : m_fd(fd) {} + +CFileDescriptor::CFileDescriptor(CFileDescriptor&& other) : m_fd(std::exchange(other.m_fd, -1)) {} + +CFileDescriptor& CFileDescriptor::operator=(CFileDescriptor&& other) { + if (this == &other) + return *this; + + reset(); + m_fd = std::exchange(other.m_fd, -1); + return *this; +} + +CFileDescriptor::~CFileDescriptor() { + reset(); +} + +bool CFileDescriptor::isValid() const { + return m_fd != -1; +} + +int CFileDescriptor::get() const { + return m_fd; +} + +int CFileDescriptor::take() { + return std::exchange(m_fd, -1); +} + +void CFileDescriptor::reset() { + if (m_fd != -1) { + close(m_fd); + m_fd = -1; + } +} + +CFileDescriptor CFileDescriptor::duplicate() const { + if (m_fd == -1) + return {}; + + return CFileDescriptor{fcntl(m_fd, F_DUPFD_CLOEXEC, 0)}; +} + +bool CFileDescriptor::isClosed() const { + return isClosed(m_fd); +} + +bool CFileDescriptor::isReadable() const { + return isReadable(m_fd); +} + +bool CFileDescriptor::isClosed(int fd) { + pollfd pfd = { + .fd = fd, + .events = POLLIN, + .revents = 0, + }; + + if (poll(&pfd, 1, 0) < 0) + return true; + + return pfd.revents & (POLLHUP | POLLERR); +} + +bool CFileDescriptor::isReadable(int fd) { + pollfd pfd = {.fd = fd, .events = POLLIN, .revents = 0}; + + return poll(&pfd, 1, 0) > 0 && (pfd.revents & POLLIN); +}