Add SDL_RemovePathWatch().

Rename SDL_WatchPathForChange() to SDL_AddPathWatch().
Improve documentation.
This commit is contained in:
meyraud705 2026-05-07 21:30:39 +02:00
parent 3d9e800294
commit 0ea3074fa1
7 changed files with 123 additions and 21 deletions

View file

@ -260,8 +260,8 @@ typedef enum SDL_EventType
SDL_EVENT_CAMERA_DEVICE_DENIED, /**< A camera device has been denied for use by the user. */
/* File watch events */
SDL_EVENT_FILE_CHANGED = 0x1500, /**< A watched file was written. */
SDL_EVENT_FILE_WATCH_ERROR, /**< Watched files may have been modified, but the events are lost. */
SDL_EVENT_FILE_DATA_WRITTEN = 0x1500, /**< Data was written in a watched file. */
SDL_EVENT_FILE_WATCH_ERROR, /**< Watched files may have been modified, but the events are lost. */
/* Render events */
SDL_EVENT_RENDER_TARGETS_RESET = 0x2000, /**< The render targets have been reset and their contents need to be updated */
@ -977,16 +977,16 @@ typedef struct SDL_SensorEvent
/**
* File watch event structure (event.file_watch.*)
*
* You can add file to the watch list with SDL_WatchPathForChanges().
* You can add file to the watch list with SDL_AddPathWatch().
*
* \sa SDL_WatchPathForChanges
* \sa SDL_AddPathWatch
*/
typedef struct SDL_FileWatchEvent
{
SDL_EventType type; /**< SDL_EVENT_FILE_CHANGED or SDL_EVENT_FILE_WATCH_ERROR */
SDL_EventType type; /**< SDL_EVENT_FILE_DATA_WRITTEN or SDL_EVENT_FILE_WATCH_ERROR */
Uint32 reserved;
Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */
const char *path; /**< Path of the modified file for SDL_EVENT_FILE_CHANGED, NULL for SDL_EVENT_FILE_WATCH_ERROR */
const char *path; /**< Path of the modified file for SDL_EVENT_FILE_DATA_WRITTEN, NULL for SDL_EVENT_FILE_WATCH_ERROR */
} SDL_FileWatchEvent;
/**

View file

@ -532,7 +532,7 @@ extern SDL_DECLSPEC char * SDLCALL SDL_GetCurrentDirectory(void);
/**
* A function pointer used for callbacks that watch for files change.
*
* \param userdata what was passed as `userdata` to SDL_WatchPathForChanges().
* \param userdata what was passed as `userdata` to SDL_AddPathWatch().
* \param path path of file that was modified.
*
* \threadsafety SDL may call this callback at any time from any thread; the
@ -543,9 +543,9 @@ typedef void (SDLCALL *SDL_FileWatchCallback)(void *userdata, const char *path);
/**
* This function adds a file watcher that will fires an app-provided callback
* and send an SDL_EVENT_FILE_CHANGED event every time the file is modified. If
* path is a directory, the callback will be called for every file modified in
* that directory.
* and send an SDL_EVENT_FILE_CHANGED event every time data is written in the
* file. If path is a directory, the callback will be called for every file
* in that directory that has data written into.
*
* \param path file or directory path to watch.
* \param callback a function that is called when the watched file is modified.
@ -558,8 +558,25 @@ typedef void (SDLCALL *SDL_FileWatchCallback)(void *userdata, const char *path);
* \threadsafety It is safe to call this function from any thread.
*
* \sa SDL_FileWatchEvent
* \sa SDL_RemovePathWatch
*/
extern SDL_DECLSPEC bool SDLCALL SDL_WatchPathForChanges(const char *path, SDL_FileWatchCallback callback, void *userdata);
extern SDL_DECLSPEC bool SDLCALL SDL_AddPathWatch(const char *path, SDL_FileWatchCallback callback, void *userdata);
/**
* Remove an file watch callback added with SDL_AddPathWatch().
*
* This function takes the same input as SDL_AddPathWatch() to identify and
* delete the corresponding callback.
*
* \param path the path originally passed to SDL_AddPathWatch().
* \param callback the callback originally passed to SDL_AddPathWatch().
* \param userdata the pointer originally passed to SDL_AddPathWatch().
*
* \threadsafety It is safe to call this function from any thread.
*
* \sa SDL_AddPathWatch
*/
extern SDL_DECLSPEC void SDLCALL SDL_RemovePathWatch(const char *path, SDL_FileWatchCallback callback, void *userdata);
/* Ends C function definitions when using C++ */
#ifdef __cplusplus

