Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add a directory watcher to file watcher #27

Open
wants to merge 1 commit into
base: wip
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions apps/tests/test_file_watch/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
cmake_minimum_required(VERSION 3.7.2)
set (CMAKE_CXX_STANDARD 17)

set (PROJECT_NAME "Island-TestFileWatch")

# Set global property (all targets are impacted)
# set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CMAKE_COMMAND} -E time")
# set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK "${CMAKE_COMMAND} -E time")

project (${PROJECT_NAME})

# set to number of worker threads if you wish to use multi-threaded rendering
# add_compile_definitions( LE_MT=4 )

# Vulkan Validation layers are enabled by default for Debug builds.
# Uncomment the next line to disable loading Vulkan Validation Layers for Debug builds.
# add_compile_definitions( SHOULD_USE_VALIDATION_LAYERS=false )

# Point this to the base directory of your Island installation
set (ISLAND_BASE_DIR "${PROJECT_SOURCE_DIR}/../../..")

# Select which standard Island modules to use
set(REQUIRES_ISLAND_LOADER ON )
set(REQUIRES_ISLAND_CORE ON )

# Loads Island framework, based on selected Island modules from above
include ("${ISLAND_BASE_DIR}/CMakeLists.txt.island_prolog.in")

# Main application c++ file. Not much to see there,
set (SOURCES main.cpp)

# Add application module, and (optional) any other private
# island modules which should not be part of the shared framework.
add_subdirectory (test_file_watch_app)

# Specify any optional modules from the standard framework here
add_island_module(le_log)

# Sets up Island framework linkage and housekeeping, based on user selections
include ("${ISLAND_BASE_DIR}/CMakeLists.txt.island_epilog.in")

# create a link to local resources
link_resources(${PROJECT_SOURCE_DIR}/resources ${CMAKE_BINARY_DIR}/local_resources)

source_group(${PROJECT_NAME} FILES ${SOURCES})
33 changes: 33 additions & 0 deletions apps/tests/test_file_watch/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#include "test_file_watch_app/test_file_watch_app.h"

// ----------------------------------------------------------------------

int main( int argc, char const *argv[] ) {

TestFileWatchApp::initialize();

{
// We instantiate TestFileWatchApp in its own scope - so that
// it will be destroyed before TestFileWatchApp::terminate
// is called.

TestFileWatchApp TestFileWatchApp{};

for ( ;; ) {

#ifdef PLUGINS_DYNAMIC
le_core_poll_for_module_reloads();
#endif
auto result = TestFileWatchApp.update();

if ( !result ) {
break;
}
}
}

// Must only be called once last TestFileWatchApp is destroyed
TestFileWatchApp::terminate();

return 0;
}
1 change: 1 addition & 0 deletions apps/tests/test_file_watch/resources/file.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
change this file to exit test app
27 changes: 27 additions & 0 deletions apps/tests/test_file_watch/test_file_watch_app/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
set (TARGET test_file_watch_app)

set (SOURCES "test_file_watch_app.cpp")
set (SOURCES ${SOURCES} "test_file_watch_app.h")

if (${PLUGINS_DYNAMIC})

add_library(${TARGET} SHARED ${SOURCES})


add_dynamic_linker_flags()

target_compile_definitions(${TARGET} PUBLIC "PLUGINS_DYNAMIC")

else()

# Adding a static library means to also add a linker dependency for our target
# to the library.
set (STATIC_LIBS ${STATIC_LIBS} ${TARGET} PARENT_SCOPE)

add_library(${TARGET} STATIC ${SOURCES})

endif()

target_link_libraries(${TARGET} PUBLIC ${LINKER_FLAGS})

source_group(${TARGET} FILES ${SOURCES})
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#include "test_file_watch_app.h"

#include "le_file_watcher/le_file_watcher.h"
#include "le_log/le_log.h"

struct test_file_watch_app_o {
le::FileWatcher file_watcher;
LeLog log{};
bool quit = false;
};

typedef test_file_watch_app_o app_o;

// ----------------------------------------------------------------------

static void app_initialize(){};

// ----------------------------------------------------------------------

static void app_terminate(){};

// ----------------------------------------------------------------------

static bool file_callback( const char *path, void *user_data ) {
auto app = static_cast<test_file_watch_app_o *>( user_data );
app->log.info( "File modified '%s', will exit now", path );
app->quit = true;
return true;
}

static bool directory_callback( le::FileWatcher::Event event, const char *path, void *user_data ) {
auto app = static_cast<test_file_watch_app_o *>( user_data );
const char *action = nullptr;
switch ( event ) {
case le_file_watcher_api::Event::FILE_CREATED:
action = "file created";
break;
case le_file_watcher_api::Event::FILE_DELETED:
action = "file deleted";
break;
case le_file_watcher_api::Event::FILE_MODIFIED:
action = "file modified";
break;
case le_file_watcher_api::Event::FILE_MOVED:
action = "file moved";
break;
case le_file_watcher_api::Event::DIRECTORY_CREATED:
action = "folder created";
break;
case le_file_watcher_api::Event::DIRECTORY_DELETED:
action = "folder deleted";
break;
case le_file_watcher_api::Event::DIRECTORY_MOVED:
action = "folder moved";
break;
}
app->log.info( "%s %s", action, path );
return true;
}

// ----------------------------------------------------------------------

static test_file_watch_app_o *test_file_watch_app_create() {
auto app = new ( test_file_watch_app_o );
app->log.info( "App Created" );
app->file_watcher.watch_file( "./local_resources/file.txt", file_callback, app );
app->file_watcher.watch_directory( "./local_resources", directory_callback, app );
return app;
}

// ----------------------------------------------------------------------

static bool test_file_watch_app_update( test_file_watch_app_o *self ) {
self->file_watcher.poll();
return !self->quit; // keep app alive
}

// ----------------------------------------------------------------------

static void test_file_watch_app_destroy( test_file_watch_app_o *self ) {
delete ( self ); // deletes camera
}

// ----------------------------------------------------------------------

LE_MODULE_REGISTER_IMPL( test_file_watch_app, api ) {

auto test_file_watch_app_api_i = static_cast<test_file_watch_app_api *>( api );
auto &test_file_watch_app_i = test_file_watch_app_api_i->test_file_watch_app_i;

test_file_watch_app_i.initialize = app_initialize;
test_file_watch_app_i.terminate = app_terminate;

test_file_watch_app_i.create = test_file_watch_app_create;
test_file_watch_app_i.destroy = test_file_watch_app_destroy;
test_file_watch_app_i.update = test_file_watch_app_update;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#ifndef GUARD_test_file_watch_app_H
#define GUARD_test_file_watch_app_H
#endif

#include "le_core/le_core.h"

// depends on le_backend_vk. le_backend_vk must be loaded before this class is used.

struct test_file_watch_app_o;

// clang-format off
struct test_file_watch_app_api {

struct test_file_watch_app_interface_t {
test_file_watch_app_o * ( *create )();
void ( *destroy )( test_file_watch_app_o *self );
bool ( *update )( test_file_watch_app_o *self );
void ( *initialize )(); // static methods
void ( *terminate )(); // static methods
};

test_file_watch_app_interface_t test_file_watch_app_i;
};
// clang-format on

LE_MODULE( test_file_watch_app );
LE_MODULE_LOAD_DEFAULT( test_file_watch_app );

#ifdef __cplusplus

namespace test_file_watch_app {
static const auto &api = test_file_watch_app_api_i;
static const auto &test_file_watch_app_i = api -> test_file_watch_app_i;
} // namespace test_file_watch_app

class TestFileWatchApp : NoCopy, NoMove {

test_file_watch_app_o *self;

public:
TestFileWatchApp()
: self( test_file_watch_app::test_file_watch_app_i.create() ) {
}

bool update() {
return test_file_watch_app::test_file_watch_app_i.update( self );
}

~TestFileWatchApp() {
test_file_watch_app::test_file_watch_app_i.destroy( self );
}

static void initialize() {
test_file_watch_app::test_file_watch_app_i.initialize();
}

static void terminate() {
test_file_watch_app::test_file_watch_app_i.terminate();
}
};

#endif
76 changes: 67 additions & 9 deletions modules/le_file_watcher/le_file_watcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,39 @@

struct le_file_watcher_o;

struct le_file_watcher_watch_settings {
const char *filePath = nullptr;
bool ( *callback_fun )( const char *file_path, void *user_data ) = nullptr;
void *callback_user_data = nullptr;
};

// clang-format off
struct le_file_watcher_api {

enum class Event : int {
FILE_CREATED = 0,
FILE_DELETED = 1,
FILE_MODIFIED = 2,
FILE_MOVED = 3,
DIRECTORY_CREATED = 4,
DIRECTORY_DELETED = 5,
DIRECTORY_MOVED = 6
};

struct directory_settings {
const char *path = nullptr;
bool ( *callback_fun )( Event event, const char *file_path, void *user_data ) = nullptr;
void *callback_user_data = nullptr;
};

struct file_settings {
const char *filePath = nullptr;
bool ( *callback_fun )( const char *file_path, void *user_data ) = nullptr;
void *callback_user_data = nullptr;
};

struct le_file_watcher_interface_t{
le_file_watcher_o *( *create )();
le_file_watcher_o *( *create )();
void ( *destroy )( le_file_watcher_o *self );

// returns unique id for the watch, -1 if unsuccessful.
int ( *add_watch )( le_file_watcher_o *self, le_file_watcher_watch_settings const *settings );

int ( *add_watch )( le_file_watcher_o *self, file_settings const *settings );
int ( *add_watch_directory )( le_file_watcher_o *self, directory_settings const *settings );

bool ( *remove_watch )( le_file_watcher_o *self, int watch_id );
void ( *poll_notifications )( le_file_watcher_o *self);
};
Expand All @@ -36,6 +53,9 @@ struct le_file_watcher_api {
};
// clang-format on

using le_file_watcher_watch_settings = le_file_watcher_api::file_settings;
using le_directory_watcher_watch_settings = le_file_watcher_api::directory_settings;

LE_MODULE( le_file_watcher );

// File watcher can only be loaded as a static module - it will always
Expand All @@ -49,6 +69,44 @@ namespace le_file_watcher {
static const auto &api = le_file_watcher_api_i;
static const auto &le_file_watcher_i = api -> le_file_watcher_i;
} // namespace le_file_watcher

namespace le {

class FileWatcher : NoCopy, NoMove {
le_file_watcher_o *watcher;

public:
using Event = le_file_watcher_api::Event;

FileWatcher()
: watcher( le_file_watcher::le_file_watcher_i.create() ) {
}

~FileWatcher() {
le_file_watcher::le_file_watcher_i.destroy( watcher );
}

int watch_file( const char *file_path, bool ( *callback_fun )( const char *file_path, void *user_data ), void *callback_userdata ) {
le_file_watcher_api::file_settings settings{ file_path, callback_fun, callback_userdata };
return le_file_watcher::le_file_watcher_i.add_watch( watcher, &settings );
}

int watch_directory( const char *file_path, bool ( *callback_fun )( Event event, const char *file_path, void *user_data ), void *callback_userdata ) {
le_file_watcher_api::directory_settings settings{ file_path, callback_fun, callback_userdata };
return le_file_watcher::le_file_watcher_i.add_watch_directory( watcher, &settings );
}

bool remove_watch( int watch_id ) {
return le_file_watcher::le_file_watcher_i.remove_watch( watcher, watch_id );
}

void poll() {
return le_file_watcher::le_file_watcher_i.poll_notifications( watcher );
}
};

} // namespace le

#endif // !__cplusplus

#endif // GUARD_FILE_SYSTEM_H
Loading