1
+ // //////////////////////////////////////////////////////////////////////////////////////////////////
2
+ // Copyright (c) 2025 RacoonStudios
3
+ //
4
+ // Permission is hereby granted, free of charge, to any person obtaining a copy of this
5
+ // software and associated documentation files (the "Software"), to deal in the Software
6
+ // without restriction, including without limitation the rights to use, copy, modify, merge,
7
+ // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
8
+ // to whom the Software is furnished to do so, subject to the following conditions:
9
+ //
10
+ // The above copyright notice and this permission notice shall be included in all copies or
11
+ // substantial portions of the Software.
12
+ //
13
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
14
+ // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
15
+ // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
16
+ // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
17
+ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
18
+ // DEALINGS IN THE SOFTWARE.
19
+ // //////////////////////////////////////////////////////////////////////////////////////////////////
20
+
21
+
22
+ // [-------------------------------------------------------]
23
+ // [ Includes ]
24
+ // [-------------------------------------------------------]
25
+ #include " core/linux/linux_file_watcher.h"
26
+ #include " core/memory/memory.h"
27
+ #include < dirent.h>
28
+ #include < unistd.h>
29
+ #include < sys/inotify.h>
30
+
31
+
32
+ // [-------------------------------------------------------]
33
+ // [ Namespace ]
34
+ // [-------------------------------------------------------]
35
+ namespace core {
36
+
37
+
38
+ namespace detail {
39
+
40
+ static core::Vector<core::String> list_directory_entries (const char * dirPath) {
41
+ core::Vector<core::String> entries;
42
+
43
+ DIR* dir = opendir (dirPath);
44
+
45
+ if (dir != nullptr ) {
46
+ while (true ) {
47
+ struct dirent * dirEntry = readdir (dir);
48
+
49
+ if (dirEntry == nullptr ) {
50
+ break ;
51
+ }
52
+
53
+ core::uint32 numBytes = strlen (dirEntry->d_name );
54
+ char * stringBuffer = new char [numBytes];
55
+
56
+ core::Memory::copy (&stringBuffer[0 ], dirEntry->d_name , numBytes + 1 );
57
+
58
+ entries.push_back (stringBuffer);
59
+ }
60
+ }
61
+
62
+ return entries;
63
+ }
64
+
65
+ static void create_watcher_recursively (core::int32 fileHandle, const char * dirPath) {
66
+ core::Path path (dirPath);
67
+ if (path.is_directory ()) {
68
+ // Entry for a directory, add a watcher for that as well
69
+ inotify_add_watch (
70
+ fileHandle,
71
+ dirPath,
72
+ IN_CREATE | IN_DELETE | IN_MODIFY | IN_MOVED_TO | IN_MOVED_FROM | IN_MOVE_SELF | IN_DELETE_SELF);
73
+
74
+ // Get all elements of the directory and call this recursively
75
+ core::Vector<core::String> fileEntries = list_directory_entries (dirPath);
76
+
77
+ for (core::uint32 i = 0 ; i < fileEntries.size (); ++i) {
78
+ core::String& fileEntry = fileEntries[i];
79
+ // Skip the entries for the current directory and the parent directory
80
+ if (fileEntry[0 ] == ' .' ) {
81
+ continue ;
82
+ }
83
+
84
+ core::Path fullFilePath = path / fileEntry;
85
+
86
+ // Call recursively
87
+ create_watcher_recursively (
88
+ fileHandle,
89
+ fullFilePath.get_native_path ().c_str ());
90
+ }
91
+ }
92
+ }
93
+
94
+ }
95
+
96
+
97
+ // [-------------------------------------------------------]
98
+ // [ Classes ]
99
+ // [-------------------------------------------------------]
100
+ LinuxFileWatcher::LinuxFileWatcher (const Path &path)
101
+ : FileWatcherImpl(path)
102
+ , mHandle (0 ) {
103
+ // Initialize the notification handle
104
+ initialize_notification_handle ();
105
+ }
106
+
107
+ LinuxFileWatcher::LinuxFileWatcher (const String &watcherPath)
108
+ : FileWatcherImpl(watcherPath)
109
+ , mHandle (0 ) {
110
+ // Initialize the notification handle
111
+ initialize_notification_handle ();
112
+ }
113
+
114
+ LinuxFileWatcher::~LinuxFileWatcher () {
115
+ // Nothing to do here
116
+ close ();
117
+ }
118
+
119
+ bool LinuxFileWatcher::any_changes () const {
120
+ fd_set fileHandles;
121
+ FD_ZERO (&fileHandles);
122
+ FD_SET (mHandle , &fileHandles);
123
+
124
+ struct timeval timeout;
125
+ timeout.tv_sec = 0 ;
126
+ timeout.tv_usec = 0 ;
127
+
128
+ bool anyChanges = select (FD_SETSIZE, &fileHandles, nullptr , nullptr , &timeout) > 0 ;
129
+
130
+ // Read flush element
131
+ if (anyChanges) {
132
+ read (mHandle , nullptr , 1024 );
133
+ }
134
+
135
+ return anyChanges;
136
+ }
137
+
138
+ void LinuxFileWatcher::close () {
139
+ ::close (mHandle );
140
+ }
141
+
142
+
143
+ void LinuxFileWatcher::initialize_notification_handle () {
144
+ mHandle = inotify_init ();
145
+
146
+ // Add watcher
147
+ if (mPath .is_directory ()) {
148
+ // Add watcher recursively here
149
+ detail::create_watcher_recursively (
150
+ mHandle ,
151
+ mPath .get_native_path ().c_str ());
152
+ } else {
153
+ // Just single file watching
154
+ inotify_add_watch (
155
+ mHandle ,
156
+ mPath .get_native_path ().c_str (),
157
+ IN_CREATE | IN_DELETE | IN_MODIFY | IN_MOVED_TO | IN_MOVED_FROM | IN_MOVE_SELF | IN_DELETE_SELF);
158
+ }
159
+ }
160
+
161
+
162
+ // [-------------------------------------------------------]
163
+ // [ Namespace ]
164
+ // [-------------------------------------------------------]
165
+ }
0 commit comments