mirror of
https://github.com/libsdl-org/SDL.git
synced 2026-06-06 14:43:20 +00:00
Add callback to file watch.
This commit is contained in:
parent
5ed86c7580
commit
7b8d6155cc
9 changed files with 116 additions and 64 deletions
|
|
@ -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_WATCH_ERROR = 0x1500, /**< Watched files may have been modified, but the events are lost. */
|
||||
SDL_EVENT_FILE_CHANGED, /**< A watched file was written. */
|
||||
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. */
|
||||
|
||||
/* Render events */
|
||||
SDL_EVENT_RENDER_TARGETS_RESET = 0x2000, /**< The render targets have been reset and their contents need to be updated */
|
||||
|
|
@ -976,6 +976,10 @@ typedef struct SDL_SensorEvent
|
|||
|
||||
/**
|
||||
* File watch event structure (event.file_watch.*)
|
||||
*
|
||||
* You can add file to the watch list with SDL_WatchFileForChanges().
|
||||
*
|
||||
* \sa SDL_WatchFileForChanges
|
||||
*/
|
||||
typedef struct SDL_FileWatchEvent
|
||||
{
|
||||
|
|
|
|||
|
|
@ -529,6 +529,38 @@ extern SDL_DECLSPEC char ** SDLCALL SDL_GlobDirectory(const char *path, const ch
|
|||
*/
|
||||
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_WatchFileForChanges().
|
||||
* \param path path of file that was modified.
|
||||
*
|
||||
* \threadsafety SDL may call this callback at any time from any thread; the
|
||||
* application is responsible for locking resources the callback
|
||||
* touches that need to be protected.
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* \param path file or directory path to watch.
|
||||
* \param callback a function that is called when the watched file is modified.
|
||||
* Can be NULL if you only want to receive event.
|
||||
* \param userdata a pointer that is passed to `callback`.
|
||||
*
|
||||
* \returns true on success or false on failure; call SDL_GetError() for more
|
||||
* information.
|
||||
*
|
||||
* \threadsafety It is safe to call this function from any thread.
|
||||
*
|
||||
* \sa SDL_FileWatchEvent
|
||||
*/
|
||||
extern SDL_DECLSPEC bool SDLCALL SDL_WatchFileForChanges(const char *path, SDL_FileWatchCallback callback, void *userdata);
|
||||
|
||||
/* Ends C function definitions when using C++ */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@
|
|||
#include "../SDL_hints_c.h"
|
||||
#include "../audio/SDL_audio_c.h"
|
||||
#include "../camera/SDL_camera_c.h"
|
||||
#include "../filesystem/SDL_filesystem_c.h"
|
||||
#include "../timer/SDL_timer_c.h"
|
||||
#include "../core/linux/SDL_udev.h"
|
||||
#ifndef SDL_JOYSTICK_DISABLED
|
||||
|
|
@ -1459,8 +1458,6 @@ bool SDL_RunOnMainThread(SDL_MainThreadCallback callback, void *userdata, bool w
|
|||
|
||||
void SDL_PumpEventMaintenance(void)
|
||||
{
|
||||
SDL_UpdateFileWatch();
|
||||
|
||||
#ifdef SDL_USE_LIBUDEV
|
||||
SDL_UDEV_Poll();
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -142,17 +142,12 @@ bool SDL_GetPathInfo(const char *path, SDL_PathInfo *info)
|
|||
return SDL_SYS_GetPathInfo(path, info);
|
||||
}
|
||||
|
||||
bool SDL_WatchFileForChanges(const char *path)
|
||||
bool SDL_WatchFileForChanges(const char *path, SDL_FileWatchCallback cb, void *userdata)
|
||||
{
|
||||
CHECK_PARAM(!path) {
|
||||
return SDL_InvalidParamError("path");
|
||||
}
|
||||
return SDL_SYS_WatchFileForChanges(path);
|
||||
}
|
||||
|
||||
void SDL_UpdateFileWatch(void)
|
||||
{
|
||||
SDL_SYS_UpdateFileWatch();
|
||||
return SDL_SYS_WatchFileForChanges(path, cb, userdata);
|
||||
}
|
||||
|
||||
static bool EverythingMatch(const char *pattern, const char *str, bool *matched_to_dir)
|
||||
|
|
|
|||
|
|
@ -25,6 +25,5 @@
|
|||
extern void SDL_InitFilesystem(void);
|
||||
extern void SDL_QuitFilesystem(void);
|
||||
|
||||
extern void SDL_UpdateFileWatch(void);
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -35,8 +35,7 @@ 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_WatchFileForChanges(const char *path);
|
||||
extern void SDL_SYS_UpdateFileWatch(void);
|
||||
extern bool SDL_SYS_WatchFileForChanges(const char *path, SDL_FileWatchCallback cb, void *userdata);
|
||||
extern void SDL_SYS_QuitFileWatch(void);
|
||||
|
||||
typedef bool (*SDL_GlobEnumeratorFunc)(const char *path, SDL_EnumerateDirectoryCallback cb, void *cbuserdata, void *userdata);
|
||||
|
|
|
|||
|
|
@ -58,15 +58,11 @@ bool SDL_SYS_GetPathInfo(const char *path, SDL_PathInfo *info)
|
|||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
bool SDL_SYS_WatchFileForChanges(const char *path)
|
||||
bool SDL_SYS_WatchFileForChanges(const char *path, SDL_FileWatchCallback cb, void *userdata)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
void SDL_SYS_UpdateFileWatch(void)
|
||||
{
|
||||
}
|
||||
|
||||
void SDL_SYS_QuitFileWatch(void)
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@
|
|||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
// System dependent filesystem routines
|
||||
|
||||
#include <SDL3/SDL_atomic.h>
|
||||
#include <SDL3/SDL_thread.h>
|
||||
#include "../SDL_sysfilesystem.h"
|
||||
#include "../../SDL_hashtable.h"
|
||||
#include "../../events/SDL_events_c.h"
|
||||
|
|
@ -419,8 +421,18 @@ bool SDL_SYS_GetPathInfo(const char *path, SDL_PathInfo *info)
|
|||
|
||||
#ifdef HAVE_INOTIFY
|
||||
static int inotify_fd = -1;
|
||||
static SDL_HashTable *watch_descriptor_table = NULL; // stores directory or file path for a watch descriptor
|
||||
typedef struct WatchEntry
|
||||
{
|
||||
SDL_FileWatchCallback callback;
|
||||
void *user_data;
|
||||
char path[]; // directory or file path
|
||||
} WatchEntry;
|
||||
static SDL_HashTable *watch_descriptor_table = NULL; // stores WatchEntry for a watch descriptor
|
||||
|
||||
static int SDL_FileWatchThread(void *user_data);
|
||||
static SDL_Thread *file_watch_thread = NULL;
|
||||
static SDL_Mutex *file_watch_lock = NULL;
|
||||
static SDL_AtomicInt quit_watch_file;
|
||||
|
||||
#ifdef HAVE_INOTIFY_INIT1
|
||||
static int SDL_inotify_init1(void)
|
||||
|
|
@ -441,7 +453,7 @@ static int SDL_inotify_init1(void)
|
|||
#endif // HAVE_INOTIFY_INIT1
|
||||
#endif // HAVE_INOTIFY
|
||||
|
||||
bool SDL_SYS_WatchFileForChanges(const char *path)
|
||||
bool SDL_SYS_WatchFileForChanges(const char *path, SDL_FileWatchCallback cb, void *user_data)
|
||||
{
|
||||
#ifdef HAVE_INOTIFY
|
||||
if (!watch_descriptor_table) {
|
||||
|
|
@ -463,29 +475,41 @@ bool SDL_SYS_WatchFileForChanges(const char *path)
|
|||
inotify_fd = -1;
|
||||
return false;
|
||||
}
|
||||
file_watch_thread = SDL_CreateThread(SDL_FileWatchThread, "SDL_FileWatch", NULL);
|
||||
SDL_SetAtomicInt(&quit_watch_file, 0);
|
||||
if (!file_watch_thread) {
|
||||
SDL_DestroyHashTable(watch_descriptor_table);
|
||||
watch_descriptor_table = NULL;
|
||||
close(inotify_fd);
|
||||
inotify_fd = -1;
|
||||
SDL_DestroyMutex(file_watch_lock);
|
||||
file_watch_lock = NULL;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
char *p = SDL_strdup(path);
|
||||
if (!p) {
|
||||
const size_t slen = SDL_strlen(path);
|
||||
WatchEntry *watch_entry = SDL_malloc(sizeof(*watch_entry) + slen + 1);
|
||||
if (!watch_entry) {
|
||||
return false;
|
||||
}
|
||||
watch_entry->callback = cb;
|
||||
watch_entry->user_data = user_data;
|
||||
SDL_memcpy(watch_entry->path, path, slen + 1);
|
||||
// remove separator at the end of the path
|
||||
const size_t slen = SDL_strlen(p);
|
||||
if (p[slen - 1] == '/') {
|
||||
p[slen - 1] = '\0';
|
||||
if (watch_entry->path[slen - 1] == '/') {
|
||||
watch_entry->path[slen - 1] = '\0';
|
||||
}
|
||||
|
||||
SDL_LockMutex(file_watch_lock);
|
||||
int wd = inotify_add_watch(inotify_fd, p, IN_MODIFY);
|
||||
int wd = inotify_add_watch(inotify_fd, path, IN_MODIFY);
|
||||
if (wd == -1) {
|
||||
SDL_UnlockMutex(file_watch_lock);
|
||||
SDL_free(p);
|
||||
SDL_free(watch_entry);
|
||||
return SDL_SetError("inotify_add_watch failed: %s", strerror(errno));
|
||||
}
|
||||
if (!SDL_InsertIntoHashTable(watch_descriptor_table, (void *)(intptr_t)wd, p, 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(p);
|
||||
SDL_free(watch_entry);
|
||||
return false;
|
||||
}
|
||||
SDL_UnlockMutex(file_watch_lock);
|
||||
|
|
@ -495,10 +519,27 @@ bool SDL_SYS_WatchFileForChanges(const char *path)
|
|||
#endif // HAVE_INOTIFY
|
||||
}
|
||||
|
||||
void SDL_SYS_UpdateFileWatch(void)
|
||||
{
|
||||
#ifdef HAVE_INOTIFY
|
||||
if (inotify_fd >= 0) {
|
||||
static void SendFileWatchEvent(SDL_EventType event_type, const char *path) {
|
||||
if (SDL_EventEnabled(event_type)) {
|
||||
SDL_Event event;
|
||||
SDL_zero(event);
|
||||
event.type = event_type;
|
||||
event.common.timestamp = 0;
|
||||
if (path) {
|
||||
event.file_watch.path = SDL_CreateTemporaryString(path);
|
||||
if (!event.file_watch.path) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
}
|
||||
|
||||
static int SDL_FileWatchThread(void *userdata)
|
||||
{
|
||||
while (SDL_GetAtomicInt(&quit_watch_file) == 0) {
|
||||
SDL_Delay(100);
|
||||
SDL_LockMutex(file_watch_lock);
|
||||
union
|
||||
{
|
||||
|
|
@ -518,31 +559,21 @@ void SDL_SYS_UpdateFileWatch(void)
|
|||
}
|
||||
|
||||
while (remain > 0) {
|
||||
const char *watched_path;
|
||||
if (SDL_FindInHashTable(watch_descriptor_table, (void *)(intptr_t)buf.event.wd, (const void **)&watched_path)) {
|
||||
const char *path_tmp;
|
||||
SDL_EventType event_type;
|
||||
bool post_event = true;
|
||||
const WatchEntry *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) {
|
||||
event_type = SDL_EVENT_FILE_WATCH_ERROR;
|
||||
path_tmp = NULL;
|
||||
SendFileWatchEvent(SDL_EVENT_FILE_WATCH_ERROR, NULL);
|
||||
} else if (buf.event.len != 0) {
|
||||
(void)SDL_snprintf(path, SDL_arraysize(path), "%s/%s", watched_path, buf.event.name);
|
||||
event_type = SDL_EVENT_FILE_CHANGED;
|
||||
path_tmp = SDL_CreateTemporaryString(path);
|
||||
post_event = (path_tmp != NULL);
|
||||
(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);
|
||||
} else {
|
||||
event_type = SDL_EVENT_FILE_CHANGED;
|
||||
path_tmp = SDL_CreateTemporaryString(watched_path);
|
||||
post_event = (path_tmp != NULL);
|
||||
}
|
||||
if (post_event && SDL_EventEnabled(event_type)) {
|
||||
SDL_Event event;
|
||||
SDL_zero(event);
|
||||
event.type = event_type;
|
||||
event.common.timestamp = 0;
|
||||
event.file_watch.path = path_tmp;
|
||||
SDL_PushEvent(&event);
|
||||
if (watch_entry->callback) {
|
||||
watch_entry->callback(watch_entry->user_data, watch_entry->path);
|
||||
}
|
||||
SendFileWatchEvent(SDL_EVENT_FILE_CHANGED, watch_entry->path);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -555,13 +586,16 @@ void SDL_SYS_UpdateFileWatch(void)
|
|||
}
|
||||
SDL_UnlockMutex(file_watch_lock);
|
||||
}
|
||||
#endif // HAVE_INOTIFY
|
||||
return 0;
|
||||
}
|
||||
#endif // HAVE_INOTIFY
|
||||
|
||||
void SDL_SYS_QuitFileWatch(void)
|
||||
{
|
||||
#ifdef HAVE_INOTIFY
|
||||
if (inotify_fd >= 0) {
|
||||
SDL_SetAtomicInt(&quit_watch_file, 0);
|
||||
SDL_WaitThread(file_watch_thread, NULL);
|
||||
close(inotify_fd);
|
||||
inotify_fd = -1;
|
||||
SDL_DestroyMutex(file_watch_lock);
|
||||
|
|
|
|||
|
|
@ -231,15 +231,11 @@ bool SDL_SYS_GetPathInfo(const char *path, SDL_PathInfo *info)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool SDL_SYS_WatchFileForChanges(const char *path)
|
||||
bool SDL_SYS_WatchFileForChanges(const char *path, SDL_FileWatchCallback cb, void *userdata)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
void SDL_SYS_UpdateFileWatch(void)
|
||||
{
|
||||
}
|
||||
|
||||
void SDL_SYS_QuitFileWatch(void)
|
||||
{
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue