First integration of dmon for directory monitoring
This commit is contained in:
parent
18189e63b6
commit
1ba385eb5e
|
@ -99,6 +99,18 @@ local function compare_file(a, b)
|
||||||
return a.filename < b.filename
|
return a.filename < b.filename
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- compute a file's info entry completed with "filename" to be used
|
||||||
|
-- in project scan or falsy if it shouldn't appear in the list.
|
||||||
|
local function get_project_file_info(root, file, size_limit)
|
||||||
|
local info = system.get_file_info(root .. file)
|
||||||
|
if info then
|
||||||
|
info.filename = strip_leading_path(file)
|
||||||
|
end
|
||||||
|
return info and info.size < size_limit and info
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
-- "root" will by an absolute path without trailing '/'
|
-- "root" will by an absolute path without trailing '/'
|
||||||
-- "path" will be a path starting with '/' and without trailing '/'
|
-- "path" will be a path starting with '/' and without trailing '/'
|
||||||
-- or the empty string.
|
-- or the empty string.
|
||||||
|
@ -117,10 +129,8 @@ local function get_directory_files(root, path, t, recursive, begin_hook)
|
||||||
local max_entries = config.max_project_files
|
local max_entries = config.max_project_files
|
||||||
for _, file in ipairs(all) do
|
for _, file in ipairs(all) do
|
||||||
if not common.match_pattern(file, config.ignore_files) then
|
if not common.match_pattern(file, config.ignore_files) then
|
||||||
local file = path .. PATHSEP .. file
|
local info = get_project_file_info(root, path .. PATHSEP .. file, size_limit)
|
||||||
local info = system.get_file_info(root .. file)
|
if info then
|
||||||
if info and info.size < size_limit then
|
|
||||||
info.filename = strip_leading_path(file)
|
|
||||||
table.insert(info.type == "dir" and dirs or files, info)
|
table.insert(info.type == "dir" and dirs or files, info)
|
||||||
entries_count = entries_count + 1
|
entries_count = entries_count + 1
|
||||||
if recursive and entries_count > max_entries then return nil, entries_count end
|
if recursive and entries_count > max_entries then return nil, entries_count end
|
||||||
|
@ -276,6 +286,69 @@ function core.project_files_number()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function file_search(files, info)
|
||||||
|
local filename, type = info.filename, info.type
|
||||||
|
local inf, sup = 1, #files
|
||||||
|
while sup - inf > 8 do
|
||||||
|
local curr = math.floor((inf + sup) / 2)
|
||||||
|
if system.path_compare(filename, type, files[curr].filename, files[curr].type) then
|
||||||
|
sup = curr - 1
|
||||||
|
else
|
||||||
|
inf = curr
|
||||||
|
end
|
||||||
|
end
|
||||||
|
repeat
|
||||||
|
if files[inf].filename == filename then
|
||||||
|
return inf, true
|
||||||
|
end
|
||||||
|
inf = inf + 1
|
||||||
|
until inf > sup or system.path_compare(filename, type, files[inf].filename, files[inf].type)
|
||||||
|
return inf, false
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function project_scan_remove_file(watch_id, filepath)
|
||||||
|
local project_dir_entry
|
||||||
|
for i = 1, #core.project_directories do
|
||||||
|
if core.project_directories[i].watch_id == watch_id then
|
||||||
|
project_dir_entry = core.project_directories[i]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not project_dir_entry then return end
|
||||||
|
print("LOOKING for", filepath, " in", project_dir_entry and project_dir_entry.name)
|
||||||
|
local fileinfo = { filename = filepath }
|
||||||
|
for _, filetype in ipairs {"dir", "file"} do
|
||||||
|
fileinfo.type = filetype
|
||||||
|
local index, match = file_search(project_dir_entry.files, fileinfo)
|
||||||
|
if match then
|
||||||
|
print("FOUND", filepath, " at index", index)
|
||||||
|
table.remove(project_dir_entry.files, index)
|
||||||
|
project_dir_entry.is_dirty = true
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function project_scan_add_file(watch_id, filepath)
|
||||||
|
local project_dir_entry
|
||||||
|
for i = 1, #core.project_directories do
|
||||||
|
if core.project_directories[i].watch_id == watch_id then
|
||||||
|
project_dir_entry = core.project_directories[i]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not project_dir_entry then return end
|
||||||
|
local size_limit = config.file_size_limit * 10e5
|
||||||
|
local fileinfo = get_project_file_info(project_dir_entry.name, PATHSEP .. filepath, size_limit)
|
||||||
|
local index, match = file_search(project_dir_entry.files, fileinfo)
|
||||||
|
if not match then
|
||||||
|
table.insert(project_dir_entry.files, index, fileinfo)
|
||||||
|
project_dir_entry.is_dirty = true
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
-- create a directory using mkdir but may need to create the parent
|
-- create a directory using mkdir but may need to create the parent
|
||||||
-- directories as well.
|
-- directories as well.
|
||||||
local function create_user_directory()
|
local function create_user_directory()
|
||||||
|
@ -373,10 +446,13 @@ function core.add_project_directory(path)
|
||||||
-- will be simply the name of the directory, without its path.
|
-- will be simply the name of the directory, without its path.
|
||||||
-- The field item.topdir will identify it as a top level directory.
|
-- The field item.topdir will identify it as a top level directory.
|
||||||
path = common.normalize_path(path)
|
path = common.normalize_path(path)
|
||||||
|
local watch_id = system.watch_dir(path);
|
||||||
table.insert(core.project_directories, {
|
table.insert(core.project_directories, {
|
||||||
name = path,
|
name = path,
|
||||||
item = {filename = common.basename(path), type = "dir", topdir = true},
|
item = {filename = common.basename(path), type = "dir", topdir = true},
|
||||||
files = {}
|
files = {},
|
||||||
|
is_dirty = true,
|
||||||
|
watch_id = watch_id,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -916,6 +992,15 @@ function core.try(fn, ...)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function core.on_dir_change(watch_id, action, filepath)
|
||||||
|
if action == "delete" then
|
||||||
|
project_scan_remove_file(watch_id, filepath)
|
||||||
|
elseif action == "create" then
|
||||||
|
project_scan_add_file(watch_id, filepath)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
function core.on_event(type, ...)
|
function core.on_event(type, ...)
|
||||||
local did_keymap = false
|
local did_keymap = false
|
||||||
if type == "textinput" then
|
if type == "textinput" then
|
||||||
|
@ -951,6 +1036,9 @@ function core.on_event(type, ...)
|
||||||
end
|
end
|
||||||
elseif type == "focuslost" then
|
elseif type == "focuslost" then
|
||||||
core.root_view:on_focus_lost(...)
|
core.root_view:on_focus_lost(...)
|
||||||
|
elseif type == "dirchange" then
|
||||||
|
print("DEBUG: dirchange", select(1, ...), select(2, ...), select(3, ...))
|
||||||
|
core.on_dir_change(...)
|
||||||
elseif type == "quit" then
|
elseif type == "quit" then
|
||||||
core.quit()
|
core.quit()
|
||||||
end
|
end
|
||||||
|
|
|
@ -111,11 +111,12 @@ function TreeView:check_cache()
|
||||||
if not last_files then
|
if not last_files then
|
||||||
self.last[dir.name] = dir.files
|
self.last[dir.name] = dir.files
|
||||||
else
|
else
|
||||||
if dir.files ~= last_files then
|
if dir.is_dirty or dir.files ~= last_files then
|
||||||
self:invalidate_cache(dir.name)
|
self:invalidate_cache(dir.name)
|
||||||
self.last[dir.name] = dir.files
|
self.last[dir.name] = dir.files
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
dir.is_dirty = false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,33 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
|
|
||||||
|
## septag/dmon
|
||||||
|
|
||||||
|
Copyright 2019 Sepehr Taghdisian. All rights reserved.
|
||||||
|
|
||||||
|
https://github.com/septag/dmon
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER "AS IS" AND ANY EXPRESS OR
|
||||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||||
|
EVENT SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||||
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||||
|
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
## Fira Sans
|
## Fira Sans
|
||||||
|
|
||||||
Digitized data copyright (c) 2012-2015, The Mozilla Foundation and Telefonica S.A.
|
Digitized data copyright (c) 2012-2015, The Mozilla Foundation and Telefonica S.A.
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include "api.h"
|
#include "api.h"
|
||||||
|
#include "dirmonitor.h"
|
||||||
#include "rencache.h"
|
#include "rencache.h"
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <direct.h>
|
#include <direct.h>
|
||||||
|
@ -222,6 +223,14 @@ top:
|
||||||
lua_pushnumber(L, e.wheel.y);
|
lua_pushnumber(L, e.wheel.y);
|
||||||
return 2;
|
return 2;
|
||||||
|
|
||||||
|
case SDL_USEREVENT:
|
||||||
|
lua_pushstring(L, "dirchange");
|
||||||
|
lua_pushnumber(L, e.user.code >> 16);
|
||||||
|
lua_pushstring(L, (e.user.code & 0xffff) == DMON_ACTION_DELETE ? "delete" : "create");
|
||||||
|
lua_pushstring(L, e.user.data1);
|
||||||
|
free(e.user.data1);
|
||||||
|
return 4;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
goto top;
|
goto top;
|
||||||
}
|
}
|
||||||
|
@ -637,6 +646,56 @@ static int f_set_window_opacity(lua_State *L) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void watch_callback(dmon_watch_id watch_id, dmon_action action, const char* rootdir,
|
||||||
|
const char* filepath, const char* oldfilepath, void* user)
|
||||||
|
{
|
||||||
|
(void)(user);
|
||||||
|
(void)(rootdir);
|
||||||
|
dirmonitor_push_event(watch_id, action, filepath, oldfilepath);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int f_watch_dir(lua_State *L) {
|
||||||
|
const char *path = luaL_checkstring(L, 1);
|
||||||
|
fprintf(stderr, "DEBUG: watching dir: %s\n", path); fflush(stderr);
|
||||||
|
dmon_watch_id watch_id = dmon_watch(path, watch_callback, DMON_WATCHFLAGS_RECURSIVE, NULL);
|
||||||
|
lua_pushnumber(L, watch_id.id);
|
||||||
|
// FIXME: we ignore if there is an error.
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define PATHSEP '\\'
|
||||||
|
#else
|
||||||
|
#define PATHSEP '/'
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
static int f_path_compare(lua_State *L) {
|
||||||
|
const char *path1 = luaL_checkstring(L, 1);
|
||||||
|
const char *type1_s = luaL_checkstring(L, 2);
|
||||||
|
const char *path2 = luaL_checkstring(L, 3);
|
||||||
|
const char *type2_s = luaL_checkstring(L, 4);
|
||||||
|
const int len1 = strlen(path1), len2 = strlen(path2);
|
||||||
|
int type1 = strcmp(type1_s, "dir") != 0;
|
||||||
|
int type2 = strcmp(type2_s, "dir") != 0;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < len1 && i < len2; i++) {
|
||||||
|
if (path1[i] != path2[i]) break;
|
||||||
|
}
|
||||||
|
if (strchr(path1 + i, PATHSEP)) {
|
||||||
|
type1 = 0;
|
||||||
|
}
|
||||||
|
if (strchr(path2 + i, PATHSEP)) {
|
||||||
|
type2 = 0;
|
||||||
|
}
|
||||||
|
if (type1 != type2) {
|
||||||
|
lua_pushboolean(L, type1 < type2);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
lua_pushboolean(L, strcmp(path1 + i, path2 + i) < 0);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static const luaL_Reg lib[] = {
|
static const luaL_Reg lib[] = {
|
||||||
{ "poll_event", f_poll_event },
|
{ "poll_event", f_poll_event },
|
||||||
|
@ -664,6 +723,8 @@ static const luaL_Reg lib[] = {
|
||||||
{ "exec", f_exec },
|
{ "exec", f_exec },
|
||||||
{ "fuzzy_match", f_fuzzy_match },
|
{ "fuzzy_match", f_fuzzy_match },
|
||||||
{ "set_window_opacity", f_set_window_opacity },
|
{ "set_window_opacity", f_set_window_opacity },
|
||||||
|
{ "watch_dir", f_watch_dir },
|
||||||
|
{ "path_compare", f_path_compare },
|
||||||
{ NULL, NULL }
|
{ NULL, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <SDL.h>
|
||||||
|
|
||||||
|
#define DMON_IMPL
|
||||||
|
#include "dmon.h"
|
||||||
|
|
||||||
|
#include "dirmonitor.h"
|
||||||
|
|
||||||
|
static void send_sdl_event(dmon_watch_id watch_id, dmon_action action, const char *filepath) {
|
||||||
|
SDL_Event ev;
|
||||||
|
const int size = strlen(filepath) + 1;
|
||||||
|
char *new_filepath = malloc(size);
|
||||||
|
if (!new_filepath) return;
|
||||||
|
memcpy(new_filepath, filepath, size);
|
||||||
|
#ifdef _WIN32
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
if (new_filepath[i] == '/') {
|
||||||
|
new_filepath[i] = '\\';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
SDL_zero(ev);
|
||||||
|
ev.type = SDL_USEREVENT;
|
||||||
|
fprintf(stderr, "DEBUG: send watch_id: %d action; %d\n", watch_id.id, action); fflush(stderr);
|
||||||
|
ev.user.code = ((watch_id.id & 0xffff) << 16) | (action & 0xffff);
|
||||||
|
ev.user.data1 = new_filepath;
|
||||||
|
SDL_PushEvent(&ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dirmonitor_init() {
|
||||||
|
dmon_init();
|
||||||
|
/* FIXME: not needed ? */
|
||||||
|
/* sdl_dmon_event_type = SDL_RegisterEvents(1); */
|
||||||
|
}
|
||||||
|
|
||||||
|
void dirmonitor_deinit() {
|
||||||
|
dmon_deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void dirmonitor_push_event(dmon_watch_id watch_id, dmon_action action, const char *filepath,
|
||||||
|
const char *oldfilepath)
|
||||||
|
{
|
||||||
|
switch (action) {
|
||||||
|
case DMON_ACTION_MOVE:
|
||||||
|
send_sdl_event(watch_id, DMON_ACTION_DELETE, oldfilepath);
|
||||||
|
send_sdl_event(watch_id, DMON_ACTION_CREATE, filepath);
|
||||||
|
break;
|
||||||
|
case DMON_ACTION_MODIFY:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
send_sdl_event(watch_id, action, filepath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
#ifndef DIRMONITOR_H
|
||||||
|
#define DIRMONITOR_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "dmon.h"
|
||||||
|
|
||||||
|
void dirmonitor_init();
|
||||||
|
void dirmonitor_deinit();
|
||||||
|
void dirmonitor_push_event(dmon_watch_id watch_id, dmon_action action, const char *filepath,
|
||||||
|
const char *oldfilepath);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -14,6 +14,8 @@
|
||||||
#include <mach-o/dyld.h>
|
#include <mach-o/dyld.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "dirmonitor.h"
|
||||||
|
|
||||||
|
|
||||||
SDL_Window *window;
|
SDL_Window *window;
|
||||||
|
|
||||||
|
@ -108,6 +110,8 @@ int main(int argc, char **argv) {
|
||||||
SDL_DisplayMode dm;
|
SDL_DisplayMode dm;
|
||||||
SDL_GetCurrentDisplayMode(0, &dm);
|
SDL_GetCurrentDisplayMode(0, &dm);
|
||||||
|
|
||||||
|
dirmonitor_init();
|
||||||
|
|
||||||
window = SDL_CreateWindow(
|
window = SDL_CreateWindow(
|
||||||
"", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, dm.w * 0.8, dm.h * 0.8,
|
"", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, dm.w * 0.8, dm.h * 0.8,
|
||||||
SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN);
|
SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN);
|
||||||
|
@ -191,6 +195,7 @@ init_lua:
|
||||||
|
|
||||||
lua_close(L);
|
lua_close(L);
|
||||||
ren_free_window_resources();
|
ren_free_window_resources();
|
||||||
|
dirmonitor_deinit();
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ lite_sources = [
|
||||||
'api/regex.c',
|
'api/regex.c',
|
||||||
'api/system.c',
|
'api/system.c',
|
||||||
'api/process.c',
|
'api/process.c',
|
||||||
|
'dirmonitor.c',
|
||||||
'renderer.c',
|
'renderer.c',
|
||||||
'renwindow.c',
|
'renwindow.c',
|
||||||
'fontdesc.c',
|
'fontdesc.c',
|
||||||
|
|
Loading…
Reference in New Issue