# HG changeset patch # User František Kučera # Date 1701525776 -3600 # Node ID e6065118326fdcc251bc3645683dc0f2ff78a8c0 # Parent fd93a46db15bc5464f0099a0f14b5871bf30aeae monitor texture and shader file writes using inotify: print file events diff -r fd93a46db15b -r e6065118326f FileMonitor.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/FileMonitor.cpp Sat Dec 02 15:02:56 2023 +0100 @@ -0,0 +1,127 @@ +/** + * ShaderShark + * Copyright © 2023 František Kučera (Frantovo.cz, GlobalCode.info) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#include "FileMonitor.h" + +void checkError(bool hasError, const std::string& message) { + if (hasError) throw std::logic_error(message + strerror(errno)); +} + +class WatchedFile::Impl { +public: + int fd; + int wd; + std::string fileName; + uint32_t mask; + std::shared_ptr fileMonitorImpl; + + virtual ~Impl(); +}; + +WatchedFile::WatchedFile() { + impl = std::make_shared(); +} + +WatchedFile::~WatchedFile() { +} + +class FileMonitor::Impl { +public: + int fd; + std::map fileNames; + + void unwatch(int wd) { + int result = inotify_rm_watch(fd, wd); + checkError(result < 0, "unable to stop monitoring a file: "); + fileNames.erase(wd); + } + + virtual ~Impl() { + close(fd); + } + +}; + +FileMonitor::FileMonitor() : FileMonitor(std::make_shared()) { +} + +FileMonitor::FileMonitor(std::shared_ptr impl) { + this->impl = impl; + impl->fd = inotify_init1(IN_NONBLOCK); + checkError(impl->fd < 0, "unable to initialize FileMonitor: "); +} + +FileMonitor::~FileMonitor() { +} + +int FileMonitor::getFD() const { + return impl->fd; +} + +WatchedFile FileMonitor::watch(const std::string& fileName) { + return watch(fileName, IN_CLOSE_WRITE); +} + +WatchedFile FileMonitor::watch(const std::string& fileName, uint32_t mask) { + int wd = inotify_add_watch(impl->fd, fileName.c_str(), mask); + checkError(wd < 0, "unable to monitor a file: "); + impl->fileNames[wd] = fileName; + WatchedFile wf; + wf.impl->fd = impl->fd; + wf.impl->wd = wd; + wf.impl->fileMonitorImpl = impl; + wf.impl->fileName = fileName; + wf.impl->mask = mask; + return wf; +} + +bool FileMonitor::readEvent(FileEvent& event) { + struct inotify_event rawEvent; + int result = ::read(impl->fd, &rawEvent, sizeof (rawEvent)); + if (result == sizeof (rawEvent)) { + event.fileName = impl->fileNames[rawEvent.wd]; + event.mask = rawEvent.mask; + return true; + } else { + return false; + } +} + +WatchedFile::Impl::~Impl() { + fileMonitorImpl->unwatch(wd); +} + +FileMonitor WatchedFile::getFileMonitor() const { + return FileMonitor(impl->fileMonitorImpl); +} + +std::string WatchedFile::getFileName() const { + return impl->fileName; +} + +uint32_t WatchedFile::getMask() const { + return impl->mask; +} + + diff -r fd93a46db15b -r e6065118326f FileMonitor.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/FileMonitor.h Sat Dec 02 15:02:56 2023 +0100 @@ -0,0 +1,70 @@ +/** + * ShaderShark + * Copyright © 2023 František Kučera (Frantovo.cz, GlobalCode.info) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include + +class FileMonitor; + +class WatchedFile { +public: + class Impl; + WatchedFile(); + virtual ~WatchedFile(); + std::string getFileName() const; + uint32_t getMask() const; + FileMonitor getFileMonitor() const; +private: + std::shared_ptr impl; + friend FileMonitor; +}; + +class FileEvent { +public: + std::string fileName; + uint32_t mask; + // TODO: more information, see: man 7 inotify +}; + +class FileMonitor { +public: + class Impl; + FileMonitor(); + virtual ~FileMonitor(); + /** + * @return file descriptor that can be monitored by e.g. epoll + */ + int getFD() const; + /** + * @param fileName file to be monitored + * @param mask IN_CLOSE_WRITE and other IN_* + * @return monitoring will continue until any copy of this object exists + */ + WatchedFile watch(const std::string& fileName); + WatchedFile watch(const std::string& fileName, uint32_t mask); + /** + * @param event object to be filled with information + * @return true if event was filled, call this method until false returned + */ + bool readEvent(FileEvent& event); +private: + std::shared_ptr impl; + FileMonitor(std::shared_ptr impl); + friend WatchedFile; +}; diff -r fd93a46db15b -r e6065118326f Makefile --- a/Makefile Fri Dec 01 21:02:02 2023 +0100 +++ b/Makefile Sat Dec 02 15:02:56 2023 +0100 @@ -26,7 +26,13 @@ build: mkdir -p $(@) -SRC=Shark.cpp shader-shark.cpp ImageLoader.cpp Shader.cpp Program.cpp +SRC= \ + Shark.cpp \ + shader-shark.cpp \ + ImageLoader.cpp \ + Shader.cpp \ + Program.cpp \ + FileMonitor.cpp build/shader-shark: $(SRC) build *.h $(CXX) -std=c++20 -g -o $(@) $(SRC) $$(pkg-config --cflags --libs \ diff -r fd93a46db15b -r e6065118326f Shark.cpp --- a/Shark.cpp Fri Dec 01 21:02:02 2023 +0100 +++ b/Shark.cpp Sat Dec 02 15:02:56 2023 +0100 @@ -102,6 +102,7 @@ int x11fd = XConnectionNumber(dpy); EPoll epoll; epoll.add(x11fd); + epoll.add(fileMonitor.getFD()); try { epoll.add(setNonBlocking(STDIN_FILENO)); } catch (const EPoll::Exception& e) { @@ -211,6 +212,14 @@ } logOutput << std::endl; + } else if (epoll[epollEvent].data.fd == fileMonitor.getFD()) { + std::cout << "FileMonitor event:" << std::endl; + for (FileEvent fe; fileMonitor.readEvent(fe);) { + std::cout << " " + << " file: " << fe.fileName + << " mask: " << fe.mask + << std::endl; + } } else { logOutput << "error: event on an unexpected FD: " @@ -353,6 +362,7 @@ void Shark::loadTextures() { for (const Configuration::Texture& tex : cfg.textures) { textures.push_back(loadTexture(tex.fileName)); + watchedFiles.push_back(fileMonitor.watch(tex.fileName)); // TODO: review texture loading and binding // works even without this - default texture // glUniform1i(ProgAttr.jazz, jazz); @@ -400,6 +410,7 @@ program->attachShader(*shader.get()); shaders.push_back(shader); + watchedFiles.push_back(fileMonitor.watch(fileName)); std::cerr << "GLSL loaded: " << fileName.c_str() << std::endl; } diff -r fd93a46db15b -r e6065118326f Shark.h --- a/Shark.h Fri Dec 01 21:02:02 2023 +0100 +++ b/Shark.h Sat Dec 02 15:02:56 2023 +0100 @@ -37,6 +37,7 @@ #include "ImageLoader.h" #include "Shader.h" #include "Program.h" +#include "FileMonitor.h" class Shark { private: @@ -174,7 +175,9 @@ Window win; XVisualInfo* vi; GLXContext glc; - + + FileMonitor fileMonitor; + std::vector watchedFiles; ImageLoader imageLoader; std::vector> shaders; std::shared_ptr shaderProgram; diff -r fd93a46db15b -r e6065118326f nbproject/configurations.xml --- a/nbproject/configurations.xml Fri Dec 01 21:02:02 2023 +0100 +++ b/nbproject/configurations.xml Sat Dec 02 15:02:56 2023 +0100 @@ -2,6 +2,7 @@ + FileMonitor.cpp ImageLoader.cpp Program.cpp Shader.cpp @@ -55,6 +56,10 @@ + + + +