add linux file watcher and test runner script
This commit is contained in:
parent
475c1cf8d6
commit
8dc6ca9a09
295
platform.d
295
platform.d
@ -1,14 +1,15 @@
|
||||
module dlib.platform;
|
||||
|
||||
import dlib.aliases;
|
||||
import dlib.alloc;
|
||||
import dlib.util;
|
||||
|
||||
import includes;
|
||||
import std.stdio;
|
||||
import core.memory;
|
||||
import core.thread.osthread;
|
||||
import core.time;
|
||||
|
||||
@nogc:
|
||||
|
||||
const WINDOW_EDGE_BUFFER = 50;
|
||||
|
||||
enum Input
|
||||
@ -45,6 +46,11 @@ version(linux)
|
||||
{
|
||||
import core.sys.posix.dlfcn;
|
||||
import core.sys.posix.sys.mman;
|
||||
import core.sys.linux.sys.inotify;
|
||||
import core.sys.linux.fcntl;
|
||||
import core.sys.posix.unistd;
|
||||
|
||||
import core.stdc.string : strlen;
|
||||
|
||||
struct InputEvent
|
||||
{
|
||||
@ -616,6 +622,291 @@ MemFree(void* ptr, u64 size)
|
||||
assert(munmap(ptr, size) == 0, "MemFree failure");
|
||||
}
|
||||
|
||||
struct Watcher
|
||||
{
|
||||
Arena arena;
|
||||
u8[] buffer;
|
||||
WatcherH handle;
|
||||
WatchH dir_handle;
|
||||
u8[] watched_dir;
|
||||
bool blocking;
|
||||
}
|
||||
|
||||
alias WatcherH = int;
|
||||
alias WatchH = int;
|
||||
|
||||
enum WatchType
|
||||
{
|
||||
None,
|
||||
Access = IN_ACCESS,
|
||||
Metadata = IN_ATTRIB,
|
||||
Create = IN_CREATE,
|
||||
Delete = IN_DELETE,
|
||||
Modify = IN_MODIFY,
|
||||
Moved = IN_MOVED_FROM | IN_MOVED_TO,
|
||||
}
|
||||
|
||||
alias WT = WatchType;
|
||||
|
||||
Watcher
|
||||
WatchDirectory(string dir, WatchType type, bool blocking = false)
|
||||
{
|
||||
assert(dir.length > 0);
|
||||
|
||||
Watcher watcher = {
|
||||
arena: CreateArena(MB(4)),
|
||||
buffer: AllocArray!(u8)(MB(1)),
|
||||
blocking: blocking,
|
||||
watched_dir: (cast(u8*)dir.ptr)[0 .. dir.length],
|
||||
};
|
||||
|
||||
watcher.handle = inotify_init();
|
||||
assert(watcher.dir_handle >= 0, "WatchDirectory failure: unable to initialize");
|
||||
|
||||
if (!blocking)
|
||||
{
|
||||
fcntl(watcher.handle, F_SETFL, fcntl(watcher.handle, F_GETFL) | O_NONBLOCK);
|
||||
}
|
||||
|
||||
watcher.dir_handle = inotify_add_watch(watcher.handle, dir.ptr, IN_CREATE|IN_DELETE|IN_DELETE_SELF|IN_MODIFY|IN_MOVE_SELF|IN_MOVED_FROM|IN_MOVED_TO|IN_ATTRIB|IN_EXCL_UNLINK);
|
||||
|
||||
return watcher;
|
||||
}
|
||||
|
||||
WatchEvent[]
|
||||
ViewChanges(Watcher* watcher)
|
||||
{
|
||||
assert(watcher.handle >= 0 && watcher.dir_handle >= 0, "ViewChanges failure: handles are not valid");
|
||||
|
||||
Reset(&watcher.arena);
|
||||
|
||||
WatchEvent[] events;
|
||||
|
||||
i64 length = read(watcher.handle, watcher.buffer.ptr, watcher.buffer.length);
|
||||
if (length > 0)
|
||||
{
|
||||
i64 count = 0;
|
||||
i64 i = 0;
|
||||
while(i < length)
|
||||
{
|
||||
inotify_event* event = (cast(inotify_event*)(watcher.buffer.ptr + i));
|
||||
count += 1;
|
||||
|
||||
assert(event.wd == watcher.dir_handle);
|
||||
|
||||
i += inotify_event.sizeof + event.len;
|
||||
}
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
struct Moved
|
||||
{
|
||||
u32 cookie;
|
||||
i64 to;
|
||||
i64 from;
|
||||
}
|
||||
|
||||
Moved[] moved = AllocArray!(Moved)(&watcher.arena, (count/2)+1);
|
||||
i64 m_count = 0;
|
||||
events = AllocArray!(WatchEvent)(&watcher.arena, count);
|
||||
count = 0;
|
||||
i = 0;
|
||||
while (i < length)
|
||||
{
|
||||
inotify_event* event = (cast(inotify_event*)(watcher.buffer.ptr + i));
|
||||
if (event.len > 0)
|
||||
{
|
||||
u8[] file_name = (cast(u8*)event.name)[0 .. strlen(event.name.ptr)];
|
||||
|
||||
if (event.mask & IN_MOVED_FROM || event.mask & IN_MOVED_TO)
|
||||
{
|
||||
bool from = (event.mask & IN_MOVED_FROM) > 0;
|
||||
|
||||
Moved* m;
|
||||
foreach(j; 0 .. m_count)
|
||||
{
|
||||
if (moved[j].cookie == event.cookie)
|
||||
{
|
||||
m = moved.ptr + j;
|
||||
}
|
||||
}
|
||||
|
||||
if (m != null)
|
||||
{
|
||||
if (from && m.to >= 0)
|
||||
{
|
||||
events[m.to].names[0] = file_name;
|
||||
|
||||
if (watcher.watched_dir == file_name)
|
||||
{
|
||||
events[m.to].type &= ~(WET.File | WET.Dir);
|
||||
events[m.to].type |= WET.Self;
|
||||
}
|
||||
}
|
||||
else if (!from && m.from >= 0)
|
||||
{
|
||||
events[m.from].names[1] = file_name;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WatchEvent* ev = events.ptr + count;
|
||||
ev.type = (event.mask & IN_ISDIR) ? WET.Dir : WET.File;
|
||||
ev.type |= WET.Moved;
|
||||
|
||||
moved[m_count].cookie = event.cookie;
|
||||
|
||||
if (from)
|
||||
{
|
||||
ev.names[0] = file_name;
|
||||
moved[m_count].from = count;
|
||||
moved[m_count].to = -1;
|
||||
|
||||
if (watcher.watched_dir == file_name)
|
||||
{
|
||||
ev.type &= ~(WET.File | WET.Dir);
|
||||
ev.type |= WET.Self;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ev.names[1] = file_name;
|
||||
moved[m_count].to = count;
|
||||
moved[m_count].from = -1;
|
||||
}
|
||||
|
||||
count += 1;
|
||||
m_count += 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WatchEvent* ev = events.ptr + count;
|
||||
ev.type = (event.mask & IN_ISDIR) ? WET.Dir : WET.File;
|
||||
|
||||
ev.names[0] = file_name;
|
||||
if (ev.names[0] == watcher.watched_dir)
|
||||
{
|
||||
ev.type = WET.Self;
|
||||
}
|
||||
|
||||
SetEventType(ev, event.mask);
|
||||
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WatchEvent* ev = events.ptr + count;
|
||||
ev.type = (event.mask & IN_ISDIR) ? WET.Dir : WET.File;
|
||||
|
||||
SetEventType(ev, event.mask);
|
||||
|
||||
count += 1;
|
||||
}
|
||||
|
||||
i += inotify_event.sizeof + event.len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
void
|
||||
SetEventType(WatchEvent* ev, u32 mask)
|
||||
{
|
||||
if (mask & IN_ACCESS)
|
||||
{
|
||||
ev.type |= WET.Accessed;
|
||||
}
|
||||
else if (mask & IN_ATTRIB)
|
||||
{
|
||||
ev.type |= WET.Metadata;
|
||||
}
|
||||
else if (mask & IN_CREATE)
|
||||
{
|
||||
ev.type |= WET.Created;
|
||||
}
|
||||
else if (mask & IN_MODIFY)
|
||||
{
|
||||
ev.type |= WET.Modified;
|
||||
}
|
||||
else if (mask & IN_DELETE)
|
||||
{
|
||||
ev.type |= WET.Deleted;
|
||||
}
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
import std.stdio;
|
||||
import std.file : Remove = remove;
|
||||
|
||||
Watcher fw = WatchDirectory("./", WT.Create | WT.Delete | WT.Moved | WT.Modify | WT.Access | WT.Metadata | WT.Access | WT.Metadata);
|
||||
|
||||
auto f = File("test_file.txt", "wb");
|
||||
|
||||
f.write("test");
|
||||
f.sync();
|
||||
f.close();
|
||||
|
||||
Remove("./test_file.txt");
|
||||
|
||||
WatchEvent[] events = ViewChanges(&fw);
|
||||
|
||||
assert(events.length == 3);
|
||||
|
||||
assert(events[0].type == WET.FileCreated);
|
||||
assert(events[0].names[0] == r"test_file.txt");
|
||||
|
||||
assert(events[1].type == WET.FileModified);
|
||||
assert(events[1].names[0] == r"test_file.txt");
|
||||
|
||||
assert(events[2].type == WET.FileDeleted);
|
||||
assert(events[2].names[0] == r"test_file.txt");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
enum WatchEventType
|
||||
{
|
||||
None = 0x0000,
|
||||
File = 0x0001,
|
||||
Dir = 0x0002,
|
||||
Self = 0x0004,
|
||||
Created = 0x0008,
|
||||
Modified = 0x0010,
|
||||
Deleted = 0x0020,
|
||||
Moved = 0x0040,
|
||||
Metadata = 0x0080,
|
||||
Accessed = 0x0100,
|
||||
FileCreated = WET.File | WET.Created,
|
||||
FileModified = WET.File | WET.Modified,
|
||||
FileDeleted = WET.File | WET.Deleted,
|
||||
FileMoved = WET.File | WET.Moved,
|
||||
FileMetadata = WET.File | WET.Metadata,
|
||||
FileAccessed = WET.File | WET.Accessed,
|
||||
DirCreated = WET.Dir | WET.Created,
|
||||
DirModified = WET.Dir | WET.Modified,
|
||||
DirDeleted = WET.Dir | WET.Deleted,
|
||||
DirMoved = WET.Dir | WET.Moved,
|
||||
DirMetadata = WET.Dir | WET.Metadata,
|
||||
DirAccessed = WET.Dir | WET.Accessed,
|
||||
SelfCreated = WET.Self | WET.Created,
|
||||
SelfModified = WET.Self | WET.Modified,
|
||||
SelfDeleted = WET.Self | WET.Deleted,
|
||||
SelfMoved = WET.Self | WET.Moved,
|
||||
SelfMetadata = WET.Self | WET.Metadata,
|
||||
SelfAccessed = WET.Self | WET.Accessed,
|
||||
}
|
||||
|
||||
alias WET = WatchEventType;
|
||||
|
||||
struct WatchEvent
|
||||
{
|
||||
WatchEventType type;
|
||||
u8[][2] names;
|
||||
}
|
||||
|
||||
version(Windows)
|
||||
|
||||
9
test.sh
Executable file
9
test.sh
Executable file
@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
name="Test_Runner"
|
||||
|
||||
ldc2 platform.d aliases.d math.d util.d alloc.d external/xxhash/xxhash.d -P-I/usr/include/freetype2 -L-lfreetype --main --unittest --of=$name
|
||||
rm $name.o
|
||||
./$name
|
||||
rm $name
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user