From a35b410b1e9c2773cf6ae59c2f9b34fe9b908750 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Fri, 21 Sep 2018 22:55:25 +0200 Subject: gl: Save history and bookmarks in ~/.mupdf.history. Use MuJS to parse/write JSON object. --- Makefile | 2 +- platform/gl/gl-main.c | 213 ++++++++++++++++++++++++++++++++++++++++- platform/win32/mupdf-gl.vcproj | 24 ++--- 3 files changed, 225 insertions(+), 14 deletions(-) diff --git a/Makefile b/Makefile index 0eebda0f..37fc48e6 100644 --- a/Makefile +++ b/Makefile @@ -90,7 +90,7 @@ $(OUT)/platform/x11/curl/%.o : platform/x11/%.c $(CC_CMD) -Wall $(X11_CFLAGS) $(CURL_CFLAGS) -DHAVE_CURL $(OUT)/platform/gl/%.o : platform/gl/%.c - $(CC_CMD) -Wall $(GLUT_CFLAGS) + $(CC_CMD) -Wall $(THIRD_CFLAGS) $(GLUT_CFLAGS) ifeq ($(HAVE_OBJCOPY),yes) $(OUT)/source/fitz/noto.o : source/fitz/noto.c diff --git a/platform/gl/gl-main.c b/platform/gl/gl-main.c index dcb97c21..fad822fa 100644 --- a/platform/gl/gl-main.c +++ b/platform/gl/gl-main.c @@ -5,12 +5,15 @@ #include #include +#include "mujs.h" + #ifndef PATH_MAX #define PATH_MAX 2048 #endif #ifndef _WIN32 #include /* for fork and exec */ +char *realpath(const char *path, char *resolved_path); /* in gl-file.c */ #endif #ifdef __APPLE__ @@ -143,6 +146,203 @@ static int future_count = 0; static struct mark future[256]; static struct mark marks[10]; +static char *get_history_filename(void) +{ + static char history_path[PATH_MAX]; + static int once = 0; + if (!once) + { + char *home = getenv("HOME"); + if (!home) + home = getenv("USERPROFILE"); + if (!home) + home = "/tmp"; + fz_snprintf(history_path, sizeof history_path, "%s/.mupdf.history", home); + fz_cleanname(history_path); + once = 1; + } + return history_path; +} + +static void read_history_file_as_json(js_State *J) +{ + fz_buffer *buf = NULL; + const char *json = "{}"; + + fz_var(buf); + + if (fz_file_exists(ctx, get_history_filename())) + { + fz_try(ctx) + { + buf = fz_read_file(ctx, get_history_filename()); + json = fz_string_from_buffer(ctx, buf); + } + fz_catch(ctx) + ; + } + + js_getglobal(J, "JSON"); + js_getproperty(J, -1, "parse"); + js_pushnull(J); + js_pushstring(J, json); + if (js_pcall(J, 1)) + { + fz_warn(ctx, "Can't parse history file: %s", js_trystring(J, -1, "error")); + js_pop(J, 1); + js_newobject(J); + } + else + { + js_rot2pop1(J); + } + + fz_drop_buffer(ctx, buf); +} + +static void load_history(void) +{ + js_State *J; + char absname[PATH_MAX]; + int i, n; + + if (!realpath(filename, absname)) + return; + + J = js_newstate(NULL, NULL, 0); + + read_history_file_as_json(J); + + if (js_hasproperty(J, -1, absname)) + { + if (js_hasproperty(J, -1, "current")) + { + currentpage = js_tryinteger(J, -1, 1) - 1; + js_pop(J, 1); + } + + if (js_hasproperty(J, -1, "history")) + { + if (js_isarray(J, -1)) + { + history_count = fz_clampi(js_getlength(J, -1), 0, nelem(history)); + for (i = 0; i < history_count; ++i) + { + js_getindex(J, -1, i); + history[i].page = js_tryinteger(J, -1, 1) - 1; + js_pop(J, 1); + } + } + js_pop(J, 1); + } + + if (js_hasproperty(J, -1, "future")) + { + if (js_isarray(J, -1)) + { + future_count = fz_clampi(js_getlength(J, -1), 0, nelem(future)); + for (i = 0; i < future_count; ++i) + { + js_getindex(J, -1, i); + future[i].page = js_tryinteger(J, -1, 1) - 1; + js_pop(J, 1); + } + } + js_pop(J, 1); + } + + if (js_hasproperty(J, -1, "marks")) + { + if (js_isarray(J, -1)) + { + n = fz_clampi(js_getlength(J, -1), 0, nelem(marks)); + for (i = 0; i < n; ++i) + { + js_getindex(J, -1, i); + marks[i].page = js_tryinteger(J, -1, 1) - 1; + js_pop(J, 1); + } + } + js_pop(J, 1); + } + } + + js_freestate(J); +} + +static void save_history(void) +{ + js_State *J; + char absname[PATH_MAX]; + fz_output *out = NULL; + const char *json; + int i; + + fz_var(out); + + if (!realpath(filename, absname)) + return; + + J = js_newstate(NULL, NULL, 0); + + read_history_file_as_json(J); + + js_newobject(J); + { + js_pushnumber(J, currentpage+1); + js_setproperty(J, -2, "current"); + + js_newarray(J); + for (i = 0; i < history_count; ++i) + { + js_pushnumber(J, history[i].page+1); + js_setindex(J, -2, i); + } + js_setproperty(J, -2, "history"); + + js_newarray(J); + for (i = 0; i < future_count; ++i) + { + js_pushnumber(J, future[i].page+1); + js_setindex(J, -2, i); + } + js_setproperty(J, -2, "future"); + + js_newarray(J); + for (i = 0; i < nelem(marks); ++i) + { + js_pushnumber(J, marks[i].page+1); + js_setindex(J, -2, i); + } + js_setproperty(J, -2, "marks"); + } + js_setproperty(J, -2, absname); + + js_getglobal(J, "JSON"); + js_getproperty(J, -1, "stringify"); + js_pushnull(J); + js_copy(J, -4); + js_pushnull(J); + js_pushnumber(J, 0); + js_call(J, 3); + js_rot2pop1(J); + json = js_tostring(J, -1); + + fz_try(ctx) + { + out = fz_new_output_with_path(ctx, get_history_filename(), 0); + fz_write_string(ctx, out, json); + fz_write_byte(ctx, out, '\n'); + fz_close_output(ctx, out); + } + fz_always(ctx) + fz_drop_output(ctx, out); + fz_catch(ctx) + fz_warn(ctx, "Can't write history file."); + + js_freestate(J); +} + static int search_active = 0; static struct input search_input = { { 0 }, 0 }; static char *search_needle = 0; @@ -160,7 +360,11 @@ static void error_dialog(void) ui_label("%C %s", 0x1f4a3, error_message); /* BOMB */ ui_layout(B, NONE, S, 2, 2); if (ui_button("Quit") || ui.key == KEY_ENTER || ui.key == KEY_ESCAPE || ui.key == 'q') + { + if (doc) + save_history(); exit(1); + } ui_dialog_end(); } void ui_show_error_dialog(const char *fmt, ...) @@ -290,6 +494,8 @@ static void restore_mark(struct mark mark) static void push_history(void) { + if (history_count > 0 && history[history_count-1].page == currentpage) + return; if (history_count + 1 >= nelem(history)) { memmove(history, history + 1, sizeof *history * (nelem(history) - 1)); @@ -656,11 +862,13 @@ static void load_document(void) } anchor = NULL; + load_history(); currentpage = fz_clampi(currentpage, 0, fz_count_pages(ctx, doc) - 1); } void reload(void) { + save_history(); load_document(); if (doc) { @@ -778,7 +986,10 @@ static void clear_search(void) static void do_app(void) { if (ui.key == KEY_F4 && ui.mod == GLUT_ACTIVE_ALT) + { + save_history(); glutLeaveMainLoop(); + } if (ui.down || ui.middle || ui.right || ui.key) showinfo = showhelp = 0; @@ -795,7 +1006,7 @@ static void do_app(void) case 'F': showform = !showform; break; case 'i': showinfo = !showinfo; break; case 'r': reload(); break; - case 'q': glutLeaveMainLoop(); break; + case 'q': save_history(); glutLeaveMainLoop(); break; case 'I': currentinvert = !currentinvert; break; case 'f': toggle_fullscreen(); break; diff --git a/platform/win32/mupdf-gl.vcproj b/platform/win32/mupdf-gl.vcproj index 3f48e261..c5229cce 100644 --- a/platform/win32/mupdf-gl.vcproj +++ b/platform/win32/mupdf-gl.vcproj @@ -43,7 +43,7 @@ @@ -340,7 +340,7 @@ /> @@ -640,7 +640,7 @@