2626/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2727// System dependent filesystem routines
2828
29+ #include <SDL3/SDL_atomic.h>
30+ #include <SDL3/SDL_thread.h>
2931#include "../SDL_sysfilesystem.h"
3032#include "../../SDL_hashtable.h"
3133#include "../../events/SDL_events_c.h"
@@ -419,8 +421,18 @@ bool SDL_SYS_GetPathInfo(const char *path, SDL_PathInfo *info)
419421
420422#ifdef HAVE_INOTIFY
421423static int inotify_fd = -1 ;
422- static SDL_HashTable * watch_descriptor_table = NULL ; // stores directory or file path for a watch descriptor
424+ typedef struct WatchEntry
425+ {
426+ SDL_FileWatchCallback callback ;
427+ void * user_data ;
428+ char path []; // directory or file path
429+ } WatchEntry ;
430+ static SDL_HashTable * watch_descriptor_table = NULL ; // stores WatchEntry for a watch descriptor
431+
432+ static int SDL_FileWatchThread (void * user_data );
433+ static SDL_Thread * file_watch_thread = NULL ;
423434static SDL_Mutex * file_watch_lock = NULL ;
435+ static SDL_AtomicInt quit_watch_file ;
424436
425437#ifdef HAVE_INOTIFY_INIT1
426438static int SDL_inotify_init1 (void )
@@ -441,7 +453,7 @@ static int SDL_inotify_init1(void)
441453#endif // HAVE_INOTIFY_INIT1
442454#endif // HAVE_INOTIFY
443455
444- bool SDL_SYS_WatchFileForChanges (const char * path )
456+ bool SDL_SYS_WatchFileForChanges (const char * path , SDL_FileWatchCallback cb , void * user_data )
445457{
446458#ifdef HAVE_INOTIFY
447459 if (!watch_descriptor_table ) {
@@ -463,29 +475,41 @@ bool SDL_SYS_WatchFileForChanges(const char *path)
463475 inotify_fd = -1 ;
464476 return false;
465477 }
478+ file_watch_thread = SDL_CreateThread (SDL_FileWatchThread , "SDL_FileWatch" , NULL );
479+ SDL_SetAtomicInt (& quit_watch_file , 0 );
480+ if (!file_watch_thread ) {
481+ SDL_DestroyHashTable (watch_descriptor_table );
482+ watch_descriptor_table = NULL ;
483+ close (inotify_fd );
484+ inotify_fd = -1 ;
485+ SDL_DestroyMutex (file_watch_lock );
486+ file_watch_lock = NULL ;
487+ return false;
488+ }
466489 }
467-
468- char * p = SDL_strdup ( path );
469- if (!p ) {
490+ const size_t slen = SDL_strlen ( path );
491+ WatchEntry * watch_entry = SDL_malloc ( sizeof ( * watch_entry ) + slen + 1 );
492+ if (!watch_entry ) {
470493 return false;
471494 }
495+ watch_entry -> callback = cb ;
496+ watch_entry -> user_data = user_data ;
497+ SDL_memcpy (watch_entry -> path , path , slen + 1 );
472498 // remove separator at the end of the path
473- const size_t slen = SDL_strlen (p );
474- if (p [slen - 1 ] == '/' ) {
475- p [slen - 1 ] = '\0' ;
499+ if (watch_entry -> path [slen - 1 ] == '/' ) {
500+ watch_entry -> path [slen - 1 ] = '\0' ;
476501 }
477-
478502 SDL_LockMutex (file_watch_lock );
479- int wd = inotify_add_watch (inotify_fd , p , IN_MODIFY );
503+ int wd = inotify_add_watch (inotify_fd , path , IN_MODIFY );
480504 if (wd == -1 ) {
481505 SDL_UnlockMutex (file_watch_lock );
482- SDL_free (p );
506+ SDL_free (watch_entry );
483507 return SDL_SetError ("inotify_add_watch failed: %s" , strerror (errno ));
484508 }
485- if (!SDL_InsertIntoHashTable (watch_descriptor_table , (void * )(intptr_t )wd , p , false)) {
509+ if (!SDL_InsertIntoHashTable (watch_descriptor_table , (void * )(intptr_t )wd , watch_entry , false)) {
486510 inotify_rm_watch (inotify_fd , wd );
487511 SDL_UnlockMutex (file_watch_lock );
488- SDL_free (p );
512+ SDL_free (watch_entry );
489513 return false;
490514 }
491515 SDL_UnlockMutex (file_watch_lock );
@@ -495,10 +519,27 @@ bool SDL_SYS_WatchFileForChanges(const char *path)
495519#endif // HAVE_INOTIFY
496520}
497521
498- void SDL_SYS_UpdateFileWatch (void )
499- {
500522#ifdef HAVE_INOTIFY
501- if (inotify_fd >= 0 ) {
523+ static void SendFileWatchEvent (SDL_EventType event_type , const char * path ) {
524+ if (SDL_EventEnabled (event_type )) {
525+ SDL_Event event ;
526+ SDL_zero (event );
527+ event .type = event_type ;
528+ event .common .timestamp = 0 ;
529+ if (path ) {
530+ event .file_watch .path = SDL_CreateTemporaryString (path );
531+ if (!event .file_watch .path ) {
532+ return ;
533+ }
534+ }
535+ SDL_PushEvent (& event );
536+ }
537+ }
538+
539+ static int SDL_FileWatchThread (void * userdata )
540+ {
541+ while (SDL_GetAtomicInt (& quit_watch_file ) == 0 ) {
542+ SDL_Delay (100 );
502543 SDL_LockMutex (file_watch_lock );
503544 union
504545 {
@@ -518,31 +559,21 @@ void SDL_SYS_UpdateFileWatch(void)
518559 }
519560
520561 while (remain > 0 ) {
521- const char * watched_path ;
522- if (SDL_FindInHashTable (watch_descriptor_table , (void * )(intptr_t )buf .event .wd , (const void * * )& watched_path )) {
523- const char * path_tmp ;
524- SDL_EventType event_type ;
525- bool post_event = true;
562+ const WatchEntry * watch_entry ;
563+ if (SDL_FindInHashTable (watch_descriptor_table , (void * )(intptr_t )buf .event .wd , (const void * * )& watch_entry )) {
526564 if (buf .event .mask & IN_Q_OVERFLOW ) {
527- event_type = SDL_EVENT_FILE_WATCH_ERROR ;
528- path_tmp = NULL ;
565+ SendFileWatchEvent (SDL_EVENT_FILE_WATCH_ERROR , NULL );
529566 } else if (buf .event .len != 0 ) {
530- (void )SDL_snprintf (path , SDL_arraysize (path ), "%s/%s" , watched_path , buf .event .name );
531- event_type = SDL_EVENT_FILE_CHANGED ;
532- path_tmp = SDL_CreateTemporaryString (path );
533- post_event = (path_tmp != NULL );
567+ (void )SDL_snprintf (path , SDL_arraysize (path ), "%s/%s" , watch_entry -> path , buf .event .name );
568+ if (watch_entry -> callback ) {
569+ watch_entry -> callback (watch_entry -> user_data , path );
570+ }
571+ SendFileWatchEvent (SDL_EVENT_FILE_CHANGED , path );
534572 } else {
535- event_type = SDL_EVENT_FILE_CHANGED ;
536- path_tmp = SDL_CreateTemporaryString (watched_path );
537- post_event = (path_tmp != NULL );
538- }
539- if (post_event && SDL_EventEnabled (event_type )) {
540- SDL_Event event ;
541- SDL_zero (event );
542- event .type = event_type ;
543- event .common .timestamp = 0 ;
544- event .file_watch .path = path_tmp ;
545- SDL_PushEvent (& event );
573+ if (watch_entry -> callback ) {
574+ watch_entry -> callback (watch_entry -> user_data , watch_entry -> path );
575+ }
576+ SendFileWatchEvent (SDL_EVENT_FILE_CHANGED , watch_entry -> path );
546577 }
547578 }
548579
@@ -555,13 +586,16 @@ void SDL_SYS_UpdateFileWatch(void)
555586 }
556587 SDL_UnlockMutex (file_watch_lock );
557588 }
558- #endif // HAVE_INOTIFY
589+ return 0 ;
559590}
591+ #endif // HAVE_INOTIFY
560592
561593void SDL_SYS_QuitFileWatch (void )
562594{
563595#ifdef HAVE_INOTIFY
564596 if (inotify_fd >= 0 ) {
597+ SDL_SetAtomicInt (& quit_watch_file , 0 );
598+ SDL_WaitThread (file_watch_thread , NULL );
565599 close (inotify_fd );
566600 inotify_fd = -1 ;
567601 SDL_DestroyMutex (file_watch_lock );
0 commit comments