Skip to content
Merged
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
78 changes: 65 additions & 13 deletions src/lib/libglfw.js
Original file line number Diff line number Diff line change
Expand Up @@ -838,29 +838,34 @@ var LibraryGLFW = {
event.preventDefault();

#if FILESYSTEM
var drop_dir = '.glfw_dropped_files';
var filenames = _malloc(event.dataTransfer.files.length * {{{ POINTER_SIZE }}});
var filenamesArray = [];
var count = event.dataTransfer.files.length;
for (var i = 0; i < event.dataTransfer.files.length; ++i) {
var path = `/${drop_dir}/${event.dataTransfer.files[i].name.replace(/\//g, "_")}`;
var filename = stringToNewUTF8(path);
filenamesArray.push(filename);
{{{ makeSetValue('filenames', `i*${POINTER_SIZE}` , 'filename', '*') }}};
}

// Read and save the files to emscripten's FS
var written = 0;
var drop_dir = '.glfw_dropped_files';
FS.createPath('/', drop_dir);

function save(file) {
var path = '/' + drop_dir + '/' + file.name.replace(/\//g, '_');
function save(file, in_path, numfiles) {
var path = '/' + drop_dir + in_path + '/' + file.name.replace(/\//g, '_');
var reader = new FileReader();
reader.onloadend = (e) => {
if (reader.readyState != 2) { // not DONE
++written;
out('failed to read dropped file: '+file.name+': '+reader.error);
err(`failed to read dropped file: ${in_path}/${file.name}: ${reader.error}`);
return;
}

var data = e.target.result;
FS.writeFile(path, new Uint8Array(data));
if (++written === count) {
{{{ makeDynCall('vpip', 'GLFW.active.dropFunc') }}}(GLFW.active.id, count, filenames);
if (++written === numfiles) {
{{{ makeDynCall('vpip', 'GLFW.active.dropFunc') }}}(GLFW.active.id, filenamesArray.length, filenames);

for (var i = 0; i < filenamesArray.length; ++i) {
_free(filenamesArray[i]);
Expand All @@ -869,14 +874,61 @@ var LibraryGLFW = {
}
};
reader.readAsArrayBuffer(file);

var filename = stringToNewUTF8(path);
filenamesArray.push(filename);
{{{ makeSetValue('filenames', `i*${POINTER_SIZE}` , 'filename', '*') }}};
}

for (var i = 0; i < count; ++i) {
save(event.dataTransfer.files[i]);
let filesQ = [];
function finalize() {
var count = filesQ.length;
for (var i = 0; i < count; ++i) {
save(filesQ[i].file, filesQ[i].path, count);
}
}

if (DataTransferItem.prototype.webkitGetAsEntry) {
let entriesTree = {};
function markDone(fullpath, recursive) {
if (entriesTree[fullpath].subpaths.length != 0) return;
delete entriesTree[fullpath];
let parentpath = fullpath.substring(0, fullpath.lastIndexOf('/'));
if (!entriesTree.hasOwnProperty(parentpath)) {
if (Object.keys(entriesTree).length == 0) finalize();
return;
}
const fpIndex = entriesTree[parentpath].subpaths.indexOf(fullpath);
if (fpIndex > -1) entriesTree[parentpath].subpaths.splice(fpIndex, 1);
if (recursive) markDone(parentpath, true);
if (Object.keys(entriesTree).length == 0) finalize();
}
function processEntry(entry) {
let fp = entry.fullPath;
let pp = fp.substring(0, fp.lastIndexOf('/'));
entriesTree[fp] = { subpaths: [] };
if (entry.isFile) {
entry.file((f) => { filesQ.push({ file: f, path: pp }); markDone(fp, false); })
} else if (entry.isDirectory) {
if (entriesTree.hasOwnProperty(pp)) entriesTree[pp].subpaths.push(fp);
FS.createPath("/" + drop_dir + pp, entry.name);
var reader = entry.createReader();
var rRead = function (dirEntries) {
if (dirEntries.length == 0) {
markDone(fp, true);
return;
}
for (const ent of dirEntries) processEntry(ent);
reader.readEntries(rRead);
};
reader.readEntries(rRead);
}
}
for (const item of event.dataTransfer.items) {
processEntry(item.webkitGetAsEntry());
}
} else {
// fallback for browsers that does not support webkitGetAsEntry
for (const file of event.dataTransfer.files) {
filesQ.push({ file: file, path: "" });
}
finalize();
}
#endif // FILESYSTEM

Expand Down
36 changes: 26 additions & 10 deletions test/interactive/test_glfw_dropfile.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

#include <stdio.h>
#include <ftw.h>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, I've been using UNIX for over 30 years and I've never come across this API before! TIL.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eventually it was suggest to me by google AI while searching how to traverse directory using C lang

#include <stdbool.h>
#include <string.h>
#include <assert.h>
Expand All @@ -25,29 +26,44 @@ void render() {
glClear(GL_COLOR_BUFFER_BIT);
}

void on_file_drop(GLFWwindow *window, int count, const char **paths) {
for (int i = 0; i < count; ++i) {
printf("dropped file %s\n", paths[i]);

FILE *fp = fopen(paths[i], "rb");
int display_info(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf) {
printf("%-3s %2d %s\n",
(tflag == FTW_D) ? "d" : "f", // Type: directory or file
ftwbuf->level, // Depth level
fpath // Full path
);
if ( tflag != FTW_D ) {
FILE *fp = fopen(fpath, "rb");
if (!fp) {
printf("failed to open %s\n", paths[i]);
printf("failed to open %s\n", fpath);
perror("fopen");
assert(false);
return -1;
}
int c;
long size = 0;
bool dump = strstr(paths[i], ".txt") != 0;
bool dump = strstr(fpath, ".txt") != 0;
if (dump) printf("text file contents (first 100 bytes): ");
while ((c = fgetc(fp)) != -1) {
++size;
if (dump && size <= 100) putchar(c);
}
if (dump) putchar('\n');
printf("read %ld bytes from %s\n", size, paths[i]);
printf("read %ld bytes from %s\n", size, fpath);

fclose(fp);
}
return 0; // Return 0 to continue traversal
}

void on_file_drop(GLFWwindow *window, int count, const char **paths) {
for (int i = 0; i < count; ++i) {
printf("dropped file %s\n", paths[i]);
// FTW_PHYS: Do not follow symbolic links
if (nftw(paths[i], display_info, 20, FTW_PHYS) == -1) {
printf("failed to traverse %s\n", paths[i]);
perror("nftw");
assert(false);
}
#ifdef __EMSCRIPTEN__
// Emscripten copies the contents of the dropped file into the
// in-browser filesystem. Delete after usage to free up memory.
Expand Down Expand Up @@ -79,7 +95,7 @@ int main() {
glfwSetDropCallback(g_window, on_file_drop);

// Main loop
printf("Drag and drop a file from your desktop onto the green canvas.\n");
printf("Drag and drop a file or directory from your desktop onto the green canvas.\n");
#ifdef __EMSCRIPTEN__
emscripten_set_main_loop(render, 0, 1);
#else
Expand Down