summaryrefslogtreecommitdiff
path: root/platform/x11
diff options
context:
space:
mode:
authorRobin Watts <robin.watts@artifex.com>2013-07-17 17:32:12 +0100
committerRobin Watts <robin.watts@artifex.com>2013-07-19 19:56:27 +0100
commit42eb247ea0c69ba8cc249b1bb44fafcdcbdd2621 (patch)
tree6fde9d99a9551faa8abc6e0d4d3aa24db5824e4f /platform/x11
parent90d1d2cf603ba9d61e8160c0ba12325cd8249034 (diff)
downloadmupdf-42eb247ea0c69ba8cc249b1bb44fafcdcbdd2621.tar.xz
Add mupdf-curl app
Windows and X11. Allows files to be fetched and displayed as they are downloaded both with and without linearization, using hints if available.
Diffstat (limited to 'platform/x11')
-rw-r--r--platform/x11/curl_stream.c525
-rw-r--r--platform/x11/curl_stream.h7
-rw-r--r--platform/x11/jstest_main.c4
-rw-r--r--platform/x11/pdfapp.c110
-rw-r--r--platform/x11/pdfapp.h7
-rw-r--r--platform/x11/win_main.c24
-rw-r--r--platform/x11/x11_main.c24
7 files changed, 685 insertions, 16 deletions
diff --git a/platform/x11/curl_stream.c b/platform/x11/curl_stream.c
new file mode 100644
index 00000000..3a0de68c
--- /dev/null
+++ b/platform/x11/curl_stream.c
@@ -0,0 +1,525 @@
+/* Simple example fz_stream implementation using curl */
+
+#include "mupdf/fitz.h"
+#include "curl_stream.h"
+
+#define CURL_STATICLIB
+#include <curl/curl.h>
+
+#undef DEBUG_BLOCK_FETCHING
+
+#ifdef DEBUG_BLOCK_FETCHING
+#define DEBUG_MESSAGE(A) do { fz_warn A; } while(0)
+#else
+#define DEBUG_MESSAGE(A) do { } while(0)
+#endif
+
+#define BLOCK_SHIFT 12
+#define BLOCK_SIZE (1<<BLOCK_SHIFT)
+
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include "pthread.h"
+#include <ctype.h>
+#endif
+
+typedef struct curl_stream_state_s curl_stream_state;
+
+struct curl_stream_state_s
+{
+ fz_context *ctx;
+ CURL *handle;
+ char *filename;
+ int data_arrived;
+ int content_length; /* As returned by curl. -1 for unknown. */
+ int total_length; /* As obtained from the Content-Range header. */
+ int buffer_max;
+ int buffer_fill;
+ unsigned char *buffer;
+ int map_length;
+ unsigned char *map;
+ int fill_point; /* The next file offset we will fetch to */
+ int current_fill_point; /* The current file offset we are fetching to */
+ int complete;
+ int kill_thread;
+ void (*more_data)(void *, int);
+ void *more_data_arg;
+
+#ifdef _WIN32
+ void *thread;
+ DWORD thread_id;
+ HANDLE mutex;
+#else
+ pthread_t thread;
+ pthread_mutex_t mutex;
+#endif
+};
+
+static void fetcher_thread(curl_stream_state *state);
+
+#ifdef _WIN32
+static void
+lock(curl_stream_state *state)
+{
+ WaitForSingleObject(state->mutex, INFINITE);
+}
+
+static void
+unlock(curl_stream_state *state)
+{
+ ReleaseMutex(state->mutex);
+}
+
+static DWORD WINAPI
+win_thread(void *lparam)
+{
+ fetcher_thread((curl_stream_state *)lparam);
+
+ return 0;
+}
+
+#else /* Anything else assumed to be pthreads */
+
+static void
+lock(curl_stream_state *state)
+{
+ pthread_mutex_lock(&state->mutex);
+}
+
+static void
+unlock(curl_stream_state *state)
+{
+ pthread_mutex_unlock(&state->mutex);
+}
+
+
+static void *
+pthread_thread(void *arg)
+{
+ fetcher_thread((curl_stream_state *)arg);
+
+ return NULL;
+}
+#endif
+
+static size_t header_arrived(void *ptr, size_t size, size_t nmemb, void *state_)
+{
+ curl_stream_state *state = (curl_stream_state *)state_;
+
+ if (strncmp(ptr, "Content-Range:", 14) == 0)
+ {
+ char *p = (char *)ptr;
+ int len = nmemb * size;
+ int start, end, total;
+ while (len && !isdigit(*p))
+ p++, len--;
+ start = 0;
+ while (len && isdigit(*p))
+ {
+ start = start*10 + *p-'0';
+ p++, len--;
+ }
+ while (len && !isdigit(*p))
+ p++, len--;
+ end = 0;
+ while (len && isdigit(*p))
+ {
+ end = end*10 + *p-'0';
+ p++, len--;
+ }
+ while (len && !isdigit(*p))
+ p++, len--;
+ total = 0;
+ while (len && isdigit(*p))
+ {
+ total = total*10 + *p-'0';
+ p++, len--;
+ }
+ state->total_length = total;
+ }
+
+ return nmemb * size;
+}
+
+static size_t data_arrived(void *ptr, size_t size, size_t nmemb, void *state_)
+{
+ curl_stream_state *state = (curl_stream_state *)state_;
+ int old_start;
+
+ size *= nmemb;
+
+ if (state->data_arrived == 0)
+ {
+ double d;
+ long response;
+ int len;
+ /* This is the first time data has arrived. If the response
+ * code is 206, then we can do byte requests, and we will
+ * known the total_length from having processed the header
+ * already. */
+ curl_easy_getinfo(state->handle, CURLINFO_RESPONSE_CODE, &response);
+ if (state->total_length && response == 206)
+ {
+ /* We got a range header, and the correct http response
+ * code. We can assume that byte fetches are accepted
+ * and we'll run without progressive mode. */
+ state->content_length = len = state->total_length;
+ state->map_length = (len+BLOCK_SIZE-1)>>BLOCK_SHIFT;
+ state->map = fz_malloc_no_throw(state->ctx, (state->map_length+7)>>3);
+ state->buffer = fz_malloc_no_throw(state->ctx, len);
+ state->buffer_max = len;
+ if (state->map == NULL || state->buffer == NULL)
+ {
+ /* FIXME: Crap error handling! */
+ exit(1);
+ }
+ memset(state->map, 0, (state->map_length+7)>>3);
+ }
+ else
+ {
+ /* So we can't use ByteRanges. Do we at least know the
+ * complete length of the file? */
+ curl_easy_getinfo(state->handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d);
+ state->content_length = len = (int)d;
+ if (len > 0 && response == 200)
+ {
+ /* Yes. We can run as a progressive file */
+ state->buffer = fz_malloc_no_throw(state->ctx, len);
+ state->buffer_max = len;
+ if (state->buffer == NULL)
+ {
+ /* FIXME: Crap error handling! */
+ exit(1);
+ }
+ }
+ else
+ {
+ /* What a crap server. Won't tell us how big
+ * the file is. We'll have to expand as data
+ * as arrives. */
+ state->content_length = -1;
+ }
+ }
+ state->data_arrived = 1;
+ }
+ if (state->content_length < 0)
+ {
+ int newsize = state->current_fill_point + size;
+ if (newsize > state->buffer_max)
+ {
+ /* Expand the buffer */
+ int new_max = state->buffer_max * 2;
+ if (new_max == 0)
+ new_max = 4096;
+ state->buffer = fz_resize_array_no_throw(state->ctx, state->buffer, new_max, 1);
+ if (state->buffer == NULL)
+ {
+ /* FIXME: Crap error handling! */
+ exit(1);
+ }
+ state->buffer_max = new_max;
+ }
+ }
+
+ DEBUG_MESSAGE((state->ctx, "data arrived: offset=%d len=%d", state->current_fill_point, size));
+ old_start = state->current_fill_point;
+ memcpy(state->buffer + state->current_fill_point, ptr, size);
+ state->current_fill_point += size;
+ if (state->current_fill_point == state->content_length ||
+ (((state->current_fill_point ^ old_start) & ~(BLOCK_SIZE-1)) != 0))
+ {
+ if (state->map)
+ {
+ old_start >>= BLOCK_SHIFT;
+ state->map[old_start>>3] |= 1<<(old_start & 7);
+ }
+ }
+
+ if (state->more_data)
+ state->more_data(state->more_data_arg, 0);
+
+ return size;
+}
+
+#define HAVE_BLOCK(map, num) \
+ (((map)[(num)>>3] & (1<<((num) & 7))) != 0)
+
+static void
+fetch_chunk(curl_stream_state *state)
+{
+ char text[32];
+ int fill, start, end;
+
+ lock(state);
+
+ if (state->kill_thread)
+ {
+ state->complete = 1;
+ unlock(state);
+ return;
+ }
+
+ fill = state->fill_point;
+ if (state->content_length > 0)
+ {
+ /* Find the next block that we haven't got */
+ int map_length = state->map_length;
+ unsigned char *map = state->map;
+ for ( ; (fill < map_length && HAVE_BLOCK(map, fill)); fill++);
+ if (fill == map_length)
+ {
+ for (fill = 0;
+ (fill < map_length && HAVE_BLOCK(map, fill));
+ fill++);
+ if (fill == map_length)
+ {
+ /* We've got it all! */
+ state->complete = 1;
+ state->kill_thread = 1;
+ unlock(state);
+ if (state->more_data)
+ state->more_data(state->more_data_arg, 1);
+ fz_warn(state->ctx, "Background fetch complete!");
+ return;
+ }
+ }
+ DEBUG_MESSAGE((state->ctx, "block requested was %d, fetching %d", state->fill_point, fill));
+ state->fill_point = fill;
+ }
+
+ unlock(state);
+
+ /* Fetch that block */
+ start = fill * BLOCK_SIZE;
+ end = start + BLOCK_SIZE-1;
+ state->current_fill_point = start;
+ if (state->content_length > 0 && start >= state->content_length)
+ state->complete = 1;
+ if (state->content_length > 0 && end >= state->content_length)
+ end = state->content_length-1;
+ snprintf(text, 32, "%d-%d", start, end);
+ curl_easy_setopt(state->handle, CURLOPT_RANGE, text);
+ curl_easy_perform(state->handle);
+}
+
+static void
+fetcher_thread(curl_stream_state *state)
+{
+ while (!state->complete)
+ fetch_chunk(state);
+}
+
+static int
+stream_read(fz_stream *stream, unsigned char *buf, int len)
+{
+ curl_stream_state *state = (curl_stream_state *)stream->state;
+ int len_read = 0;
+ int read_point = stream->pos;
+ int block = read_point>>BLOCK_SHIFT;
+ int left_over = (-read_point) & (BLOCK_SIZE-1);
+
+ if (state->content_length == 0)
+ fz_throw(stream->ctx, FZ_ERROR_TRYLATER, "read of a block we don't have (A) (offset=%d)", read_point);
+
+ if (state->map == NULL)
+ {
+ /* We are doing a simple linear fetch as we don't know the
+ * content length. */
+ if (read_point + len > state->current_fill_point)
+ {
+ stream->rp = stream->wp = stream->bp;
+ fz_throw(stream->ctx, FZ_ERROR_TRYLATER, "read of a block we don't have (B) (offset=%d)", read_point);
+ }
+ memcpy(buf, state->buffer + read_point, len);
+ return len;
+ }
+
+ if (read_point + len > state->content_length)
+ len = state->content_length - read_point;
+ if (left_over > len)
+ left_over = len;
+
+ if (left_over)
+ {
+ /* We are starting midway through a block */
+ if (!HAVE_BLOCK(state->map, block))
+ {
+ lock(state);
+ state->fill_point = block;
+ unlock(state);
+ stream->rp = stream->wp = stream->bp;
+ fz_throw(stream->ctx, FZ_ERROR_TRYLATER, "read of a block we don't have (C) (offset=%d)", read_point);
+ }
+ block++;
+ if (left_over > len)
+ left_over = len;
+ memcpy(buf, state->buffer + read_point, left_over);
+ buf += left_over;
+ read_point += left_over;
+ len -= left_over;
+ len_read += left_over;
+ }
+
+ /* Copy any complete blocks */
+ while (len > BLOCK_SIZE)
+ {
+ if (!HAVE_BLOCK(state->map, block))
+ {
+ lock(state);
+ state->fill_point = block;
+ unlock(state);
+ stream->rp = stream->wp = stream->bp;
+ fz_throw(stream->ctx, FZ_ERROR_TRYLATER, "read of a block we don't have (D) (offset=%d)", read_point);
+ }
+ block++;
+ memcpy(buf, state->buffer + read_point, BLOCK_SIZE);
+ buf += BLOCK_SIZE;
+ read_point += BLOCK_SIZE;
+ len -= BLOCK_SIZE;
+ len_read += BLOCK_SIZE;
+ }
+
+ /* Copy any trailing bytes */
+ if (len > 0)
+ {
+ if (!HAVE_BLOCK(state->map, block))
+ {
+ lock(state);
+ state->fill_point = block;
+ unlock(state);
+ stream->rp = stream->wp = stream->bp;
+ fz_throw(stream->ctx, FZ_ERROR_TRYLATER, "read of a block we don't have (E) (offset=%d)", read_point);
+ }
+ memcpy(buf, state->buffer + read_point, len);
+ len_read += len;
+ }
+
+ return len_read;
+}
+
+static void
+stream_close(fz_context *ctx, void *state_)
+{
+ curl_stream_state *state = (curl_stream_state *)state_;
+
+ if (!state || state->kill_thread)
+ return;
+
+ lock(state);
+ state->kill_thread = 1;
+ unlock(state);
+
+#ifdef _WIN32
+ WaitForSingleObject(state->thread, INFINITE);
+ CloseHandle(state->thread);
+ CloseHandle(state->mutex);
+#else
+ pthread_join(state->thread, NULL);
+ pthread_mutex_destroy(&state->mutex);
+#endif
+
+ fz_free(state->ctx, state->buffer);
+ fz_free(state->ctx, state->map);
+ fz_free(state->ctx, state);
+}
+
+static fz_stream hack_stream;
+static curl_stream_state hack;
+static int hack_pos;
+
+static void
+stream_seek(fz_stream *stream, int offset, int whence)
+{
+ curl_stream_state *state = (curl_stream_state *)stream->state;
+
+ switch(whence)
+ {
+ case SEEK_CUR:
+ offset += stream->pos;
+ break;
+ case SEEK_END:
+ offset += state->content_length;
+ break;
+ default:
+ case SEEK_SET:
+ break;
+ }
+ if (offset < 0)
+ offset = 0;
+ else if (state->content_length > 0 && offset > state->content_length)
+ offset = state->content_length;
+ stream->wp = stream->rp = stream->bp;
+ stream->pos = offset;
+ hack = *state;
+ hack_pos = offset;
+ hack_stream = *stream;
+}
+
+static int
+stream_meta(fz_stream *stream, int key, int size, void *ptr)
+{
+ curl_stream_state *state = (curl_stream_state *)stream->state;
+
+ switch(key)
+ {
+ case FZ_STREAM_META_LENGTH:
+ if (!state->data_arrived)
+ fz_throw(stream->ctx, FZ_ERROR_TRYLATER, "still awaiting file length");
+ return state->content_length;
+ case FZ_STREAM_META_PROGRESSIVE:
+ return 1;
+ }
+ return -1;
+}
+
+fz_stream *fz_stream_from_curl(fz_context *ctx, char *filename, void (*more_data)(void *,int), void *more_data_arg)
+{
+ CURLcode ret;
+ CURL *handle;
+ curl_stream_state *state = fz_malloc_struct(ctx, curl_stream_state);
+ fz_stream *stream;
+
+ ret = curl_global_init(CURL_GLOBAL_ALL);
+ if (ret != 0)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "curl init failed (code %d)", ret);
+
+ state->ctx = ctx;
+ state->handle = handle = curl_easy_init();
+ state->more_data = more_data;
+ state->more_data_arg = more_data_arg;
+
+ curl_easy_setopt(handle, CURLOPT_URL, filename);
+
+ curl_easy_setopt(handle, CURLOPT_NOPROGRESS, 1);
+
+ curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, data_arrived);
+
+ curl_easy_setopt(handle, CURLOPT_WRITEDATA, state);
+
+ curl_easy_setopt(handle, CURLOPT_WRITEHEADER, state);
+
+ curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, header_arrived);
+
+#ifdef _WIN32
+ state->mutex = CreateMutex(NULL, FALSE, NULL);
+ if (state->mutex == NULL)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "mutex creation failed");
+
+ state->thread = CreateThread(NULL, 0, win_thread, state, 0, &state->thread_id);
+ if (state->thread == NULL)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "thread creation failed");
+#else
+ if (pthread_mutex_init(&state->mutex, NULL))
+ fz_throw(ctx, FZ_ERROR_GENERIC, "mutex creation failed");
+
+ if (pthread_create(&state->thread, NULL, pthread_thread, state))
+ fz_throw(ctx, FZ_ERROR_GENERIC, "thread creation failed");
+
+#endif
+
+ stream = fz_new_stream(ctx, state, stream_read, stream_close);
+ stream->seek = stream_seek;
+ stream->meta = stream_meta;
+ return stream;
+}
diff --git a/platform/x11/curl_stream.h b/platform/x11/curl_stream.h
new file mode 100644
index 00000000..8672df17
--- /dev/null
+++ b/platform/x11/curl_stream.h
@@ -0,0 +1,7 @@
+#if !defined(CURL_STREAM_H) && defined(HAVE_CURL)
+
+#define CURL_STREAM_H
+
+fz_stream *fz_stream_from_curl(fz_context *ctx, char *url, void (*more_data)(void *,int), void *more_data_arg);
+
+#endif
diff --git a/platform/x11/jstest_main.c b/platform/x11/jstest_main.c
index a07385bc..4cb11185 100644
--- a/platform/x11/jstest_main.c
+++ b/platform/x11/jstest_main.c
@@ -158,6 +158,10 @@ void winreloadfile(pdfapp_t *app)
pdfapp_open(app, filename, 1);
}
+void winreloadpage(pdfapp_t *app)
+{
+}
+
void winopenuri(pdfapp_t *app, char *buf)
{
}
diff --git a/platform/x11/pdfapp.c b/platform/x11/pdfapp.c
index c891558f..af4cb095 100644
--- a/platform/x11/pdfapp.c
+++ b/platform/x11/pdfapp.c
@@ -1,4 +1,5 @@
#include "pdfapp.h"
+#include "curl_stream.h"
#include <ctype.h> /* for tolower() */
@@ -19,6 +20,12 @@ enum panning
PAN_TO_BOTTOM
};
+enum
+{
+ PDFAPP_OUTLINE_DEFERRED = 1,
+ PDFAPP_OUTLINE_LOAD_NOW = 2
+};
+
static void pdfapp_showpage(pdfapp_t *app, int loadpage, int drawpage, int repaint, int transition);
static void pdfapp_updatepage(pdfapp_t *app);
@@ -160,6 +167,22 @@ void pdfapp_open(pdfapp_t *app, char *filename, int reload)
pdfapp_open_progressive(app, filename, reload, 0);
}
+#ifdef HAVE_CURL
+static void
+pdfapp_more_data(void *app_, int complete)
+{
+ pdfapp_t *app = (pdfapp_t *)app_;
+
+ if (complete && app->outline_deferred == PDFAPP_OUTLINE_DEFERRED)
+ {
+ app->outline_deferred = PDFAPP_OUTLINE_LOAD_NOW;
+ winreloadpage(app);
+ }
+ else if (app->incomplete)
+ winreloadpage(app);
+}
+#endif
+
void pdfapp_open_progressive(pdfapp_t *app, char *filename, int reload, int bps)
{
fz_context *ctx = app->ctx;
@@ -169,6 +192,31 @@ void pdfapp_open_progressive(pdfapp_t *app, char *filename, int reload, int bps)
{
pdf_document *idoc;
+#ifdef HAVE_CURL
+ if (!strncmp(filename, "http://", 7))
+ {
+ app->stream = fz_stream_from_curl(ctx, filename, pdfapp_more_data, app);
+ while (1)
+ {
+ fz_try(ctx)
+ {
+ fz_seek(app->stream, 0, SEEK_SET);
+ app->doc = fz_open_document_with_stream(ctx, filename, app->stream);
+ }
+ fz_catch(ctx)
+ {
+ if (fz_caught(ctx) == FZ_ERROR_TRYLATER)
+ {
+ pdfapp_warn(app, "not enough data to open yet");
+ continue;
+ }
+ fz_rethrow(ctx);
+ }
+ break;
+ }
+ }
+ else
+#endif
if (bps == 0)
{
app->doc = fz_open_document(ctx, filename);
@@ -250,8 +298,7 @@ void pdfapp_open_progressive(pdfapp_t *app, char *filename, int reload, int bps)
{
if (fz_caught(ctx) == FZ_ERROR_TRYLATER)
{
- pdfapp_warn(app, "not enough data to load outline yet - ignoring");
- /* FIXME: Set 'outline_deferred' and retry at end? */
+ app->outline_deferred = PDFAPP_OUTLINE_DEFERRED;
}
else
fz_rethrow(ctx);
@@ -325,6 +372,10 @@ void pdfapp_close(pdfapp_t *app)
fz_close_document(app->doc);
app->doc = NULL;
+#ifdef HAVE_CURL
+ fz_close(app->stream);
+#endif
+
fz_flush_warnings(app->ctx);
}
@@ -486,6 +537,8 @@ static void pdfapp_loadpage(pdfapp_t *app)
app->page_bbox.x1 = 100;
app->page_bbox.y1 = 100;
+ app->incomplete = 0;
+
fz_try(app->ctx)
{
app->page = fz_load_page(app->doc, app->pageno - 1);
@@ -494,7 +547,10 @@ static void pdfapp_loadpage(pdfapp_t *app)
}
fz_catch(app->ctx)
{
- pdfapp_warn(app, "Cannot load page");
+ if (fz_caught(app->ctx) == FZ_ERROR_TRYLATER)
+ app->incomplete = 1;
+ else
+ pdfapp_warn(app, "Cannot load page");
return;
}
@@ -512,14 +568,15 @@ static void pdfapp_loadpage(pdfapp_t *app)
mdev = fz_new_list_device(app->ctx, app->annotations_list);
for (annot = fz_first_annot(app->doc, app->page); annot; annot = fz_next_annot(app->doc, annot))
fz_run_annot(app->doc, app->page, annot, mdev, &fz_identity, &cookie);
- if (cookie.errors)
+ if (cookie.incomplete)
{
- pdfapp_warn(app, "Errors found on page");
- errored = 1;
+ app->incomplete = 1;
+ //pdfapp_warn(app, "Incomplete page rendering");
}
- if (cookie.incomplete)
+ else if (cookie.errors)
{
- pdfapp_warn(app, "Incomplete page rendering");
+ pdfapp_warn(app, "Errors found on page");
+ errored = 1;
}
}
fz_always(app->ctx)
@@ -528,8 +585,13 @@ static void pdfapp_loadpage(pdfapp_t *app)
}
fz_catch(app->ctx)
{
- pdfapp_warn(app, "Cannot load page");
- errored = 1;
+ if (fz_caught(app->ctx) == FZ_ERROR_TRYLATER)
+ app->incomplete = 1;
+ else
+ {
+ pdfapp_warn(app, "Cannot load page");
+ errored = 1;
+ }
}
fz_try(app->ctx)
@@ -538,7 +600,9 @@ static void pdfapp_loadpage(pdfapp_t *app)
}
fz_catch(app->ctx)
{
- if (!errored)
+ if (fz_caught(app->ctx) == FZ_ERROR_TRYLATER)
+ app->incomplete = 1;
+ else if (!errored)
pdfapp_warn(app, "Cannot load page");
}
@@ -564,7 +628,12 @@ static void pdfapp_recreate_annotationslist(pdfapp_t *app)
mdev = fz_new_list_device(app->ctx, app->annotations_list);
for (annot = fz_first_annot(app->doc, app->page); annot; annot = fz_next_annot(app->doc, annot))
fz_run_annot(app->doc, app->page, annot, mdev, &fz_identity, &cookie);
- if (cookie.errors)
+ if (cookie.incomplete)
+ {
+ app->incomplete = 1;
+ //pdfapp_warn(app, "Incomplete page rendering");
+ }
+ else if (cookie.errors)
{
pdfapp_warn(app, "Errors found on page");
errored = 1;
@@ -621,6 +690,23 @@ static void pdfapp_updatepage(pdfapp_t *app)
pdfapp_showpage(app, 0, 0, 1, 0);
}
+void pdfapp_reloadpage(pdfapp_t *app)
+{
+ if (app->outline_deferred == PDFAPP_OUTLINE_LOAD_NOW)
+ {
+ fz_try(app->ctx)
+ {
+ app->outline = fz_load_outline(app->doc);
+ }
+ fz_catch(app->ctx)
+ {
+ /* Ignore any error now */
+ }
+ app->outline_deferred = 0;
+ }
+ pdfapp_showpage(app, 1, 1, 1, 0);
+}
+
static void pdfapp_showpage(pdfapp_t *app, int loadpage, int drawpage, int repaint, int transition)
{
char buf[MAX_TITLE];
diff --git a/platform/x11/pdfapp.h b/platform/x11/pdfapp.h
index eabb6ca5..9c1dcd0a 100644
--- a/platform/x11/pdfapp.h
+++ b/platform/x11/pdfapp.h
@@ -43,6 +43,7 @@ extern void winprint(pdfapp_t *);
extern void winadvancetimer(pdfapp_t *, float duration);
extern void winreplacefile(char *source, char *target);
extern void wincopyfile(char *source, char *target);
+extern void winreloadpage(pdfapp_t *);
struct pdfapp_s
{
@@ -51,6 +52,7 @@ struct pdfapp_s
char *docpath;
char *doctitle;
fz_outline *outline;
+ int outline_deferred;
int pagecount;
@@ -82,6 +84,7 @@ struct pdfapp_s
fz_text_sheet *page_sheet;
fz_link *page_links;
int errored;
+ int incomplete;
/* snapback history */
int hist[256];
@@ -126,6 +129,9 @@ struct pdfapp_s
void *userdata;
fz_context *ctx;
+#ifdef HAVE_CURL
+ fz_stream *stream;
+#endif
};
void pdfapp_init(fz_context *ctx, pdfapp_t *app);
@@ -142,6 +148,7 @@ void pdfapp_onmouse(pdfapp_t *app, int x, int y, int btn, int modifiers, int sta
void pdfapp_oncopy(pdfapp_t *app, unsigned short *ucsbuf, int ucslen);
void pdfapp_onresize(pdfapp_t *app, int w, int h);
void pdfapp_gotopage(pdfapp_t *app, int number);
+void pdfapp_reloadpage(pdfapp_t *app);
void pdfapp_invert(pdfapp_t *app, const fz_rect *rect);
void pdfapp_inverthit(pdfapp_t *app);
diff --git a/platform/x11/win_main.c b/platform/x11/win_main.c
index 51dada02..6ddd171a 100644
--- a/platform/x11/win_main.c
+++ b/platform/x11/win_main.c
@@ -25,7 +25,7 @@ static HWND hwndview = NULL;
static HDC hdc;
static HBRUSH bgbrush;
static HBRUSH shbrush;
-static BITMAPINFO *dibinf;
+static BITMAPINFO *dibinf = NULL;
static HCURSOR arrowcurs, handcurs, waitcurs, caretcurs;
static LRESULT CALLBACK frameproc(HWND, UINT, WPARAM, LPARAM);
static LRESULT CALLBACK viewproc(HWND, UINT, WPARAM, LPARAM);
@@ -644,11 +644,18 @@ void winopen()
SetCursor(arrowcurs);
}
+static void
+do_close(pdfapp_t *app)
+{
+ pdfapp_close(app);
+ free(dibinf);
+}
+
void winclose(pdfapp_t *app)
{
if (pdfapp_preclose(app))
{
- pdfapp_close(app);
+ do_close(app);
exit(0);
}
}
@@ -877,6 +884,11 @@ void winreloadfile(pdfapp_t *app)
pdfapp_open(app, filename, 1);
}
+void winreloadpage(pdfapp_t *app)
+{
+ SendMessage(hwndview, WM_APP, 0, 0);
+}
+
void winopenuri(pdfapp_t *app, char *buf)
{
ShellExecuteA(hwndframe, "open", buf, 0, 0, SW_SHOWNORMAL);
@@ -1126,6 +1138,12 @@ viewproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
handlemouse(oldx, oldy, 0, 0); /* update cursor */
}
return 0;
+
+ /* We use WM_APP to trigger a reload and repaint of a page */
+ case WM_APP:
+ pdfapp_reloadpage(&gapp);
+ break;
+
}
fflush(stdout);
@@ -1199,7 +1217,7 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShow
DispatchMessage(&msg);
}
- pdfapp_close(&gapp);
+ do_close(&gapp);
fz_free_context(ctx);
return 0;
diff --git a/platform/x11/x11_main.c b/platform/x11/x11_main.c
index 0bcb98f4..0857046b 100644
--- a/platform/x11/x11_main.c
+++ b/platform/x11/x11_main.c
@@ -70,6 +70,7 @@ static Atom XA_UTF8_STRING;
static Atom WM_DELETE_WINDOW;
static Atom NET_WM_STATE;
static Atom NET_WM_STATE_FULLSCREEN;
+static Atom WM_RELOAD_PAGE;
static int x11fd;
static int xscr;
static Window xwin;
@@ -182,6 +183,7 @@ static void winopen(void)
WM_DELETE_WINDOW = XInternAtom(xdpy, "WM_DELETE_WINDOW", False);
NET_WM_STATE = XInternAtom(xdpy, "_NET_WM_STATE", False);
NET_WM_STATE_FULLSCREEN = XInternAtom(xdpy, "_NET_WM_STATE_FULLSCREEN", False);
+ WM_RELOAD_PAGE = XInternAtom(xdpy, "_WM_RELOAD_PAGE", False);
xscr = DefaultScreen(xdpy);
@@ -652,6 +654,24 @@ void onselreq(Window requestor, Atom selection, Atom target, Atom property, Time
XSendEvent(xdpy, requestor, False, 0, &nevt);
}
+void winreloadpage(pdfapp_t *app)
+{
+ XEvent xev;
+ Display *dpy = XOpenDisplay(NULL);
+
+ xev.xclient.type = ClientMessage;
+ xev.xclient.serial = 0;
+ xev.xclient.send_event = True;
+ xev.xclient.window = xwin;
+ xev.xclient.message_type = WM_RELOAD_PAGE;
+ xev.xclient.format = 32;
+ xev.xclient.data.l[0] = 0;
+ xev.xclient.data.l[1] = 0;
+ xev.xclient.data.l[2] = 0;
+ XSendEvent(dpy, xwin, 0, 0, &xev);
+ XCloseDisplay(dpy);
+}
+
void winreloadfile(pdfapp_t *app)
{
pdfapp_close(app);
@@ -886,7 +906,9 @@ int main(int argc, char **argv)
break;
case ClientMessage:
- if (xevt.xclient.format == 32 && xevt.xclient.data.l[0] == WM_DELETE_WINDOW)
+ if (xevt.xclient.message_type == WM_RELOAD_PAGE)
+ pdfapp_reloadpage(&gapp);
+ else if (xevt.xclient.format == 32 && xevt.xclient.data.l[0] == WM_DELETE_WINDOW)
closing = 1;
break;
}