View file

@ -142,7 +142,7 @@ bool SDL_GetPathInfo(const char *path, SDL_PathInfo *info)
return SDL_SYS_GetPathInfo(path, info);
}
bool SDL_WatchPathForChanges(const char *path, SDL_FileWatchCallback cb, void *userdata)
bool SDL_AddPathWatch(const char *path, SDL_FileWatchCallback cb, void *userdata)
{
CHECK_PARAM(!path) {
return SDL_InvalidParamError("path");
@ -150,7 +150,18 @@ bool SDL_WatchPathForChanges(const char *path, SDL_FileWatchCallback cb, void *u
CHECK_PARAM(path[0] == '\0') {
return SDL_InvalidParamError("path");
}
return SDL_SYS_WatchPathForChanges(path, cb, userdata);
return SDL_SYS_AddPathWatch(path, cb, userdata);
}
void SDL_RemovePathWatch(const char *path, SDL_FileWatchCallback cb, void *userdata)
{
CHECK_PARAM(!path) {
return;
}
CHECK_PARAM(path[0] == '\0') {
return;
}
SDL_SYS_RemovePathWatch(path, cb, userdata);
}
static bool EverythingMatch(const char *pattern, const char *str, bool *matched_to_dir)

View file

@ -35,7 +35,8 @@ extern bool SDL_SYS_CopyFile(const char *oldpath, const char *newpath);
extern bool SDL_SYS_CreateDirectory(const char *path);
extern bool SDL_SYS_GetPathInfo(const char *path, SDL_PathInfo *info);
extern bool SDL_SYS_WatchPathForChanges(const char *path, SDL_FileWatchCallback cb, void *userdata);
extern bool SDL_SYS_AddPathWatch(const char *path, SDL_FileWatchCallback cb, void *userdata);
extern void SDL_SYS_RemovePathWatch(const char *path, SDL_FileWatchCallback cb, void *userdata);
extern void SDL_SYS_QuitPathWatch(void);
typedef bool (*SDL_GlobEnumeratorFunc)(const char *path, SDL_EnumerateDirectoryCallback cb, void *cbuserdata, void *userdata);

View file

@ -58,11 +58,15 @@ bool SDL_SYS_GetPathInfo(const char *path, SDL_PathInfo *info)
return SDL_Unsupported();
}
bool SDL_SYS_WatchPathForChanges(const char *path, SDL_FileWatchCallback cb, void *userdata)
bool SDL_SYS_AddPathWatch(const char *path, SDL_FileWatchCallback cb, void *userdata)
{
return SDL_Unsupported();
}
void SDL_SYS_RemovePathWatch(const char *path, SDL_FileWatchCallback cb, void *userdata)
{
}
void SDL_SYS_QuitPathWatch(void)
{
}

View file

@ -451,9 +451,10 @@ static int SDL_inotify_init1(void)
return fd;
}
#endif // HAVE_INOTIFY_INIT1
#endif // HAVE_INOTIFY
bool SDL_SYS_WatchPathForChanges(const char *path, SDL_FileWatchCallback cb, void *user_data)
bool SDL_SYS_AddPathWatch(const char *path, SDL_FileWatchCallback cb, void *user_data)
{
#ifdef HAVE_INOTIFY
if (!watch_descriptor_table) {
@ -487,7 +488,11 @@ bool SDL_SYS_WatchPathForChanges(const char *path, SDL_FileWatchCallback cb, voi
return false;
}
}
const size_t slen = SDL_strlen(path);
if (slen >= PATH_MAX) {
return SDL_SetError("path too long");
}
WatchEntry *watch_entry = SDL_malloc(sizeof(*watch_entry) + slen + 1);
if (!watch_entry) {
return false;
@ -507,7 +512,7 @@ bool SDL_SYS_WatchPathForChanges(const char *path, SDL_FileWatchCallback cb, voi
SDL_free(watch_entry);
return SDL_SetError("inotify_add_watch failed: %s", strerror(errno));
}
if (!SDL_InsertIntoHashTable(watch_descriptor_table, (void *)(intptr_t)wd, watch_entry, false)) {
if (!SDL_InsertIntoHashTable(watch_descriptor_table, (void *) (intptr_t) wd, watch_entry, false)) {
inotify_rm_watch(inotify_fd, wd);
SDL_UnlockMutex(file_watch_lock);
SDL_free(watch_entry);
@ -561,20 +566,24 @@ static int SDL_FileWatchThread(void *userdata)
while (remain > 0) {
const WatchEntry *watch_entry;
if (SDL_FindInHashTable(watch_descriptor_table, (void *)(intptr_t)buf.event.wd, (const void **)&watch_entry)) {
if (SDL_FindInHashTable(watch_descriptor_table, (void *) (intptr_t) buf.event.wd, (const void **) &watch_entry)) {
if (buf.event.mask & IN_Q_OVERFLOW) {
SendFileWatchEvent(SDL_EVENT_FILE_WATCH_ERROR, NULL);
} else if (buf.event.mask & IN_IGNORED) {
// removing a watch generate an IN_IGNORED event
} else if (buf.event.mask & IN_UNMOUNT) {
// file system containing watched path was unmounted
} else if (buf.event.len != 0) {
(void)SDL_snprintf(path, SDL_arraysize(path), "%s/%s", watch_entry->path, buf.event.name);
if (watch_entry->callback) {
watch_entry->callback(watch_entry->user_data, path);
}
SendFileWatchEvent(SDL_EVENT_FILE_CHANGED, path);
SendFileWatchEvent(SDL_EVENT_FILE_DATA_WRITTEN, path);
} else {
if (watch_entry->callback) {
watch_entry->callback(watch_entry->user_data, watch_entry->path);
}
SendFileWatchEvent(SDL_EVENT_FILE_CHANGED, watch_entry->path);
SendFileWatchEvent(SDL_EVENT_FILE_DATA_WRITTEN, watch_entry->path);
}
}
@ -589,8 +598,64 @@ static int SDL_FileWatchThread(void *userdata)
}
return 0;
}
typedef struct FindWatchEntryByValueData {
WatchEntry *entry_to_find;
const void *key_found;
} FindWatchEntryByValueData;
static bool SDLCALL FindWatchEntryByValue(void *userdata, const SDL_HashTable *table, const void *key, const void *value)
{
FindWatchEntryByValueData *d = (FindWatchEntryByValueData *) userdata;
const WatchEntry *iterator = (const WatchEntry *) value;
if (SDL_strcmp(iterator->path, d->entry_to_find->path) == 0
&& iterator->callback == d->entry_to_find->user_data
&& iterator->user_data == d->entry_to_find->user_data) {
d->key_found = key;
return false;
}
return true;
}
#endif // HAVE_INOTIFY
void SDL_SYS_RemovePathWatch(const char *path, SDL_FileWatchCallback cb, void *user_data)
{
#ifdef HAVE_INOTIFY
if (!watch_descriptor_table) {
return;
}
const size_t slen = SDL_strlen(path);
if (slen >= PATH_MAX) {
return;
}
union LongestWatchEntry{
WatchEntry entry;
char enougn_for_longest_path[sizeof(WatchEntry) + PATH_MAX];
} watch_entry;
watch_entry.entry.callback = cb;
watch_entry.entry.user_data = user_data;
SDL_memcpy(watch_entry.entry.path, path, slen + 1);
// remove separator at the end of the path, it is added back when
// concatenating directory path and file name
if (watch_entry.entry.path[slen - 1] == '/') {
watch_entry.entry.path[slen - 1] = '\0';
}
FindWatchEntryByValueData data = {&watch_entry.entry, (void *) (intptr_t) -1};
SDL_LockMutex(file_watch_lock);
if (!SDL_IterateHashTable(watch_descriptor_table, FindWatchEntryByValue, &data)) {
SDL_UnlockMutex(file_watch_lock);
return;
}
if (data.key_found != (void *) (intptr_t) -1) {
SDL_RemoveFromHashTable(watch_descriptor_table, data.key_found);
inotify_rm_watch(inotify_fd, (int) (intptr_t) data.key_found);
}
SDL_UnlockMutex(file_watch_lock);
#endif // HAVE_INOTIFY
}
void SDL_SYS_QuitPathWatch(void)
{
#ifdef HAVE_INOTIFY

View file

@ -231,11 +231,15 @@ bool SDL_SYS_GetPathInfo(const char *path, SDL_PathInfo *info)
return true;
}
bool SDL_SYS_WatchPathForChanges(const char *path, SDL_FileWatchCallback cb, void *userdata)
bool SDL_SYS_AddPathWatch(const char *path, SDL_FileWatchCallback cb, void *userdata)
{
return SDL_Unsupported();
}
void SDL_SYS_RemovePathWatch(const char *path, SDL_FileWatchCallback cb, void *userdata)
{
}
void SDL_SYS_QuitPathWatch(void)
{
}