From 1a746d6e8ffd9bccad8682704d03f60f1cde129e Mon Sep 17 00:00:00 2001 From: NiLuJe Date: Tue, 2 Oct 2012 02:09:43 +0200 Subject: [PATCH 01/21] Makefile tweaks for my TC --- Makefile | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 7f4f9258b..37c2aec2a 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ TTF_FONTS_DIR=$(MUPDFDIR)/fonts # set this to your ARM cross compiler: -HOST:=arm-none-linux-gnueabi +HOST:=arm-kindle-linux-gnueabi CC:=$(HOST)-gcc CXX:=$(HOST)-g++ STRIP:=$(HOST)-strip @@ -27,10 +27,14 @@ endif HOSTCC:=gcc HOSTCXX:=g++ -CFLAGS:=-O3 $(SYSROOT) -CXXFLAGS:=-O3 $(SYSROOT) +# Base CFLAGS, without arch. Will use it as-is for luajit, because its buildsystem picks up the wrong flags, possibly from my env... +BASE_CFLAGS:=-O2 -ffast-math -pipe -fomit-frame-pointer -fno-stack-protector -U_FORTIFY_SOURCE + +CFLAGS:=$(BASE_CFLAGS) +CXXFLAGS:=$(BASE_CFLAGS) -fno-use-cxa-atexit LDFLAGS:=-Wl,-O1 -Wl,--as-needed ARM_CFLAGS:=-march=armv6j -mtune=arm1136jf-s -mfpu=vfp +HOSTCFLAGS:=-O2 -march=native -ffast-math -pipe -fomit-frame-pointer # use this for debugging: #CFLAGS:=-O0 -g @@ -92,6 +96,7 @@ all:kpdfview kpdfview: kpdfview.o einkfb.o pdf.o blitbuffer.o drawcontext.o input.o util.o ft.o lfs.o mupdfimg.o $(MUPDFLIBS) $(THIRDPARTYLIBS) $(LUALIB) djvu.o $(DJVULIBS) cre.o $(CRENGINELIBS) $(CC) \ + $(CFLAGS) \ kpdfview.o \ einkfb.o \ pdf.o \ @@ -110,6 +115,7 @@ kpdfview: kpdfview.o einkfb.o pdf.o blitbuffer.o drawcontext.o input.o util.o ft cre.o \ $(CRENGINELIBS) \ $(STATICLIBSTDCPP) \ + $(LDFLAGS) \ -o kpdfview -lm -ldl -lpthread $(EMU_LDFLAGS) $(DYNAMICLIBSTDCPP) slider_watcher: slider_watcher.c @@ -158,7 +164,7 @@ clean: -rm -f *.o kpdfview slider_watcher cleanthirdparty: - -make -C $(LUADIR) clean + -make -C $(LUADIR) clean CFLAGS="" -make -C $(MUPDFDIR) build="release" clean -make -C $(CRENGINEDIR)/thirdparty/antiword clean test -d $(CRENGINEDIR)/thirdparty/chmlib && make -C $(CRENGINEDIR)/thirdparty/chmlib clean || echo warn: chmlib folder not found @@ -170,12 +176,12 @@ cleanthirdparty: -rm -f $(MUPDFDIR)/cmapdump.host $(MUPDFDIR)/fontdump.host: - make -C mupdf build="release" CC="$(HOSTCC)" $(MUPDFTARGET)/fontdump + make -C mupdf build="release" CC="$(HOSTCC)" CFLAGS="$(HOSTCFLAGS) -I../mupdf/fitz -I../mupdf/pdf" $(MUPDFTARGET)/fontdump cp -a $(MUPDFLIBDIR)/fontdump $(MUPDFDIR)/fontdump.host make -C mupdf clean $(MUPDFDIR)/cmapdump.host: - make -C mupdf build="release" CC="$(HOSTCC)" $(MUPDFTARGET)/cmapdump + make -C mupdf build="release" CC="$(HOSTCC)" CFLAGS="$(HOSTCFLAGS) -I../mupdf/fitz -I../mupdf/pdf" $(MUPDFTARGET)/cmapdump cp -a $(MUPDFLIBDIR)/cmapdump $(MUPDFDIR)/cmapdump.host make -C mupdf clean @@ -201,7 +207,7 @@ $(LUALIB): ifdef EMULATE_READER make -C $(LUADIR) else - make -C $(LUADIR) CC="$(HOSTCC)" HOST_CC="$(HOSTCC) -m32" CROSS="$(HOST)-" TARGET_FLAGS="$(SYSROOT) -DLUAJIT_NO_LOG2 -DLUAJIT_NO_EXP2" + make -C $(LUADIR) CC="$(HOSTCC)" HOST_CC="$(HOSTCC) -m32" CFLAGS="$(BASE_CFLAGS)" HOST_CFLAGS="$(BASE_CFLAGS)" TARGET_CFLAGS="$(CFLAGS)" CROSS="$(HOST)-" TARGET_FLAGS="-DLUAJIT_NO_LOG2 -DLUAJIT_NO_EXP2" V=1 endif thirdparty: $(MUPDFLIBS) $(THIRDPARTYLIBS) $(LUALIB) $(DJVULIBS) $(CRENGINELIBS) From d27f20d6968912b2c3b0aa3cffb94f35455ede93 Mon Sep 17 00:00:00 2001 From: NiLuJe Date: Tue, 2 Oct 2012 02:16:22 +0200 Subject: [PATCH 02/21] Fix input device closing, and fix lipc-wait-event handling (using popen-noshell from http://code.google.com/p/popen-noshell/) --- Makefile | 8 +- input.c | 100 +++++-- kpdfview.c | 17 +- launchpad/kpdf.sh | 4 - popen-noshell/README | 21 ++ popen-noshell/popen_noshell.c | 550 ++++++++++++++++++++++++++++++++++ popen-noshell/popen_noshell.h | 69 +++++ 7 files changed, 740 insertions(+), 29 deletions(-) create mode 100644 popen-noshell/README create mode 100644 popen-noshell/popen_noshell.c create mode 100644 popen-noshell/popen_noshell.h diff --git a/Makefile b/Makefile index 37c2aec2a..551feef2b 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,8 @@ CRENGINEDIR=$(KPVCRLIBDIR)/crengine FREETYPEDIR=$(MUPDFDIR)/thirdparty/freetype-2.4.10 LFSDIR=luafilesystem +POPENNSDIR=popen-noshell + # must point to directory with *.ttf fonts for crengine TTF_FONTS_DIR=$(MUPDFDIR)/fonts @@ -94,7 +96,7 @@ LUALIB := $(LUADIR)/src/libluajit.a all:kpdfview -kpdfview: kpdfview.o einkfb.o pdf.o blitbuffer.o drawcontext.o input.o util.o ft.o lfs.o mupdfimg.o $(MUPDFLIBS) $(THIRDPARTYLIBS) $(LUALIB) djvu.o $(DJVULIBS) cre.o $(CRENGINELIBS) +kpdfview: kpdfview.o einkfb.o pdf.o blitbuffer.o drawcontext.o popen_noshell.o input.o util.o ft.o lfs.o mupdfimg.o $(MUPDFLIBS) $(THIRDPARTYLIBS) $(LUALIB) djvu.o $(DJVULIBS) cre.o $(CRENGINELIBS) $(CC) \ $(CFLAGS) \ kpdfview.o \ @@ -102,6 +104,7 @@ kpdfview: kpdfview.o einkfb.o pdf.o blitbuffer.o drawcontext.o input.o util.o ft pdf.o \ blitbuffer.o \ drawcontext.o \ + popen_noshell.o \ input.o \ util.o \ ft.o \ @@ -136,6 +139,9 @@ cre.o: %.o: %.cpp lfs.o: $(LFSDIR)/src/lfs.c $(CC) -c $(CFLAGS) -I$(LUADIR)/src -I$(LFSDIR)/src $(LFSDIR)/src/lfs.c -o $@ +popen_noshell.o: $(POPENNSDIR)/popen_noshell.c + $(CC) -c $(CFLAGS) -I$(POPENNSDIR) $(POPENNSDIR)/popen_noshell.c -o $@ + fetchthirdparty: -rm -Rf mupdf/thirdparty test -d mupdf && (cd mupdf; git checkout .) || echo warn: mupdf folder not found diff --git a/input.c b/input.c index fffc35d61..9176659f3 100644 --- a/input.c +++ b/input.c @@ -15,7 +15,11 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ + +#include "popen-noshell/popen_noshell.h" +#include #include +#include #include #include #include @@ -31,7 +35,6 @@ #include #include -#define OUTPUT_SIZE 21 #define CODE_IN_SAVER 10000 #define CODE_OUT_SAVER 10001 #define CODE_USB_PLUG_IN 10010 @@ -41,7 +44,18 @@ #define NUM_FDS 4 int inputfds[4] = { -1, -1, -1, -1 }; -int slider_pid = -1; +pid_t slider_pid = -1; +struct popen_noshell_pass_to_pclose pclose_arg; + +void slider_handler(int sig) +{ + int status; + /* Kill lipc-wait-event properly on exit */ + if(pclose_arg.pid != 0) { + status = kill(pclose_arg.pid, SIGTERM); + printf("[SIGTERM] kill returned %d\n", status); + } +} int findFreeFdSlot() { int i; @@ -63,6 +77,8 @@ static int openInputDevice(lua_State *L) { return luaL_error(L, "no free slot for new input device <%s>", inputdevice); } + printf("Opening input device <%s> in slot %d.\n", inputdevice, fd); + if(!strcmp("slider",inputdevice)) { /* special case: the power slider */ int pipefd[2]; @@ -73,10 +89,13 @@ static int openInputDevice(lua_State *L) { return luaL_error(L, "cannot fork() slider event listener"); } if(childpid == 0) { + // Setup signal handler, to cleanup on exit + signal(SIGTERM, slider_handler); + FILE *fp; - char std_out[OUTPUT_SIZE] = ""; + char std_out[256]; + int status; struct input_event ev; - int ret; __u16 key_code = 10000; close(pipefd[0]); @@ -86,16 +105,27 @@ static int openInputDevice(lua_State *L) { ev.value = 1; /* listen power slider events */ - while(1) { - fp = popen("exec lipc-wait-event com.lab126.powerd goingToScreenSaver,outOfScreenSaver,charging,notCharging", "r"); - /* @TODO 07.06 2012 (houqp) - * plugin and out event can only be watched by: - lipc-wait-event com.lab126.hal usbPlugOut,usbPlugIn - */ - if(fgets(std_out, OUTPUT_SIZE, fp) == NULL) { - break; - } - pclose(fp); + char *exec_file = "lipc-wait-event"; + char *arg1 = "-m"; // Hang for ever, don't exit on the first one, we're in a dedicated child, and we'll be closed on exit, so we don't care + char *arg2 = "-s"; + char *arg3 = "0"; + char *arg4 = "com.lab126.powerd"; + char *arg5 = "goingToScreenSaver,outOfScreenSaver,charging,notCharging"; + char *arg6 = (char *) NULL; + char *argv[] = {exec_file, arg1, arg2, arg3, arg4, arg5, arg6}; + /* @TODO 07.06 2012 (houqp) + * plugin and out event can only be watched by: + lipc-wait-event com.lab126.hal usbPlugOut,usbPlugIn + */ + + fp = popen_noshell(exec_file, (const char * const *)argv, "r", &pclose_arg, 0); + if (!fp) { + err(EXIT_FAILURE, "popen_noshell()"); + } + + printf("PID of our child is: %d (ours: %d)\n", (int)pclose_arg.pid, (int)getpid()); + + while(fgets(std_out, sizeof(std_out)-1, fp)) { if(std_out[0] == 'g') { ev.code = CODE_IN_SAVER; } else if(std_out[0] == 'o') { @@ -116,14 +146,32 @@ static int openInputDevice(lua_State *L) { /* generate event */ if(write(pipefd[1], &ev, sizeof(struct input_event)) == -1) { - break; + printf("Failed to generate event.\n"); } } - exit(0); /* cannot be reached?! */ + + status = pclose_noshell(&pclose_arg); + if (status == -1) { + err(EXIT_FAILURE, "pclose_noshell()"); + } else { + printf("Power slider event listener child exited with status %d.\n", status); + + if WIFEXITED(status) { + printf("Child exited normally with status: %d.\n", WEXITSTATUS(status)); + } + if WIFSIGNALED(status) { + printf("Child terminated by signal: %d.\n", WTERMSIG(status)); + } + } + + // We're done, go away :). + _exit(EXIT_SUCCESS); } else { + printf("Slider pipe close in slot %d [inputfd[fd]: %d].\n", fd, pipefd[0]); close(pipefd[1]); inputfds[fd] = pipefd[0]; slider_pid = childpid; + printf("slider_pid is: %d\n", (int)slider_pid); } } else { inputfds[fd] = open(inputdevice, O_RDONLY | O_NONBLOCK, 0); @@ -144,18 +192,26 @@ static int openInputDevice(lua_State *L) { } static int closeInputDevices(lua_State *L) { + printf("closeInputDevices(): BEGIN\n"); #ifndef EMULATE_READER - int i; + int i, ret; for(i=0; i usecs/1000) diff --git a/kpdfview.c b/kpdfview.c index d0003ae7f..08ec30283 100644 --- a/kpdfview.c +++ b/kpdfview.c @@ -15,9 +15,13 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ +#include +#include +#include +#include #include #include -#include +#include #include #include @@ -39,7 +43,7 @@ lua_State *L; int main(int argc, char **argv) { - int i, err; + int i; if(argc < 2) { fprintf(stderr, "needs config file as first argument.\n"); @@ -79,6 +83,15 @@ int main(int argc, char **argv) { } } + /* Make popen_noshell & valgrind happy */ + if (fflush(stdout) != 0) + err(EXIT_FAILURE, "fflush(stdout)"); + if (fflush(stderr) != 0) + err(EXIT_FAILURE, "fflush(stderr)"); + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + return 0; } diff --git a/launchpad/kpdf.sh b/launchpad/kpdf.sh index c1f81c700..3f5cffc43 100755 --- a/launchpad/kpdf.sh +++ b/launchpad/kpdf.sh @@ -31,7 +31,3 @@ fi # always try to continue cvm killall -cont cvm || /etc/init.d/framework start - -# cleanup hanging process -killall lipc-wait-event - diff --git a/popen-noshell/README b/popen-noshell/README new file mode 100644 index 000000000..7b829d130 --- /dev/null +++ b/popen-noshell/README @@ -0,0 +1,21 @@ +/* + * popen_noshell: A faster implementation of popen() and system() for Linux. + * Copyright (c) 2009 Ivan Zahariev (famzah) + * Version: 1.0 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; under version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +This is the faster popen() alternative implementation. + +Taken from http://code.google.com/p/popen-noshell/ diff --git a/popen-noshell/popen_noshell.c b/popen-noshell/popen_noshell.c new file mode 100644 index 000000000..ed37d07d5 --- /dev/null +++ b/popen-noshell/popen_noshell.c @@ -0,0 +1,550 @@ +/* + * popen_noshell: A faster implementation of popen() and system() for Linux. + * Copyright (c) 2009 Ivan Zahariev (famzah) + * Version: 1.0 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; under version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include "popen_noshell.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Wish-list: + * 1) Make the "ignore_stderr" parameter a mode - ignore, leave unchanged, redirect to stdout (the last is not implemented yet) + * 2) Code a faster system(): system_noshell(), system_noshell_compat() + */ + +//#define POPEN_NOSHELL_DEBUG + +// because of C++, we can't call err() or errx() within the child, because they call exit(), and _exit() is what must be called; so we wrap +#define _ERR(EVAL, FMT, ...) \ + { \ + warn(FMT, ##__VA_ARGS__); \ + _exit(EVAL); \ + } +#define _ERRX(EVAL, FMT, ...) \ + { \ + warnx(FMT, ##__VA_ARGS__); \ + _exit(EVAL); \ + } + +int _popen_noshell_fork_mode = POPEN_NOSHELL_MODE_CLONE; + +void popen_noshell_set_fork_mode(int mode) { // see "popen_noshell.h" POPEN_NOSHELL_MODE_* constants + _popen_noshell_fork_mode = mode; +} + +int popen_noshell_reopen_fd_to_dev_null(int fd) { + int dev_null_fd; + + dev_null_fd = open("/dev/null", O_RDWR); + if (dev_null_fd < 0) return -1; + + if (close(fd) != 0) { + return -1; + } + if (dup2(dev_null_fd, fd) == -1) { + return -1; + } + if (close(dev_null_fd) != 0) { + return -1; + } + + return 0; +} + +int _popen_noshell_close_and_dup(int pipefd[2], int closed_pipefd, int target_fd) { + int dupped_pipefd; + + dupped_pipefd = (closed_pipefd == 0 ? 1 : 0); // get the FD of the other end of the pipe + + if (close(pipefd[closed_pipefd]) != 0) { + return -1; + } + + if (close(target_fd) != 0) { + return -1; + } + if (dup2(pipefd[dupped_pipefd], target_fd) == -1) { + return -1; + } + if (close(pipefd[dupped_pipefd]) != 0) { + return -1; + } + + return 0; +} + +void _pclose_noshell_free_clone_arg_memory(struct popen_noshell_clone_arg *func_args) { + char **cmd_argv; + + free((char *)func_args->file); + cmd_argv = (char **)func_args->argv; + while (*cmd_argv) { + free(*cmd_argv); + ++cmd_argv; + } + free((char **)func_args->argv); + free(func_args); +} + +void _popen_noshell_child_process( + /* We need the pointer *arg_ptr only to free whatever we reference if exec() fails and we were fork()'ed (thus memory was copied), + * not clone()'d */ + struct popen_noshell_clone_arg *arg_ptr, /* NULL if we were called by pure fork() (not because of Valgrind) */ + int pipefd_0, int pipefd_1, int read_pipe, int ignore_stderr, const char *file, const char * const *argv) { + + int closed_child_fd; + int closed_pipe_fd; + int dupped_child_fd; + int pipefd[2] = {pipefd_0, pipefd_1}; + + if (ignore_stderr) { /* ignore STDERR completely? */ + if (popen_noshell_reopen_fd_to_dev_null(STDERR_FILENO) != 0) _ERR(255, "popen_noshell_reopen_fd_to_dev_null(%d)", STDERR_FILENO); + } + + if (read_pipe) { + closed_child_fd = STDIN_FILENO; /* re-open STDIN to /dev/null */ + closed_pipe_fd = 0; /* close read end of pipe */ + dupped_child_fd = STDOUT_FILENO; /* dup the other pipe end to STDOUT */ + } else { + closed_child_fd = STDOUT_FILENO; /* ignore STDOUT completely */ + closed_pipe_fd = 1; /* close write end of pipe */ + dupped_child_fd = STDIN_FILENO; /* dup the other pipe end to STDIN */ + } + if (popen_noshell_reopen_fd_to_dev_null(closed_child_fd) != 0) { + _ERR(255, "popen_noshell_reopen_fd_to_dev_null(%d)", closed_child_fd); + } + if (_popen_noshell_close_and_dup(pipefd, closed_pipe_fd, dupped_child_fd) != 0) { + _ERR(255, "_popen_noshell_close_and_dup(%d ,%d)", closed_pipe_fd, dupped_child_fd); + } + + execvp(file, (char * const *)argv); + + /* if we are here, exec() failed */ + + warn("exec(\"%s\") inside the child", file); + +#ifdef POPEN_NOSHELL_VALGRIND_DEBUG + if (arg_ptr) { /* not NULL if we were called by clone() */ + /* but Valgrind does not support clone(), so we were actually called by fork(), thus memory was copied... */ + /* free this copied memory; if it was not Valgrind, this memory would have been shared and would belong to the parent! */ + _pclose_noshell_free_clone_arg_memory(arg_ptr); + } +#endif + + if (fflush(stdout) != 0) _ERR(255, "fflush(stdout)"); + if (fflush(stderr) != 0) _ERR(255, "fflush(stderr)"); + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + + _exit(255); // call _exit() and not exit(), or you'll have troubles in C++ +} + +int popen_noshell_child_process_by_clone(void *raw_arg) { + struct popen_noshell_clone_arg *arg; + + arg = (struct popen_noshell_clone_arg *)raw_arg; + _popen_noshell_child_process(arg, arg->pipefd_0, arg->pipefd_1, arg->read_pipe, arg->ignore_stderr, arg->file, arg->argv); + + return 0; +} + +char ** popen_noshell_copy_argv(const char * const *argv_orig) { + int size = 1; /* there is at least one NULL element */ + char **argv; + char **argv_new; + int n; + + argv = (char **) argv_orig; + while (*argv) { + ++size; + ++argv; + } + + argv_new = (char **)malloc(sizeof(char *) * size); + if (!argv_new) return NULL; + + argv = (char **)argv_orig; + n = 0; + while (*argv) { + argv_new[n] = strdup(*argv); + if (!argv_new[n]) return NULL; + ++argv; + ++n; + } + argv_new[n] = (char *)NULL; + + return argv_new; +} + +/* + * Similar to vfork() and threading. + * Starts a process which behaves like a thread (shares global variables in memory with the parent) but + * has a different PID and can call exec(), unlike traditional threads which are not allowed to call exec(). + * + * This fork function is very resource-light because it does not copy any memory from the parent, but shares it. + * + * Like standard threads, you have to provide a start function *fn and arguments to it *arg. The life of the + * new vmfork()'ed process starts from this function. + * + * After you have reaped the child via waitpid(), you have to free() the memory at "*memory_to_free_on_child_exit". + * + * When the *fn function returns, the child process terminates. The integer returned by *fn is the exit code for the child process. + * The child process may also terminate explicitly by calling exit(2) or after receiving a fatal signal. + * + * Returns -1 on error. On success returns the PID of the newly created child. + */ +pid_t popen_noshell_vmfork(int (*fn)(void *), void *arg, void **memory_to_free_on_child_exit) { + void *stack, *stack_aligned; + pid_t pid; + + stack = malloc(POPEN_NOSHELL_STACK_SIZE + 15); + if (!stack) return -1; + *memory_to_free_on_child_exit = stack; + + /* + * On all supported Linux platforms the stack grows down, except for HP-PARISC. + * You can grep the kernel source for "STACK_GROWSUP", in order to get this information. + */ + // stack grows down, set pointer at the end of the block + stack_aligned = (void *) ((char * /*byte*/)stack + POPEN_NOSHELL_STACK_SIZE/*bytes*/); + + /* + * On all supported platforms by GNU libc, the stack is aligned to 16 bytes, except for the SuperH platform which is aligned to 8 bytes. + * You can grep the glibc source for "STACK_ALIGN", in order to get this information. + */ + stack_aligned = (void *) ( ((uintptr_t)stack_aligned+15) & ~ 0x0F ); // align to 16 bytes + + /* + * Maybe we could have used posix_memalign() here... + * Our implementation seems a bit more portable though - I've read somewhere that posix_memalign() is not supported on all platforms. + * The above malloc() + align implementation is taken from: + * http://stackoverflow.com/questions/227897/solve-the-memory-alignment-in-c-interview-question-that-stumped-me + */ + +#ifndef POPEN_NOSHELL_VALGRIND_DEBUG + pid = clone(fn, stack_aligned, CLONE_VM | SIGCHLD, arg); +#else + pid = fork(); // Valgrind does not support arbitrary clone() calls, so we use fork for the tests +#endif + if (pid == -1) return -1; + + if (pid == 0) { // child +#ifdef POPEN_NOSHELL_VALGRIND_DEBUG + free(stack); // this is a copy because of the fork(), we are not using it at all + _exit(fn(arg)); // if we used fork() because of Valgrind, invoke the child function manually; always use _exit() +#endif + errx(EXIT_FAILURE, "This must never happen"); + } // child life ends here, for sure + + return pid; +} + +/* + * Pipe stream to or from process. Similar to popen(), only much faster. + * + * "file" is the command to be executed. It is searched within the PATH environment variable. + * "argv[]" is an array to "char *" elements which are passed as command-line arguments. + * Note: The first element must be the same string as "file". + * Note: The last element must be a (char *)NULL terminating element. + * "type" specifies if we are reading from the STDOUT or writing to the STDIN of the executed command "file". Use "r" for reading, "w" for writing. + * "pid" is a pointer to an interger. The PID of the child process is stored there. + * "ignore_stderr" has the following meaning: + * 0: leave STDERR of the child process attached to the current STDERR of the parent process + * 1: ignore the STDERR of the child process + * + * This function is not very sustainable on failures. This means that if it fails for some reason (out of memory, no such executable, etc.), + * you are probably in trouble, because the function allocated some memory or file descriptors and never released them. + * Normally, this function should never fail. + * + * Returns NULL on any error, "errno" is set appropriately. + * On success, a stream pointer is returned. + * When you are done working with the stream, you have to close it by calling pclose_noshell(), or else you will leave zombie processes. + */ +FILE *popen_noshell(const char *file, const char * const *argv, const char *type, struct popen_noshell_pass_to_pclose *pclose_arg, int ignore_stderr) { + int read_pipe; + int pipefd[2]; // 0 -> READ, 1 -> WRITE ends + pid_t pid; + FILE *fp; + + memset(pclose_arg, 0, sizeof(struct popen_noshell_pass_to_pclose)); + + if (strcmp(type, "r") == 0) { + read_pipe = 1; + } else if (strcmp(type, "w") == 0) { + read_pipe = 0; + } else { + errno = EINVAL; + return NULL; + } + + if (pipe(pipefd) != 0) return NULL; + + if (_popen_noshell_fork_mode) { // use fork() + + pid = fork(); + if (pid == -1) return NULL; + if (pid == 0) { + _popen_noshell_child_process(NULL, pipefd[0], pipefd[1], read_pipe, ignore_stderr, file, argv); + errx(EXIT_FAILURE, "This must never happen"); + } // child life ends here, for sure + + } else { // use clone() + + struct popen_noshell_clone_arg *arg = NULL; + + arg = (struct popen_noshell_clone_arg*) malloc(sizeof(struct popen_noshell_clone_arg)); + if (!arg) return NULL; + + /* Copy memory structures, so that nobody can free() our memory while we use it in the child! */ + arg->pipefd_0 = pipefd[0]; + arg->pipefd_1 = pipefd[1]; + arg->read_pipe = read_pipe; + arg->ignore_stderr = ignore_stderr; + arg->file = strdup(file); + if (!arg->file) return NULL; + arg->argv = (const char * const *)popen_noshell_copy_argv(argv); + if (!arg->argv) return NULL; + + pclose_arg->free_clone_mem = 1; + pclose_arg->func_args = arg; + pclose_arg->stack = NULL; // we will populate it below + + pid = popen_noshell_vmfork(&popen_noshell_child_process_by_clone, arg, &(pclose_arg->stack)); + if (pid == -1) return NULL; + + } // done: using clone() + + /* parent process */ + + if (read_pipe) { + if (close(pipefd[1/*write*/]) != 0) return NULL; + fp = fdopen(pipefd[0/*read*/], "r"); + } else { // write_pipe + if (close(pipefd[0/*read*/]) != 0) return NULL; + fp = fdopen(pipefd[1/*write*/], "w"); + } + if (fp == NULL) { + return NULL; // fdopen() failed + } + + pclose_arg->fp = fp; + pclose_arg->pid = pid; + + return fp; // we should never end up here +} + +int popen_noshell_add_ptr_to_argv(char ***argv, int *count, char *start) { + *count += 1; + *argv = (char **) realloc(*argv, *count * sizeof(char **)); + if (*argv == NULL) { + return -1; + } + *(*argv + *count - 1) = start; + return 0; +} + +int _popen_noshell_add_token(char ***argv, int *count, char *start, char *command, int *j) { + if (start != NULL && command + *j - 1 - start >= 0) { + command[*j] = '\0'; // terminate the token in memory + *j += 1; +#ifdef POPEN_NOSHELL_DEBUG + printf("Token: %s\n", start); +#endif + if (popen_noshell_add_ptr_to_argv(argv, count, start) != 0) { + return -1; + } + } + return 0; +} + +#define _popen_noshell_split_return_NULL { if (argv != NULL) free(argv); if (command != NULL) free(command); return NULL; } +char ** popen_noshell_split_command_to_argv(const char *command_original, char **free_this_buf) { + char *command; + size_t i, len; + char *start = NULL; + char c; + char **argv = NULL; + int count = 0; + const char _popen_bash_meta_characters[] = "!\\$`\n|&;()<>"; + int in_sq = 0; + int in_dq = 0; + int j = 0; +#ifdef POPEN_NOSHELL_DEBUG + char **tmp; +#endif + + command = (char *)calloc(strlen(command_original) + 1, sizeof(char)); + if (!command) _popen_noshell_split_return_NULL; + + *free_this_buf = command; + + len = strlen(command_original); // get the original length + j = 0; + for (i = 0; i < len; ++i) { + if (!start) start = command + j; + c = command_original[i]; + + if (index(_popen_bash_meta_characters, c) != NULL) { + errno = EINVAL; + _popen_noshell_split_return_NULL; + } + + if (c == ' ' || c == '\t') { + if (in_sq || in_dq) { + command[j++] = c; + continue; + } + + // new token + if (_popen_noshell_add_token(&argv, &count, start, command, &j) != 0) { + _popen_noshell_split_return_NULL; + } + start = NULL; + continue; + } + + if (c == '\'' && !in_dq) { + in_sq = !in_sq; + continue; + } + if (c == '"' && !in_sq) { + in_dq = !in_dq; + continue; + } + + command[j++] = c; + } + if (in_sq || in_dq) { // unmatched single/double quote + errno = EINVAL; + _popen_noshell_split_return_NULL; + } + + if (_popen_noshell_add_token(&argv, &count, start, command, &j) != 0) { + _popen_noshell_split_return_NULL; + } + + if (count == 0) { + errno = EINVAL; + _popen_noshell_split_return_NULL; + } + + if (popen_noshell_add_ptr_to_argv(&argv, &count, NULL) != 0) { // NULL-terminate the list + _popen_noshell_split_return_NULL; + } + +#ifdef POPEN_NOSHELL_DEBUG + tmp = argv; + while (*tmp) { + printf("ARGV: |%s|\n", *tmp); + ++tmp; + } +#endif + + return argv; + + /* Example test strings: + "a'zz bb edd" + " abc ff " + " abc ff" + "' abc ff ' " + "" + " " + " '" + "ab\\c" + "ls -la /proc/self/fd 'z' 'ab'g'z\" zz' \" abc'd\" ' ab\"c def '" + */ +} + +/* + * Pipe stream to or from process. Similar to popen(), only much faster. + * + * This is simpler than popen_noshell() but is more INSECURE. + * Since shells have very complicated expansion, quoting and word splitting algorithms, we do NOT try to re-implement them here. + * This function does NOT support any special characters. It will immediately return an error if such symbols are encountered in "command". + * The "command" is split only by space and tab delimiters. The special symbols are pre-defined in _popen_bash_meta_characters[]. + * The only special characters supported are single and double quotes. You can enclose arguments in quotes and they should be splitted correctly. + * + * If possible, use popen_noshell() because of its better security. + * + * "command" is the command and its arguments to be executed. The command is searched within the PATH environment variable. + * The whole "command" string is parsed and splitted, so that it can be directly given to popen_noshell() and resp. to exec(). + * This parsing is very simple and may contain bugs (see above). If possible, use popen_noshell() directly. + * "type" specifies if we are reading from the STDOUT or writing to the STDIN of the executed command. Use "r" for reading, "w" for writing. + * "pid" is a pointer to an interger. The PID of the child process is stored there. + * + * Returns NULL on any error, "errno" is set appropriately. + * On success, a stream pointer is returned. + * When you are done working with the stream, you have to close it by calling pclose_noshell(), or else you will leave zombie processes. + */ +FILE *popen_noshell_compat(const char *command, const char *type, struct popen_noshell_pass_to_pclose *pclose_arg) { + char **argv; + FILE *fp; + char *to_free; + + argv = popen_noshell_split_command_to_argv(command, &to_free); + if (!argv) { + if (to_free) free(to_free); + return NULL; + } + + fp = popen_noshell(argv[0], (const char * const *)argv, type, pclose_arg, 0); + + free(to_free); + free(argv); + + return fp; +} + +/* + * You have to call this function after you have done working with the FILE pointer "fp" returned by popen_noshell() or by popen_noshell_compat(). + * + * Returns -1 on any error, "errno" is set appropriately. + * Returns the "status" of the child process as returned by waitpid(). + */ +int pclose_noshell(struct popen_noshell_pass_to_pclose *arg) { + int status; + + if (fclose(arg->fp) != 0) { + return -1; + } + + if (waitpid(arg->pid, &status, 0) != arg->pid) { + return -1; + } + + if (arg->free_clone_mem) { + free(arg->stack); + _pclose_noshell_free_clone_arg_memory(arg->func_args); + } + + return status; +} diff --git a/popen-noshell/popen_noshell.h b/popen-noshell/popen_noshell.h new file mode 100644 index 000000000..a2eeb3c6e --- /dev/null +++ b/popen-noshell/popen_noshell.h @@ -0,0 +1,69 @@ +/* + * popen_noshell: A faster implementation of popen() and system() for Linux. + * Copyright (c) 2009 Ivan Zahariev (famzah) + * Version: 1.0 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; under version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#ifndef POPEN_NOSHELL_H +#define POPEN_NOSHELL_H + +#include +#include +#include + +/* stack for the child process before it does exec() */ +#define POPEN_NOSHELL_STACK_SIZE 8*1024*1024 /* currently most Linux distros set this to 8 MBytes */ + +/* constants to use with popen_noshell_set_fork_mode() */ +#define POPEN_NOSHELL_MODE_CLONE 0 /* default, faster */ +#define POPEN_NOSHELL_MODE_FORK 1 /* slower */ + +struct popen_noshell_clone_arg { + int pipefd_0; + int pipefd_1; + int read_pipe; + int ignore_stderr; + const char *file; + const char * const *argv; +}; + +struct popen_noshell_pass_to_pclose { + FILE *fp; + pid_t pid; + int free_clone_mem; + void *stack; + struct popen_noshell_clone_arg *func_args; +}; + +/*************************** + * PUBLIC FUNCTIONS FOLLOW * + ***************************/ + +/* this is the native function call */ +FILE *popen_noshell(const char *file, const char * const *argv, const char *type, struct popen_noshell_pass_to_pclose *pclose_arg, int ignore_stderr); + +/* more insecure, but more compatible with popen() */ +FILE *popen_noshell_compat(const char *command, const char *type, struct popen_noshell_pass_to_pclose *pclose_arg); + +/* call this when you have finished reading and writing from/to the child process */ +int pclose_noshell(struct popen_noshell_pass_to_pclose *arg); /* the pclose() equivalent */ + +/* this is the innovative faster vmfork() which shares memory with the parent and is very resource-light; see the source code for documentation */ +pid_t popen_noshell_vmfork(int (*fn)(void *), void *arg, void **memory_to_free_on_child_exit); + +/* used only for benchmarking purposes */ +void popen_noshell_set_fork_mode(int mode); + +#endif From 9fab02e33f8fd4f819b25d6a15e7164ef1343b99 Mon Sep 17 00:00:00 2001 From: NiLuJe Date: Tue, 2 Oct 2012 02:25:08 +0200 Subject: [PATCH 03/21] Kill debug printf --- input.c | 30 +++++++----------------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/input.c b/input.c index 9176659f3..f8077f1f8 100644 --- a/input.c +++ b/input.c @@ -49,11 +49,9 @@ struct popen_noshell_pass_to_pclose pclose_arg; void slider_handler(int sig) { - int status; /* Kill lipc-wait-event properly on exit */ if(pclose_arg.pid != 0) { - status = kill(pclose_arg.pid, SIGTERM); - printf("[SIGTERM] kill returned %d\n", status); + kill(pclose_arg.pid, SIGTERM); } } @@ -77,8 +75,6 @@ static int openInputDevice(lua_State *L) { return luaL_error(L, "no free slot for new input device <%s>", inputdevice); } - printf("Opening input device <%s> in slot %d.\n", inputdevice, fd); - if(!strcmp("slider",inputdevice)) { /* special case: the power slider */ int pipefd[2]; @@ -106,7 +102,7 @@ static int openInputDevice(lua_State *L) { /* listen power slider events */ char *exec_file = "lipc-wait-event"; - char *arg1 = "-m"; // Hang for ever, don't exit on the first one, we're in a dedicated child, and we'll be closed on exit, so we don't care + char *arg1 = "-m"; // Hang for ever, don't exit on the first one: we're in a dedicated child, and we'll be killed/closed on exit, so we don't care char *arg2 = "-s"; char *arg3 = "0"; char *arg4 = "com.lab126.powerd"; @@ -123,8 +119,6 @@ static int openInputDevice(lua_State *L) { err(EXIT_FAILURE, "popen_noshell()"); } - printf("PID of our child is: %d (ours: %d)\n", (int)pclose_arg.pid, (int)getpid()); - while(fgets(std_out, sizeof(std_out)-1, fp)) { if(std_out[0] == 'g') { ev.code = CODE_IN_SAVER; @@ -167,11 +161,9 @@ static int openInputDevice(lua_State *L) { // We're done, go away :). _exit(EXIT_SUCCESS); } else { - printf("Slider pipe close in slot %d [inputfd[fd]: %d].\n", fd, pipefd[0]); close(pipefd[1]); inputfds[fd] = pipefd[0]; slider_pid = childpid; - printf("slider_pid is: %d\n", (int)slider_pid); } } else { inputfds[fd] = open(inputdevice, O_RDONLY | O_NONBLOCK, 0); @@ -192,26 +184,18 @@ static int openInputDevice(lua_State *L) { } static int closeInputDevices(lua_State *L) { - printf("closeInputDevices(): BEGIN\n"); #ifndef EMULATE_READER - int i, ret; + int i; for(i=0; i Date: Tue, 2 Oct 2012 03:11:16 +0200 Subject: [PATCH 04/21] Update the standalone testcase, too. --- .gitignore | 1 + Makefile | 7 ++++-- input.c | 1 - slider_watcher.c | 64 ++++++++++++++++++++++++++++++++++++------------ 4 files changed, 55 insertions(+), 18 deletions(-) diff --git a/.gitignore b/.gitignore index 7dbe43999..b498daf97 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ crash.log data fonts kpdfview +slider_watcher *.o kindlepdfviewer-*.zip diff --git a/Makefile b/Makefile index 551feef2b..586e5cf1e 100644 --- a/Makefile +++ b/Makefile @@ -121,8 +121,11 @@ kpdfview: kpdfview.o einkfb.o pdf.o blitbuffer.o drawcontext.o popen_noshell.o i $(LDFLAGS) \ -o kpdfview -lm -ldl -lpthread $(EMU_LDFLAGS) $(DYNAMICLIBSTDCPP) -slider_watcher: slider_watcher.c - $(CC) $(CFLAGS) $< -o $@ +slider_watcher.o: %.o: %.c + $(CC) -c $(CFLAGS) $< -o $@ + +slider_watcher: popen_noshell.o slider_watcher.o + $(CC) $(CFLAGS) popen_noshell.o slider_watcher.o -o $@ ft.o: %.o: %.c $(THIRDPARTYLIBS) $(CC) -c $(KPDFREADER_CFLAGS) -I$(FREETYPEDIR)/include -I$(MUPDFDIR)/fitz $< -o $@ diff --git a/input.c b/input.c index f8077f1f8..126f94a9c 100644 --- a/input.c +++ b/input.c @@ -33,7 +33,6 @@ #include "input.h" #include #include -#include #define CODE_IN_SAVER 10000 #define CODE_OUT_SAVER 10001 diff --git a/slider_watcher.c b/slider_watcher.c index 6a4e26e15..cca6827a5 100644 --- a/slider_watcher.c +++ b/slider_watcher.c @@ -16,8 +16,11 @@ along with this program. If not, see . */ -#include +#include "popen-noshell/popen_noshell.h" +#include #include +#include +#include #include #include #include @@ -25,18 +28,19 @@ #include #include #include +#include -#define OUTPUT_SIZE 21 -#define EVENT_PIPE "/tmp/event_slider" #define CODE_IN_SAVER 10000 #define CODE_OUT_SAVER 10001 int main ( int argc, char *argv[] ) { - int fd, ret; + int fd; FILE *fp; - char std_out[OUTPUT_SIZE] = ""; + char std_out[256]; + int status; + struct popen_noshell_pass_to_pclose pclose_arg; struct input_event ev; __u16 key_code = 10000; @@ -51,7 +55,7 @@ main ( int argc, char *argv[] ) /* open npipe for writing */ fd = open(argv[1], O_RDWR | O_NONBLOCK); if(fd < 0) { - printf("Open %s falied: %s\n", argv[1], strerror(errno)); + printf("Open %s failed: %s\n", argv[1], strerror(errno)); exit(EXIT_FAILURE); } @@ -60,15 +64,25 @@ main ( int argc, char *argv[] ) ev.code = key_code; ev.value = 1; - while(1) { - /* listen power slider events */ - memset(std_out, 0, OUTPUT_SIZE); - fp = popen("lipc-wait-event -s 0 com.lab126.powerd goingToScreenSaver,outOfScreenSaver", "r"); - ret = fread(std_out, OUTPUT_SIZE, 1, fp); - pclose(fp); + /* listen power slider events */ + char *exec_file = "lipc-wait-event"; + char *arg1 = "-m"; + char *arg2 = "-s"; + char *arg3 = "0"; + char *arg4 = "com.lab126.powerd"; + char *arg5 = "goingToScreenSaver,outOfScreenSaver"; + char *arg6 = (char *) NULL; + char *chargv[] = {exec_file, arg1, arg2, arg3, arg4, arg5, arg6}; + + fp = popen_noshell(exec_file, (const char * const *)chargv, "r", &pclose_arg, 0); + if (!fp) { + err(EXIT_FAILURE, "popen_noshell()"); + } + + while(fgets(std_out, sizeof(std_out)-1, fp)) { + + /* printf("Got line: %s", std_out); */ - /* fill event struct */ - gettimeofday(&ev.time, NULL); if(std_out[0] == 'g') { ev.code = CODE_IN_SAVER; } else if(std_out[0] == 'o') { @@ -77,9 +91,29 @@ main ( int argc, char *argv[] ) printf("Unrecognized event.\n"); exit(EXIT_FAILURE); } + /* fill event struct */ + gettimeofday(&ev.time, NULL); + + /* printf("Send event %d\n", ev.code); */ /* generate event */ - ret = write(fd, &ev, sizeof(struct input_event)); + if(write(fd, &ev, sizeof(struct input_event)) == -1) { + printf("Failed to generate event.\n"); + } + } + + status = pclose_noshell(&pclose_arg); + if (status == -1) { + err(EXIT_FAILURE, "pclose_noshell()"); + } else { + printf("Power slider event listener child exited with status %d.\n", status); + + if WIFEXITED(status) { + printf("Child exited normally with status: %d.\n", WEXITSTATUS(status)); + } + if WIFSIGNALED(status) { + printf("Child terminated by signal: %d.\n", WTERMSIG(status)); + } } close(fd); From 44f69b55cd8dced512debf370a74479f94bbc8de Mon Sep 17 00:00:00 2001 From: NiLuJe Date: Tue, 2 Oct 2012 03:29:31 +0200 Subject: [PATCH 05/21] Revert Makefile changes potentially unsuitable for upstream --- Makefile | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index 586e5cf1e..76b4c97b7 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ TTF_FONTS_DIR=$(MUPDFDIR)/fonts # set this to your ARM cross compiler: -HOST:=arm-kindle-linux-gnueabi +HOST:=arm-none-linux-gnueabi CC:=$(HOST)-gcc CXX:=$(HOST)-g++ STRIP:=$(HOST)-strip @@ -29,14 +29,10 @@ endif HOSTCC:=gcc HOSTCXX:=g++ -# Base CFLAGS, without arch. Will use it as-is for luajit, because its buildsystem picks up the wrong flags, possibly from my env... -BASE_CFLAGS:=-O2 -ffast-math -pipe -fomit-frame-pointer -fno-stack-protector -U_FORTIFY_SOURCE - -CFLAGS:=$(BASE_CFLAGS) -CXXFLAGS:=$(BASE_CFLAGS) -fno-use-cxa-atexit +CFLAGS:=-O3 $(SYSROOT) +CXXFLAGS:=-O3 $(SYSROOT) LDFLAGS:=-Wl,-O1 -Wl,--as-needed ARM_CFLAGS:=-march=armv6j -mtune=arm1136jf-s -mfpu=vfp -HOSTCFLAGS:=-O2 -march=native -ffast-math -pipe -fomit-frame-pointer # use this for debugging: #CFLAGS:=-O0 -g @@ -173,7 +169,7 @@ clean: -rm -f *.o kpdfview slider_watcher cleanthirdparty: - -make -C $(LUADIR) clean CFLAGS="" + -make -C $(LUADIR) clean -make -C $(MUPDFDIR) build="release" clean -make -C $(CRENGINEDIR)/thirdparty/antiword clean test -d $(CRENGINEDIR)/thirdparty/chmlib && make -C $(CRENGINEDIR)/thirdparty/chmlib clean || echo warn: chmlib folder not found @@ -185,12 +181,12 @@ cleanthirdparty: -rm -f $(MUPDFDIR)/cmapdump.host $(MUPDFDIR)/fontdump.host: - make -C mupdf build="release" CC="$(HOSTCC)" CFLAGS="$(HOSTCFLAGS) -I../mupdf/fitz -I../mupdf/pdf" $(MUPDFTARGET)/fontdump + make -C mupdf build="release" CC="$(HOSTCC)" $(MUPDFTARGET)/fontdump cp -a $(MUPDFLIBDIR)/fontdump $(MUPDFDIR)/fontdump.host make -C mupdf clean $(MUPDFDIR)/cmapdump.host: - make -C mupdf build="release" CC="$(HOSTCC)" CFLAGS="$(HOSTCFLAGS) -I../mupdf/fitz -I../mupdf/pdf" $(MUPDFTARGET)/cmapdump + make -C mupdf build="release" CC="$(HOSTCC)" $(MUPDFTARGET)/cmapdump cp -a $(MUPDFLIBDIR)/cmapdump $(MUPDFDIR)/cmapdump.host make -C mupdf clean @@ -216,7 +212,7 @@ $(LUALIB): ifdef EMULATE_READER make -C $(LUADIR) else - make -C $(LUADIR) CC="$(HOSTCC)" HOST_CC="$(HOSTCC) -m32" CFLAGS="$(BASE_CFLAGS)" HOST_CFLAGS="$(BASE_CFLAGS)" TARGET_CFLAGS="$(CFLAGS)" CROSS="$(HOST)-" TARGET_FLAGS="-DLUAJIT_NO_LOG2 -DLUAJIT_NO_EXP2" V=1 + make -C $(LUADIR) CC="$(HOSTCC)" HOST_CC="$(HOSTCC) -m32" CROSS="$(HOST)-" TARGET_FLAGS="$(SYSROOT) -DLUAJIT_NO_LOG2 -DLUAJIT_NO_EXP2" endif thirdparty: $(MUPDFLIBS) $(THIRDPARTYLIBS) $(LUALIB) $(DJVULIBS) $(CRENGINELIBS) From 8e7f4ba989545445977588822b0be1e3da154d4d Mon Sep 17 00:00:00 2001 From: NiLuJe Date: Tue, 2 Oct 2012 23:10:55 +0200 Subject: [PATCH 06/21] Add a small makefile to build popen_noshell as a static lib --- .gitignore | 3 +++ popen-noshell/Makefile | 15 +++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 popen-noshell/Makefile diff --git a/.gitignore b/.gitignore index b498daf97..c806532dd 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,6 @@ kpvcrlib/CMakeCache.txt kpvcrlib/CMakeFiles/ kpvcrlib/cmake_install.cmake kpvcrlib/Makefile + +popen-noshell/libpopen_noshell.a +popen-noshell/*.o diff --git a/popen-noshell/Makefile b/popen-noshell/Makefile new file mode 100644 index 000000000..8fc6d1ef5 --- /dev/null +++ b/popen-noshell/Makefile @@ -0,0 +1,15 @@ +SRCS=popen_noshell.c + +OBJS:=$(SRCS:%.c=%.o) + +%.o: %.c + $(CC) $(CPPFLAGS) $(CFLAGS) -o $@ -c $< + +all: libpopen_noshell.a + +libpopen_noshell.a: $(OBJS) + $(AR) rcs $@ $(OBJS) + +clean: + rm -rf *.o + rm -rf libpopen_noshell.a \ No newline at end of file From 3f5fe1e991181fdafbb6114f605a86ad163ae3ca Mon Sep 17 00:00:00 2001 From: NiLuJe Date: Tue, 2 Oct 2012 23:18:28 +0200 Subject: [PATCH 07/21] Use popen_noshell as a static lib instead of an object file. Use CHOST instead of HOST (and allow it to be set from the env), use $(MAKE) instead of make to allow using the jobserver properly, and remove the dash from commands where we do care about the return code (or inhibit errors the usual way: rm -f instead of -rm) --- Makefile | 82 ++++++++++++++++++++++-------------------- popen-noshell/Makefile | 2 +- 2 files changed, 44 insertions(+), 40 deletions(-) diff --git a/Makefile b/Makefile index 76b4c97b7..e2aa38226 100644 --- a/Makefile +++ b/Makefile @@ -18,10 +18,11 @@ TTF_FONTS_DIR=$(MUPDFDIR)/fonts # set this to your ARM cross compiler: -HOST:=arm-none-linux-gnueabi -CC:=$(HOST)-gcc -CXX:=$(HOST)-g++ -STRIP:=$(HOST)-strip +CHOST?=arm-none-linux-gnueabi +CC:=$(CHOST)-gcc +CXX:=$(CHOST)-g++ +STRIP:=$(CHOST)-strip +STRIP:=$(CHOST)-ar ifdef SBOX_UNAME_MACHINE CC:=gcc CXX:=g++ @@ -90,9 +91,11 @@ THIRDPARTYLIBS := $(MUPDFLIBDIR)/libfreetype.a \ LUALIB := $(LUADIR)/src/libluajit.a -all:kpdfview +POPENNSLIB := $(POPENNSDIR)/libpopen_noshell.a -kpdfview: kpdfview.o einkfb.o pdf.o blitbuffer.o drawcontext.o popen_noshell.o input.o util.o ft.o lfs.o mupdfimg.o $(MUPDFLIBS) $(THIRDPARTYLIBS) $(LUALIB) djvu.o $(DJVULIBS) cre.o $(CRENGINELIBS) +all: kpdfview + +kpdfview: kpdfview.o einkfb.o pdf.o blitbuffer.o drawcontext.o input.o $(POPENNSLIB) util.o ft.o lfs.o mupdfimg.o $(MUPDFLIBS) $(THIRDPARTYLIBS) $(LUALIB) djvu.o $(DJVULIBS) cre.o $(CRENGINELIBS) $(CC) \ $(CFLAGS) \ kpdfview.o \ @@ -100,8 +103,8 @@ kpdfview: kpdfview.o einkfb.o pdf.o blitbuffer.o drawcontext.o popen_noshell.o i pdf.o \ blitbuffer.o \ drawcontext.o \ - popen_noshell.o \ input.o \ + $(POPENNSLIB) \ util.o \ ft.o \ lfs.o \ @@ -120,8 +123,8 @@ kpdfview: kpdfview.o einkfb.o pdf.o blitbuffer.o drawcontext.o popen_noshell.o i slider_watcher.o: %.o: %.c $(CC) -c $(CFLAGS) $< -o $@ -slider_watcher: popen_noshell.o slider_watcher.o - $(CC) $(CFLAGS) popen_noshell.o slider_watcher.o -o $@ +slider_watcher: slider_watcher.o $(POPENNSLIB) + $(CC) $(CFLAGS) slider_watcher.o $(POPENNSLIB) -o $@ ft.o: %.o: %.c $(THIRDPARTYLIBS) $(CC) -c $(KPDFREADER_CFLAGS) -I$(FREETYPEDIR)/include -I$(MUPDFDIR)/fitz $< -o $@ @@ -138,11 +141,8 @@ cre.o: %.o: %.cpp lfs.o: $(LFSDIR)/src/lfs.c $(CC) -c $(CFLAGS) -I$(LUADIR)/src -I$(LFSDIR)/src $(LFSDIR)/src/lfs.c -o $@ -popen_noshell.o: $(POPENNSDIR)/popen_noshell.c - $(CC) -c $(CFLAGS) -I$(POPENNSDIR) $(POPENNSDIR)/popen_noshell.c -o $@ - fetchthirdparty: - -rm -Rf mupdf/thirdparty + rm -rf mupdf/thirdparty test -d mupdf && (cd mupdf; git checkout .) || echo warn: mupdf folder not found test -d $(LUADIR) && (cd $(LUADIR); git checkout .) || echo warn: $(LUADIR) folder not found git submodule init @@ -166,56 +166,60 @@ fetchthirdparty: cd mupdf && patch -N -p1 < ../mupdf.patch clean: - -rm -f *.o kpdfview slider_watcher + rm -f *.o kpdfview slider_watcher cleanthirdparty: - -make -C $(LUADIR) clean - -make -C $(MUPDFDIR) build="release" clean - -make -C $(CRENGINEDIR)/thirdparty/antiword clean - test -d $(CRENGINEDIR)/thirdparty/chmlib && make -C $(CRENGINEDIR)/thirdparty/chmlib clean || echo warn: chmlib folder not found - test -d $(CRENGINEDIR)/thirdparty/libpng && (make -C $(CRENGINEDIR)/thirdparty/libpng clean) || echo warn: chmlib folder not found - test -d $(CRENGINEDIR)/crengine && (make -C $(CRENGINEDIR)/crengine clean) || echo warn: chmlib folder not found - test -d $(KPVCRLIBDIR) && (make -C $(KPVCRLIBDIR) clean) || echo warn: chmlib folder not found - -rm -rf $(DJVUDIR)/build - -rm -f $(MUPDFDIR)/fontdump.host - -rm -f $(MUPDFDIR)/cmapdump.host + $(MAKE) -C $(LUADIR) clean + $(MAKE) -C $(MUPDFDIR) build="release" clean + $(MAKE) -C $(CRENGINEDIR)/thirdparty/antiword clean + test -d $(CRENGINEDIR)/thirdparty/chmlib && $(MAKE) -C $(CRENGINEDIR)/thirdparty/chmlib clean || echo warn: chmlib folder not found + test -d $(CRENGINEDIR)/thirdparty/libpng && ($(MAKE) -C $(CRENGINEDIR)/thirdparty/libpng clean) || echo warn: chmlib folder not found + test -d $(CRENGINEDIR)/crengine && ($(MAKE) -C $(CRENGINEDIR)/crengine clean) || echo warn: chmlib folder not found + test -d $(KPVCRLIBDIR) && ($(MAKE) -C $(KPVCRLIBDIR) clean) || echo warn: chmlib folder not found + rm -rf $(DJVUDIR)/build + rm -f $(MUPDFDIR)/fontdump.host + rm -f $(MUPDFDIR)/cmapdump.host + $(MAKE) -C $(POPENNSDIR) clean $(MUPDFDIR)/fontdump.host: - make -C mupdf build="release" CC="$(HOSTCC)" $(MUPDFTARGET)/fontdump + $(MAKE) -C mupdf build="release" CC="$(HOSTCC)" $(MUPDFTARGET)/fontdump cp -a $(MUPDFLIBDIR)/fontdump $(MUPDFDIR)/fontdump.host - make -C mupdf clean + $(MAKE) -C mupdf clean $(MUPDFDIR)/cmapdump.host: - make -C mupdf build="release" CC="$(HOSTCC)" $(MUPDFTARGET)/cmapdump + $(MAKE) -C mupdf build="release" CC="$(HOSTCC)" $(MUPDFTARGET)/cmapdump cp -a $(MUPDFLIBDIR)/cmapdump $(MUPDFDIR)/cmapdump.host - make -C mupdf clean + $(MAKE) -C mupdf clean $(MUPDFLIBS) $(THIRDPARTYLIBS): $(MUPDFDIR)/cmapdump.host $(MUPDFDIR)/fontdump.host # build only thirdparty libs, libfitz and pdf utils, which will care for libmupdf.a being built - CFLAGS="$(CFLAGS) -DNOBUILTINFONT" make -C mupdf build="release" CC="$(CC)" CMAPDUMP=cmapdump.host FONTDUMP=fontdump.host MUPDF= MU_APPS= BUSY_APP= XPS_APPS= verbose=1 + CFLAGS="$(CFLAGS) -DNOBUILTINFONT" $(MAKE) -C mupdf build="release" CC="$(CC)" CMAPDUMP=cmapdump.host FONTDUMP=fontdump.host MUPDF= MU_APPS= BUSY_APP= XPS_APPS= verbose=1 $(DJVULIBS): - -mkdir $(DJVUDIR)/build + mkdir -p $(DJVUDIR)/build ifdef EMULATE_READER cd $(DJVUDIR)/build && ../configure --disable-desktopfiles --disable-shared --enable-static --disable-xmltools --disable-largefile else - cd $(DJVUDIR)/build && ../configure --disable-desktopfiles --disable-shared --enable-static --host=$(HOST) --disable-xmltools --disable-largefile + cd $(DJVUDIR)/build && ../configure --disable-desktopfiles --disable-shared --enable-static --host=$(CHOST) --disable-xmltools --disable-largefile endif - make -C $(DJVUDIR)/build + $(MAKE) -C $(DJVUDIR)/build $(CRENGINELIBS): cd $(KPVCRLIBDIR) && rm -rf CMakeCache.txt CMakeFiles && \ CFLAGS="$(CFLAGS)" CXXFLAGS="$(CXXFLAGS)" CC="$(CC)" CXX="$(CXX)" LDFLAGS="$(LDFLAGS)" cmake . && \ - make + $(MAKE) $(LUALIB): ifdef EMULATE_READER - make -C $(LUADIR) + $(MAKE) -C $(LUADIR) else - make -C $(LUADIR) CC="$(HOSTCC)" HOST_CC="$(HOSTCC) -m32" CROSS="$(HOST)-" TARGET_FLAGS="$(SYSROOT) -DLUAJIT_NO_LOG2 -DLUAJIT_NO_EXP2" + $(MAKE) -C $(LUADIR) CC="$(HOSTCC)" HOST_CC="$(HOSTCC) -m32" CROSS="$(CHOST)-" TARGET_FLAGS="$(SYSROOT) -DLUAJIT_NO_LOG2 -DLUAJIT_NO_EXP2" endif -thirdparty: $(MUPDFLIBS) $(THIRDPARTYLIBS) $(LUALIB) $(DJVULIBS) $(CRENGINELIBS) +$(POPENNSLIB): + $(MAKE) -C $(POPENNSDIR) CC="$(CC)" AR="$(AR)" + +thirdparty: $(MUPDFLIBS) $(THIRDPARTYLIBS) $(LUALIB) $(DJVULIBS) $(CRENGINELIBS) $(POPENNSLIB) INSTALL_DIR=kindlepdfviewer @@ -226,8 +230,8 @@ customupdate: all # ensure that build binary is for ARM file kpdfview | grep ARM || exit 1 $(STRIP) --strip-unneeded kpdfview - -rm kindlepdfviewer-$(VERSION).zip - rm -Rf $(INSTALL_DIR) + rm -f kindlepdfviewer-$(VERSION).zip + rm -rf $(INSTALL_DIR) mkdir -p $(INSTALL_DIR)/{history,screenshots} echo $(VERSION) > $(INSTALL_DIR)/git-rev cp -p README.md COPYING kpdfview $(LUA_FILES) $(INSTALL_DIR) @@ -237,5 +241,5 @@ customupdate: all cp -r resources $(INSTALL_DIR) mkdir $(INSTALL_DIR)/fonts/host zip -9 -r kindlepdfviewer-$(VERSION).zip $(INSTALL_DIR) launchpad/ kite/ - rm -Rf $(INSTALL_DIR) + rm -rf $(INSTALL_DIR) @echo "copy kindlepdfviewer-$(VERSION).zip to /mnt/us/customupdates and install with shift+shift+I" diff --git a/popen-noshell/Makefile b/popen-noshell/Makefile index 8fc6d1ef5..facd99021 100644 --- a/popen-noshell/Makefile +++ b/popen-noshell/Makefile @@ -12,4 +12,4 @@ libpopen_noshell.a: $(OBJS) clean: rm -rf *.o - rm -rf libpopen_noshell.a \ No newline at end of file + rm -rf libpopen_noshell.a From 4e861715e3e9f7cafa98896004bd2a4c2bbfc86a Mon Sep 17 00:00:00 2001 From: NiLuJe Date: Tue, 2 Oct 2012 23:21:14 +0200 Subject: [PATCH 08/21] Fix a stupid typo --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index e2aa38226..7226dbf26 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,7 @@ CHOST?=arm-none-linux-gnueabi CC:=$(CHOST)-gcc CXX:=$(CHOST)-g++ STRIP:=$(CHOST)-strip -STRIP:=$(CHOST)-ar +AR:=$(CHOST)-ar ifdef SBOX_UNAME_MACHINE CC:=gcc CXX:=g++ From 5c76ef0f78840a86b53a9d4185e087dbc7a1d826 Mon Sep 17 00:00:00 2001 From: NiLuJe Date: Tue, 2 Oct 2012 23:30:12 +0200 Subject: [PATCH 09/21] Fetch popen_noshell during fetchthirdparty, don't bundle it in our repo --- Makefile | 3 + popen-noshell/Makefile | 15 - popen-noshell/README | 21 - popen-noshell/popen_noshell-buildfix.patch | 84 ++++ popen-noshell/popen_noshell.c | 550 --------------------- popen-noshell/popen_noshell.h | 69 --- 6 files changed, 87 insertions(+), 655 deletions(-) delete mode 100644 popen-noshell/Makefile delete mode 100644 popen-noshell/README create mode 100644 popen-noshell/popen_noshell-buildfix.patch delete mode 100644 popen-noshell/popen_noshell.c delete mode 100644 popen-noshell/popen_noshell.h diff --git a/Makefile b/Makefile index 7226dbf26..7a6ce5722 100644 --- a/Makefile +++ b/Makefile @@ -164,6 +164,9 @@ fetchthirdparty: patch -N -p0 < ../../../kpvcrlib/jpeg_decompress_struct_size.patch # MuPDF patch: use external fonts cd mupdf && patch -N -p1 < ../mupdf.patch + svn co http://popen-noshell.googlecode.com/svn/trunk/ popen-noshell + # popen_noshell patch: Make it build on recent TCs, and implement a simple Makefile for building it as a static lib + cd popen-noshell && patch -N -p0 < popen_noshell-buildfix.patch clean: rm -f *.o kpdfview slider_watcher diff --git a/popen-noshell/Makefile b/popen-noshell/Makefile deleted file mode 100644 index facd99021..000000000 --- a/popen-noshell/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -SRCS=popen_noshell.c - -OBJS:=$(SRCS:%.c=%.o) - -%.o: %.c - $(CC) $(CPPFLAGS) $(CFLAGS) -o $@ -c $< - -all: libpopen_noshell.a - -libpopen_noshell.a: $(OBJS) - $(AR) rcs $@ $(OBJS) - -clean: - rm -rf *.o - rm -rf libpopen_noshell.a diff --git a/popen-noshell/README b/popen-noshell/README deleted file mode 100644 index 7b829d130..000000000 --- a/popen-noshell/README +++ /dev/null @@ -1,21 +0,0 @@ -/* - * popen_noshell: A faster implementation of popen() and system() for Linux. - * Copyright (c) 2009 Ivan Zahariev (famzah) - * Version: 1.0 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; under version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -This is the faster popen() alternative implementation. - -Taken from http://code.google.com/p/popen-noshell/ diff --git a/popen-noshell/popen_noshell-buildfix.patch b/popen-noshell/popen_noshell-buildfix.patch new file mode 100644 index 000000000..6f9ba1bf8 --- /dev/null +++ b/popen-noshell/popen_noshell-buildfix.patch @@ -0,0 +1,84 @@ +Index: CREDITS +=================================================================== +--- CREDITS (revision 0) ++++ CREDITS (working copy) +@@ -0,0 +1 @@ ++Taken from http://code.google.com/p/popen-noshell/ + +Property changes on: CREDITS +___________________________________________________________________ +Added: svn:keywords +## -0,0 +1 ## ++Id +\ No newline at end of property +Index: Makefile +=================================================================== +--- Makefile (revision 0) ++++ Makefile (working copy) +@@ -0,0 +1,15 @@ ++SRCS=popen_noshell.c ++ ++OBJS:=$(SRCS:%.c=%.o) ++ ++%.o: %.c ++ $(CC) $(CPPFLAGS) $(CFLAGS) -o $@ -c $< ++ ++all: libpopen_noshell.a ++ ++libpopen_noshell.a: $(OBJS) ++ $(AR) rcs $@ $(OBJS) ++ ++clean: ++ rm -rf *.o ++ rm -rf libpopen_noshell.a + +Property changes on: Makefile +___________________________________________________________________ +Added: svn:keywords +## -0,0 +1 ## ++Id +\ No newline at end of property +Index: popen_noshell.c +=================================================================== +--- popen_noshell.c (revision 8) ++++ popen_noshell.c (working copy) +@@ -16,6 +16,10 @@ + * along with this program. If not, see . + */ + ++#ifndef _GNU_SOURCE ++#define _GNU_SOURCE ++#endif ++ + #include "popen_noshell.h" + #include + #include +@@ -28,10 +32,6 @@ + #include + #include + #include +- +-#ifndef _GNU_SOURCE +-#define _GNU_SOURCE +-#endif + #include + + /* +@@ -249,7 +249,7 @@ + * The above malloc() + align implementation is taken from: + * http://stackoverflow.com/questions/227897/solve-the-memory-alignment-in-c-interview-question-that-stumped-me + */ +- ++ + #ifndef POPEN_NOSHELL_VALGRIND_DEBUG + pid = clone(fn, stack_aligned, CLONE_VM | SIGCHLD, arg); + #else +@@ -358,7 +358,7 @@ + + pclose_arg->fp = fp; + pclose_arg->pid = pid; +- ++ + return fp; // we should never end up here + } + diff --git a/popen-noshell/popen_noshell.c b/popen-noshell/popen_noshell.c deleted file mode 100644 index ed37d07d5..000000000 --- a/popen-noshell/popen_noshell.c +++ /dev/null @@ -1,550 +0,0 @@ -/* - * popen_noshell: A faster implementation of popen() and system() for Linux. - * Copyright (c) 2009 Ivan Zahariev (famzah) - * Version: 1.0 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; under version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif - -#include "popen_noshell.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * Wish-list: - * 1) Make the "ignore_stderr" parameter a mode - ignore, leave unchanged, redirect to stdout (the last is not implemented yet) - * 2) Code a faster system(): system_noshell(), system_noshell_compat() - */ - -//#define POPEN_NOSHELL_DEBUG - -// because of C++, we can't call err() or errx() within the child, because they call exit(), and _exit() is what must be called; so we wrap -#define _ERR(EVAL, FMT, ...) \ - { \ - warn(FMT, ##__VA_ARGS__); \ - _exit(EVAL); \ - } -#define _ERRX(EVAL, FMT, ...) \ - { \ - warnx(FMT, ##__VA_ARGS__); \ - _exit(EVAL); \ - } - -int _popen_noshell_fork_mode = POPEN_NOSHELL_MODE_CLONE; - -void popen_noshell_set_fork_mode(int mode) { // see "popen_noshell.h" POPEN_NOSHELL_MODE_* constants - _popen_noshell_fork_mode = mode; -} - -int popen_noshell_reopen_fd_to_dev_null(int fd) { - int dev_null_fd; - - dev_null_fd = open("/dev/null", O_RDWR); - if (dev_null_fd < 0) return -1; - - if (close(fd) != 0) { - return -1; - } - if (dup2(dev_null_fd, fd) == -1) { - return -1; - } - if (close(dev_null_fd) != 0) { - return -1; - } - - return 0; -} - -int _popen_noshell_close_and_dup(int pipefd[2], int closed_pipefd, int target_fd) { - int dupped_pipefd; - - dupped_pipefd = (closed_pipefd == 0 ? 1 : 0); // get the FD of the other end of the pipe - - if (close(pipefd[closed_pipefd]) != 0) { - return -1; - } - - if (close(target_fd) != 0) { - return -1; - } - if (dup2(pipefd[dupped_pipefd], target_fd) == -1) { - return -1; - } - if (close(pipefd[dupped_pipefd]) != 0) { - return -1; - } - - return 0; -} - -void _pclose_noshell_free_clone_arg_memory(struct popen_noshell_clone_arg *func_args) { - char **cmd_argv; - - free((char *)func_args->file); - cmd_argv = (char **)func_args->argv; - while (*cmd_argv) { - free(*cmd_argv); - ++cmd_argv; - } - free((char **)func_args->argv); - free(func_args); -} - -void _popen_noshell_child_process( - /* We need the pointer *arg_ptr only to free whatever we reference if exec() fails and we were fork()'ed (thus memory was copied), - * not clone()'d */ - struct popen_noshell_clone_arg *arg_ptr, /* NULL if we were called by pure fork() (not because of Valgrind) */ - int pipefd_0, int pipefd_1, int read_pipe, int ignore_stderr, const char *file, const char * const *argv) { - - int closed_child_fd; - int closed_pipe_fd; - int dupped_child_fd; - int pipefd[2] = {pipefd_0, pipefd_1}; - - if (ignore_stderr) { /* ignore STDERR completely? */ - if (popen_noshell_reopen_fd_to_dev_null(STDERR_FILENO) != 0) _ERR(255, "popen_noshell_reopen_fd_to_dev_null(%d)", STDERR_FILENO); - } - - if (read_pipe) { - closed_child_fd = STDIN_FILENO; /* re-open STDIN to /dev/null */ - closed_pipe_fd = 0; /* close read end of pipe */ - dupped_child_fd = STDOUT_FILENO; /* dup the other pipe end to STDOUT */ - } else { - closed_child_fd = STDOUT_FILENO; /* ignore STDOUT completely */ - closed_pipe_fd = 1; /* close write end of pipe */ - dupped_child_fd = STDIN_FILENO; /* dup the other pipe end to STDIN */ - } - if (popen_noshell_reopen_fd_to_dev_null(closed_child_fd) != 0) { - _ERR(255, "popen_noshell_reopen_fd_to_dev_null(%d)", closed_child_fd); - } - if (_popen_noshell_close_and_dup(pipefd, closed_pipe_fd, dupped_child_fd) != 0) { - _ERR(255, "_popen_noshell_close_and_dup(%d ,%d)", closed_pipe_fd, dupped_child_fd); - } - - execvp(file, (char * const *)argv); - - /* if we are here, exec() failed */ - - warn("exec(\"%s\") inside the child", file); - -#ifdef POPEN_NOSHELL_VALGRIND_DEBUG - if (arg_ptr) { /* not NULL if we were called by clone() */ - /* but Valgrind does not support clone(), so we were actually called by fork(), thus memory was copied... */ - /* free this copied memory; if it was not Valgrind, this memory would have been shared and would belong to the parent! */ - _pclose_noshell_free_clone_arg_memory(arg_ptr); - } -#endif - - if (fflush(stdout) != 0) _ERR(255, "fflush(stdout)"); - if (fflush(stderr) != 0) _ERR(255, "fflush(stderr)"); - close(STDIN_FILENO); - close(STDOUT_FILENO); - close(STDERR_FILENO); - - _exit(255); // call _exit() and not exit(), or you'll have troubles in C++ -} - -int popen_noshell_child_process_by_clone(void *raw_arg) { - struct popen_noshell_clone_arg *arg; - - arg = (struct popen_noshell_clone_arg *)raw_arg; - _popen_noshell_child_process(arg, arg->pipefd_0, arg->pipefd_1, arg->read_pipe, arg->ignore_stderr, arg->file, arg->argv); - - return 0; -} - -char ** popen_noshell_copy_argv(const char * const *argv_orig) { - int size = 1; /* there is at least one NULL element */ - char **argv; - char **argv_new; - int n; - - argv = (char **) argv_orig; - while (*argv) { - ++size; - ++argv; - } - - argv_new = (char **)malloc(sizeof(char *) * size); - if (!argv_new) return NULL; - - argv = (char **)argv_orig; - n = 0; - while (*argv) { - argv_new[n] = strdup(*argv); - if (!argv_new[n]) return NULL; - ++argv; - ++n; - } - argv_new[n] = (char *)NULL; - - return argv_new; -} - -/* - * Similar to vfork() and threading. - * Starts a process which behaves like a thread (shares global variables in memory with the parent) but - * has a different PID and can call exec(), unlike traditional threads which are not allowed to call exec(). - * - * This fork function is very resource-light because it does not copy any memory from the parent, but shares it. - * - * Like standard threads, you have to provide a start function *fn and arguments to it *arg. The life of the - * new vmfork()'ed process starts from this function. - * - * After you have reaped the child via waitpid(), you have to free() the memory at "*memory_to_free_on_child_exit". - * - * When the *fn function returns, the child process terminates. The integer returned by *fn is the exit code for the child process. - * The child process may also terminate explicitly by calling exit(2) or after receiving a fatal signal. - * - * Returns -1 on error. On success returns the PID of the newly created child. - */ -pid_t popen_noshell_vmfork(int (*fn)(void *), void *arg, void **memory_to_free_on_child_exit) { - void *stack, *stack_aligned; - pid_t pid; - - stack = malloc(POPEN_NOSHELL_STACK_SIZE + 15); - if (!stack) return -1; - *memory_to_free_on_child_exit = stack; - - /* - * On all supported Linux platforms the stack grows down, except for HP-PARISC. - * You can grep the kernel source for "STACK_GROWSUP", in order to get this information. - */ - // stack grows down, set pointer at the end of the block - stack_aligned = (void *) ((char * /*byte*/)stack + POPEN_NOSHELL_STACK_SIZE/*bytes*/); - - /* - * On all supported platforms by GNU libc, the stack is aligned to 16 bytes, except for the SuperH platform which is aligned to 8 bytes. - * You can grep the glibc source for "STACK_ALIGN", in order to get this information. - */ - stack_aligned = (void *) ( ((uintptr_t)stack_aligned+15) & ~ 0x0F ); // align to 16 bytes - - /* - * Maybe we could have used posix_memalign() here... - * Our implementation seems a bit more portable though - I've read somewhere that posix_memalign() is not supported on all platforms. - * The above malloc() + align implementation is taken from: - * http://stackoverflow.com/questions/227897/solve-the-memory-alignment-in-c-interview-question-that-stumped-me - */ - -#ifndef POPEN_NOSHELL_VALGRIND_DEBUG - pid = clone(fn, stack_aligned, CLONE_VM | SIGCHLD, arg); -#else - pid = fork(); // Valgrind does not support arbitrary clone() calls, so we use fork for the tests -#endif - if (pid == -1) return -1; - - if (pid == 0) { // child -#ifdef POPEN_NOSHELL_VALGRIND_DEBUG - free(stack); // this is a copy because of the fork(), we are not using it at all - _exit(fn(arg)); // if we used fork() because of Valgrind, invoke the child function manually; always use _exit() -#endif - errx(EXIT_FAILURE, "This must never happen"); - } // child life ends here, for sure - - return pid; -} - -/* - * Pipe stream to or from process. Similar to popen(), only much faster. - * - * "file" is the command to be executed. It is searched within the PATH environment variable. - * "argv[]" is an array to "char *" elements which are passed as command-line arguments. - * Note: The first element must be the same string as "file". - * Note: The last element must be a (char *)NULL terminating element. - * "type" specifies if we are reading from the STDOUT or writing to the STDIN of the executed command "file". Use "r" for reading, "w" for writing. - * "pid" is a pointer to an interger. The PID of the child process is stored there. - * "ignore_stderr" has the following meaning: - * 0: leave STDERR of the child process attached to the current STDERR of the parent process - * 1: ignore the STDERR of the child process - * - * This function is not very sustainable on failures. This means that if it fails for some reason (out of memory, no such executable, etc.), - * you are probably in trouble, because the function allocated some memory or file descriptors and never released them. - * Normally, this function should never fail. - * - * Returns NULL on any error, "errno" is set appropriately. - * On success, a stream pointer is returned. - * When you are done working with the stream, you have to close it by calling pclose_noshell(), or else you will leave zombie processes. - */ -FILE *popen_noshell(const char *file, const char * const *argv, const char *type, struct popen_noshell_pass_to_pclose *pclose_arg, int ignore_stderr) { - int read_pipe; - int pipefd[2]; // 0 -> READ, 1 -> WRITE ends - pid_t pid; - FILE *fp; - - memset(pclose_arg, 0, sizeof(struct popen_noshell_pass_to_pclose)); - - if (strcmp(type, "r") == 0) { - read_pipe = 1; - } else if (strcmp(type, "w") == 0) { - read_pipe = 0; - } else { - errno = EINVAL; - return NULL; - } - - if (pipe(pipefd) != 0) return NULL; - - if (_popen_noshell_fork_mode) { // use fork() - - pid = fork(); - if (pid == -1) return NULL; - if (pid == 0) { - _popen_noshell_child_process(NULL, pipefd[0], pipefd[1], read_pipe, ignore_stderr, file, argv); - errx(EXIT_FAILURE, "This must never happen"); - } // child life ends here, for sure - - } else { // use clone() - - struct popen_noshell_clone_arg *arg = NULL; - - arg = (struct popen_noshell_clone_arg*) malloc(sizeof(struct popen_noshell_clone_arg)); - if (!arg) return NULL; - - /* Copy memory structures, so that nobody can free() our memory while we use it in the child! */ - arg->pipefd_0 = pipefd[0]; - arg->pipefd_1 = pipefd[1]; - arg->read_pipe = read_pipe; - arg->ignore_stderr = ignore_stderr; - arg->file = strdup(file); - if (!arg->file) return NULL; - arg->argv = (const char * const *)popen_noshell_copy_argv(argv); - if (!arg->argv) return NULL; - - pclose_arg->free_clone_mem = 1; - pclose_arg->func_args = arg; - pclose_arg->stack = NULL; // we will populate it below - - pid = popen_noshell_vmfork(&popen_noshell_child_process_by_clone, arg, &(pclose_arg->stack)); - if (pid == -1) return NULL; - - } // done: using clone() - - /* parent process */ - - if (read_pipe) { - if (close(pipefd[1/*write*/]) != 0) return NULL; - fp = fdopen(pipefd[0/*read*/], "r"); - } else { // write_pipe - if (close(pipefd[0/*read*/]) != 0) return NULL; - fp = fdopen(pipefd[1/*write*/], "w"); - } - if (fp == NULL) { - return NULL; // fdopen() failed - } - - pclose_arg->fp = fp; - pclose_arg->pid = pid; - - return fp; // we should never end up here -} - -int popen_noshell_add_ptr_to_argv(char ***argv, int *count, char *start) { - *count += 1; - *argv = (char **) realloc(*argv, *count * sizeof(char **)); - if (*argv == NULL) { - return -1; - } - *(*argv + *count - 1) = start; - return 0; -} - -int _popen_noshell_add_token(char ***argv, int *count, char *start, char *command, int *j) { - if (start != NULL && command + *j - 1 - start >= 0) { - command[*j] = '\0'; // terminate the token in memory - *j += 1; -#ifdef POPEN_NOSHELL_DEBUG - printf("Token: %s\n", start); -#endif - if (popen_noshell_add_ptr_to_argv(argv, count, start) != 0) { - return -1; - } - } - return 0; -} - -#define _popen_noshell_split_return_NULL { if (argv != NULL) free(argv); if (command != NULL) free(command); return NULL; } -char ** popen_noshell_split_command_to_argv(const char *command_original, char **free_this_buf) { - char *command; - size_t i, len; - char *start = NULL; - char c; - char **argv = NULL; - int count = 0; - const char _popen_bash_meta_characters[] = "!\\$`\n|&;()<>"; - int in_sq = 0; - int in_dq = 0; - int j = 0; -#ifdef POPEN_NOSHELL_DEBUG - char **tmp; -#endif - - command = (char *)calloc(strlen(command_original) + 1, sizeof(char)); - if (!command) _popen_noshell_split_return_NULL; - - *free_this_buf = command; - - len = strlen(command_original); // get the original length - j = 0; - for (i = 0; i < len; ++i) { - if (!start) start = command + j; - c = command_original[i]; - - if (index(_popen_bash_meta_characters, c) != NULL) { - errno = EINVAL; - _popen_noshell_split_return_NULL; - } - - if (c == ' ' || c == '\t') { - if (in_sq || in_dq) { - command[j++] = c; - continue; - } - - // new token - if (_popen_noshell_add_token(&argv, &count, start, command, &j) != 0) { - _popen_noshell_split_return_NULL; - } - start = NULL; - continue; - } - - if (c == '\'' && !in_dq) { - in_sq = !in_sq; - continue; - } - if (c == '"' && !in_sq) { - in_dq = !in_dq; - continue; - } - - command[j++] = c; - } - if (in_sq || in_dq) { // unmatched single/double quote - errno = EINVAL; - _popen_noshell_split_return_NULL; - } - - if (_popen_noshell_add_token(&argv, &count, start, command, &j) != 0) { - _popen_noshell_split_return_NULL; - } - - if (count == 0) { - errno = EINVAL; - _popen_noshell_split_return_NULL; - } - - if (popen_noshell_add_ptr_to_argv(&argv, &count, NULL) != 0) { // NULL-terminate the list - _popen_noshell_split_return_NULL; - } - -#ifdef POPEN_NOSHELL_DEBUG - tmp = argv; - while (*tmp) { - printf("ARGV: |%s|\n", *tmp); - ++tmp; - } -#endif - - return argv; - - /* Example test strings: - "a'zz bb edd" - " abc ff " - " abc ff" - "' abc ff ' " - "" - " " - " '" - "ab\\c" - "ls -la /proc/self/fd 'z' 'ab'g'z\" zz' \" abc'd\" ' ab\"c def '" - */ -} - -/* - * Pipe stream to or from process. Similar to popen(), only much faster. - * - * This is simpler than popen_noshell() but is more INSECURE. - * Since shells have very complicated expansion, quoting and word splitting algorithms, we do NOT try to re-implement them here. - * This function does NOT support any special characters. It will immediately return an error if such symbols are encountered in "command". - * The "command" is split only by space and tab delimiters. The special symbols are pre-defined in _popen_bash_meta_characters[]. - * The only special characters supported are single and double quotes. You can enclose arguments in quotes and they should be splitted correctly. - * - * If possible, use popen_noshell() because of its better security. - * - * "command" is the command and its arguments to be executed. The command is searched within the PATH environment variable. - * The whole "command" string is parsed and splitted, so that it can be directly given to popen_noshell() and resp. to exec(). - * This parsing is very simple and may contain bugs (see above). If possible, use popen_noshell() directly. - * "type" specifies if we are reading from the STDOUT or writing to the STDIN of the executed command. Use "r" for reading, "w" for writing. - * "pid" is a pointer to an interger. The PID of the child process is stored there. - * - * Returns NULL on any error, "errno" is set appropriately. - * On success, a stream pointer is returned. - * When you are done working with the stream, you have to close it by calling pclose_noshell(), or else you will leave zombie processes. - */ -FILE *popen_noshell_compat(const char *command, const char *type, struct popen_noshell_pass_to_pclose *pclose_arg) { - char **argv; - FILE *fp; - char *to_free; - - argv = popen_noshell_split_command_to_argv(command, &to_free); - if (!argv) { - if (to_free) free(to_free); - return NULL; - } - - fp = popen_noshell(argv[0], (const char * const *)argv, type, pclose_arg, 0); - - free(to_free); - free(argv); - - return fp; -} - -/* - * You have to call this function after you have done working with the FILE pointer "fp" returned by popen_noshell() or by popen_noshell_compat(). - * - * Returns -1 on any error, "errno" is set appropriately. - * Returns the "status" of the child process as returned by waitpid(). - */ -int pclose_noshell(struct popen_noshell_pass_to_pclose *arg) { - int status; - - if (fclose(arg->fp) != 0) { - return -1; - } - - if (waitpid(arg->pid, &status, 0) != arg->pid) { - return -1; - } - - if (arg->free_clone_mem) { - free(arg->stack); - _pclose_noshell_free_clone_arg_memory(arg->func_args); - } - - return status; -} diff --git a/popen-noshell/popen_noshell.h b/popen-noshell/popen_noshell.h deleted file mode 100644 index a2eeb3c6e..000000000 --- a/popen-noshell/popen_noshell.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * popen_noshell: A faster implementation of popen() and system() for Linux. - * Copyright (c) 2009 Ivan Zahariev (famzah) - * Version: 1.0 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; under version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -#ifndef POPEN_NOSHELL_H -#define POPEN_NOSHELL_H - -#include -#include -#include - -/* stack for the child process before it does exec() */ -#define POPEN_NOSHELL_STACK_SIZE 8*1024*1024 /* currently most Linux distros set this to 8 MBytes */ - -/* constants to use with popen_noshell_set_fork_mode() */ -#define POPEN_NOSHELL_MODE_CLONE 0 /* default, faster */ -#define POPEN_NOSHELL_MODE_FORK 1 /* slower */ - -struct popen_noshell_clone_arg { - int pipefd_0; - int pipefd_1; - int read_pipe; - int ignore_stderr; - const char *file; - const char * const *argv; -}; - -struct popen_noshell_pass_to_pclose { - FILE *fp; - pid_t pid; - int free_clone_mem; - void *stack; - struct popen_noshell_clone_arg *func_args; -}; - -/*************************** - * PUBLIC FUNCTIONS FOLLOW * - ***************************/ - -/* this is the native function call */ -FILE *popen_noshell(const char *file, const char * const *argv, const char *type, struct popen_noshell_pass_to_pclose *pclose_arg, int ignore_stderr); - -/* more insecure, but more compatible with popen() */ -FILE *popen_noshell_compat(const char *command, const char *type, struct popen_noshell_pass_to_pclose *pclose_arg); - -/* call this when you have finished reading and writing from/to the child process */ -int pclose_noshell(struct popen_noshell_pass_to_pclose *arg); /* the pclose() equivalent */ - -/* this is the innovative faster vmfork() which shares memory with the parent and is very resource-light; see the source code for documentation */ -pid_t popen_noshell_vmfork(int (*fn)(void *), void *arg, void **memory_to_free_on_child_exit); - -/* used only for benchmarking purposes */ -void popen_noshell_set_fork_mode(int mode); - -#endif From d0573049c94ae0d67415a3ad813013c4fcb18cbf Mon Sep 17 00:00:00 2001 From: NiLuJe Date: Tue, 2 Oct 2012 23:33:59 +0200 Subject: [PATCH 10/21] Ignore untracked popen_noshell stuff --- .gitignore | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.gitignore b/.gitignore index c806532dd..62312b73f 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,14 @@ kpvcrlib/Makefile popen-noshell/libpopen_noshell.a popen-noshell/*.o +popen-noshell/.svn/ +popen-noshell/CREDITS +popen-noshell/Makefile +popen-noshell/README +popen-noshell/performance_tests/ +popen-noshell/popen_noshell.c +popen-noshell/popen_noshell.h +popen-noshell/popen_noshell_examples.c +popen-noshell/popen_noshell_tests.c +popen-noshell/popen_noshell_tests.cpp + From 76714c453afaa368e038e5ecf6f44760b13dff59 Mon Sep 17 00:00:00 2001 From: NiLuJe Date: Tue, 2 Oct 2012 23:50:34 +0200 Subject: [PATCH 11/21] Restore Makefiles tweaks --- Makefile | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 7a6ce5722..40442bab9 100644 --- a/Makefile +++ b/Makefile @@ -30,10 +30,13 @@ endif HOSTCC:=gcc HOSTCXX:=g++ -CFLAGS:=-O3 $(SYSROOT) -CXXFLAGS:=-O3 $(SYSROOT) +# Base CFLAGS, without arch. Will use it as-is for luajit, because its buildsystem picks up the wrong flags, possibly from my env... +BASE_CFLAGS:=-O2 -ffast-math -pipe -fomit-frame-pointer -fno-stack-protector -U_FORTIFY_SOURCE +CFLAGS:=$(BASE_CFLAGS) +CXXFLAGS:=$(BASE_CFLAGS) -fno-use-cxa-atexit LDFLAGS:=-Wl,-O1 -Wl,--as-needed ARM_CFLAGS:=-march=armv6j -mtune=arm1136jf-s -mfpu=vfp +HOSTCFLAGS:=-O2 -march=native -ffast-math -pipe -fomit-frame-pointer # use this for debugging: #CFLAGS:=-O0 -g @@ -172,7 +175,7 @@ clean: rm -f *.o kpdfview slider_watcher cleanthirdparty: - $(MAKE) -C $(LUADIR) clean + $(MAKE) -C $(LUADIR) clean CFLAGS="" $(MAKE) -C $(MUPDFDIR) build="release" clean $(MAKE) -C $(CRENGINEDIR)/thirdparty/antiword clean test -d $(CRENGINEDIR)/thirdparty/chmlib && $(MAKE) -C $(CRENGINEDIR)/thirdparty/chmlib clean || echo warn: chmlib folder not found @@ -185,12 +188,12 @@ cleanthirdparty: $(MAKE) -C $(POPENNSDIR) clean $(MUPDFDIR)/fontdump.host: - $(MAKE) -C mupdf build="release" CC="$(HOSTCC)" $(MUPDFTARGET)/fontdump + $(MAKE) -C mupdf build="release" CC="$(HOSTCC)" CFLAGS="$(HOSTCFLAGS) -I../mupdf/fitz -I../mupdf/pdf" $(MUPDFTARGET)/fontdump cp -a $(MUPDFLIBDIR)/fontdump $(MUPDFDIR)/fontdump.host $(MAKE) -C mupdf clean $(MUPDFDIR)/cmapdump.host: - $(MAKE) -C mupdf build="release" CC="$(HOSTCC)" $(MUPDFTARGET)/cmapdump + $(MAKE) -C mupdf build="release" CC="$(HOSTCC)" CFLAGS="$(HOSTCFLAGS) -I../mupdf/fitz -I../mupdf/pdf" $(MUPDFTARGET)/cmapdump cp -a $(MUPDFLIBDIR)/cmapdump $(MUPDFDIR)/cmapdump.host $(MAKE) -C mupdf clean @@ -216,7 +219,7 @@ $(LUALIB): ifdef EMULATE_READER $(MAKE) -C $(LUADIR) else - $(MAKE) -C $(LUADIR) CC="$(HOSTCC)" HOST_CC="$(HOSTCC) -m32" CROSS="$(CHOST)-" TARGET_FLAGS="$(SYSROOT) -DLUAJIT_NO_LOG2 -DLUAJIT_NO_EXP2" + $(MAKE) -C $(LUADIR) CC="$(HOSTCC)" HOST_CC="$(HOSTCC) -m32" CFLAGS="$(BASE_CFLAGS)" HOST_CFLAGS="$(BASE_CFLAGS)" TARGET_CFLAGS="$(CFLAGS)" CROSS="$(CHOST)-" TARGET_FLAGS="-DLUAJIT_NO_LOG2 -DLUAJIT_NO_EXP2" endif $(POPENNSLIB): From ccff5c899d9a273cb571ea3537eb903e8bf47151 Mon Sep 17 00:00:00 2001 From: Dobrica Pavlinusic Date: Wed, 3 Oct 2012 00:16:45 +0200 Subject: [PATCH 12/21] show reneding page_indicator only in debug mode This addresses performance degradation because of two e-ink refreshes introduced by this feature #349 --- settings.lua | 1 + unireader.lua | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/settings.lua b/settings.lua index 286803d8b..3b38c3b25 100644 --- a/settings.lua +++ b/settings.lua @@ -100,6 +100,7 @@ function Debug(...) end end print("#"..line) + return true -- debug enabled end -- simple serialization function, won't do uservalues, functions, loops diff --git a/unireader.lua b/unireader.lua index 0cd17a951..a40d6edf6 100644 --- a/unireader.lua +++ b/unireader.lua @@ -1048,10 +1048,11 @@ function UniReader:drawOrCache(no, preCache) -- #4 goal: we render next page, too. (TODO) local pg_w = G_width / ( self.doc:getPages() ) - local page_indicator = function() - fb.bb:invertRect( pg_w*(no-1),0, pg_w,10) - fb:refresh(1, pg_w*(no-1),0, pg_w,10) - Debug('page_indicator',no) + local page_indicator = function() + if Debug('page_indicator',no) then + fb.bb:invertRect( pg_w*(no-1),0, pg_w,10) + fb:refresh(1, pg_w*(no-1),0, pg_w,10) + end end page_indicator() From 9cc106995d15252267762cf13a38d51d523167a2 Mon Sep 17 00:00:00 2001 From: NiLuJe Date: Wed, 3 Oct 2012 00:29:10 +0200 Subject: [PATCH 13/21] Properly fix the luajit *FLAGS mess --- Makefile | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index fa034ec45..8923ab1db 100644 --- a/Makefile +++ b/Makefile @@ -30,15 +30,16 @@ endif HOSTCC:=gcc HOSTCXX:=g++ -# Base CFLAGS, without arch. Will use it as-is for luajit, because its buildsystem picks up the wrong flags, possibly from my env... +# Base CFLAGS, without arch. We'll need it for luajit, because its Makefiles do some tricky stuff to differentiate HOST/TARGET BASE_CFLAGS:=-O2 -ffast-math -pipe -fomit-frame-pointer -fno-stack-protector -U_FORTIFY_SOURCE +# Use this for debugging: +#BASE_CFLAGS:=-O0 -g +ARM_ARCH:=-march=armv6j -mtune=arm1136jf-s -mfpu=vfp +HOST_ARCH:=-march=native +HOSTCFLAGS:=$(HOST_CFLAGS) $(BASE_CFLAGS) CFLAGS:=$(BASE_CFLAGS) CXXFLAGS:=$(BASE_CFLAGS) -fno-use-cxa-atexit LDFLAGS:=-Wl,-O1 -Wl,--as-needed -ARM_CFLAGS:=-march=armv6j -mtune=arm1136jf-s -mfpu=vfp -HOSTCFLAGS:=-O2 -march=native -ffast-math -pipe -fomit-frame-pointer -# use this for debugging: -#CFLAGS:=-O0 -g DYNAMICLIBSTDCPP:=-lstdc++ ifdef STATICLIBSTDCPP @@ -62,9 +63,11 @@ ifdef EMULATE_READER ifeq "$(shell uname -s -m)" "Darwin x86_64" EMU_LDFLAGS += -pagezero_size 10000 -image_base 100000000 endif + CFLAGS+= $(HOST_ARCH) + CXXFLAGS+= $(HOST_ARCH) else - CFLAGS+= $(ARM_CFLAGS) - CXXFLAGS+= $(ARM_CFLAGS) + CFLAGS+= $(ARM_ARCH) + CXXFLAGS+= $(ARM_ARCH) endif # standard includes @@ -175,7 +178,7 @@ clean: rm -f *.o kpdfview slider_watcher cleanthirdparty: - $(MAKE) -C $(LUADIR) clean CFLAGS="" + $(MAKE) -C $(LUADIR) CC="$(HOSTCC)" HOST_CC="$(HOSTCC) -m32" CROSS="$(CHOST)-" clean $(MAKE) -C $(MUPDFDIR) build="release" clean $(MAKE) -C $(CRENGINEDIR)/thirdparty/antiword clean test -d $(CRENGINEDIR)/thirdparty/chmlib && $(MAKE) -C $(CRENGINEDIR)/thirdparty/chmlib clean || echo warn: chmlib folder not found @@ -219,7 +222,8 @@ $(LUALIB): ifdef EMULATE_READER $(MAKE) -C $(LUADIR) else - $(MAKE) -C $(LUADIR) CC="$(HOSTCC)" HOST_CC="$(HOSTCC) -m32" CFLAGS="$(BASE_CFLAGS)" HOST_CFLAGS="$(BASE_CFLAGS)" TARGET_CFLAGS="$(CFLAGS)" CROSS="$(CHOST)-" TARGET_FLAGS="-DLUAJIT_NO_LOG2 -DLUAJIT_NO_EXP2" + # To recap: build its TARGET_CC from CROSS+CC, so we need HOSTCC in CC. Build its HOST/TARGET_CFLAGS based on CFLAGS, so we need a neutral CFLAGS without arch + $(MAKE) -C $(LUADIR) CC="$(HOSTCC)" HOST_CC="$(HOSTCC) -m32" CFLAGS="$(BASE_CFLAGS)" HOST_CFLAGS="$(HOSTCFLAGS)" TARGET_CFLAGS="$(CFLAGS)" CROSS="$(CHOST)-" TARGET_FLAGS="-DLUAJIT_NO_LOG2 -DLUAJIT_NO_EXP2" endif $(POPENNSLIB): From 4356622186e158a21e0af9d853ef47cf9ff3625d Mon Sep 17 00:00:00 2001 From: NiLuJe Date: Wed, 3 Oct 2012 00:34:42 +0200 Subject: [PATCH 14/21] Don't patch popen-noshell twice --- Makefile | 2 +- popen-noshell/popen_noshell-buildfix.patch | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 8923ab1db..5f98a1205 100644 --- a/Makefile +++ b/Makefile @@ -172,7 +172,7 @@ fetchthirdparty: cd mupdf && patch -N -p1 < ../mupdf.patch svn co http://popen-noshell.googlecode.com/svn/trunk/ popen-noshell # popen_noshell patch: Make it build on recent TCs, and implement a simple Makefile for building it as a static lib - cd popen-noshell && patch -N -p0 < popen_noshell-buildfix.patch + cd popen-noshell && tesf -f Makefile || patch -N -p0 < popen_noshell-buildfix.patch clean: rm -f *.o kpdfview slider_watcher diff --git a/popen-noshell/popen_noshell-buildfix.patch b/popen-noshell/popen_noshell-buildfix.patch index 6f9ba1bf8..97c09b881 100644 --- a/popen-noshell/popen_noshell-buildfix.patch +++ b/popen-noshell/popen_noshell-buildfix.patch @@ -15,7 +15,7 @@ Index: Makefile =================================================================== --- Makefile (revision 0) +++ Makefile (working copy) -@@ -0,0 +1,15 @@ +@@ -0,0 +1,17 @@ +SRCS=popen_noshell.c + +OBJS:=$(SRCS:%.c=%.o) @@ -31,6 +31,8 @@ Index: Makefile +clean: + rm -rf *.o + rm -rf libpopen_noshell.a ++ ++.PHONY: clean Property changes on: Makefile ___________________________________________________________________ From 7bc80277e4711cdaa55c52df563aae38296d9cf9 Mon Sep 17 00:00:00 2001 From: NiLuJe Date: Wed, 3 Oct 2012 00:36:50 +0200 Subject: [PATCH 15/21] Don't checkout popen_noshell twice (it reverts parts of our patch) --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 5f98a1205..a82a81871 100644 --- a/Makefile +++ b/Makefile @@ -170,9 +170,9 @@ fetchthirdparty: patch -N -p0 < ../../../kpvcrlib/jpeg_decompress_struct_size.patch # MuPDF patch: use external fonts cd mupdf && patch -N -p1 < ../mupdf.patch - svn co http://popen-noshell.googlecode.com/svn/trunk/ popen-noshell + test -f popen-noshell/popen_noshell.c || svn co http://popen-noshell.googlecode.com/svn/trunk/ popen-noshell # popen_noshell patch: Make it build on recent TCs, and implement a simple Makefile for building it as a static lib - cd popen-noshell && tesf -f Makefile || patch -N -p0 < popen_noshell-buildfix.patch + cd popen-noshell && test -f Makefile || patch -N -p0 < popen_noshell-buildfix.patch clean: rm -f *.o kpdfview slider_watcher From 00c1f2ec9cba0a1a97713fa78fcc6daf57bfa7bd Mon Sep 17 00:00:00 2001 From: NuPogodi Date: Wed, 3 Oct 2012 00:45:45 +0200 Subject: [PATCH 16/21] New InfoMessage Concept Part I (still no unireader, crereader, filesearcher & fileinfo) --- commands.lua | 4 +- dialog.lua | 226 ++++++++++++++++++++- filechooser.lua | 379 +++++++++++++++++++++-------------- filehistory.lua | 202 +++++++++++-------- font.lua | 2 - inputbox.lua | 32 +-- kite/KPDFviewer (last file) | 2 +- kite/KPDFviewer File Manager | 2 +- kpdf.sh | 37 ++++ launchpad/kpdf.ini | 8 +- reader.lua | 9 +- resources/info-aux.png | Bin 0 -> 1109 bytes resources/info-bug.png | Bin 0 -> 1037 bytes resources/info-confirm.png | Bin 0 -> 858 bytes resources/info-error.png | Bin 0 -> 950 bytes resources/info-i.png | Bin 538 -> 0 bytes resources/info-warn.png | Bin 0 -> 844 bytes screen.lua | 4 +- selectmenu.lua | 58 +++--- 19 files changed, 668 insertions(+), 297 deletions(-) create mode 100644 kpdf.sh create mode 100644 resources/info-aux.png create mode 100644 resources/info-bug.png create mode 100644 resources/info-confirm.png create mode 100644 resources/info-error.png delete mode 100644 resources/info-i.png create mode 100644 resources/info-warn.png diff --git a/commands.lua b/commands.lua index 4957552be..bbce7bc91 100644 --- a/commands.lua +++ b/commands.lua @@ -168,7 +168,7 @@ function Commands:new(obj) --os.execute("echo 'screensaver in' >> /mnt/us/event_test.txt") if G_charging_mode == false and G_screen_saver_mode == false then Screen:saveCurrentBB() - InfoMessage:show("Going into screensaver... ", 0) + InfoMessage:inform("Going into screensaver... ", nil, 0, MSG_AUX) Screen.kpv_rotation_mode = Screen.cur_rotation_mode fb:setOrientation(Screen.native_rotation_mode) util.sleep(1) @@ -199,7 +199,7 @@ function Commands:new(obj) Screen:saveCurrentBB() Screen.kpv_rotation_mode = Screen.cur_rotation_mode fb:setOrientation(Screen.native_rotation_mode) - InfoMessage:show("Going into USB mode... ", 0) + InfoMessage:inform("Going into USB mode... ", nil, 0, MSG_AUX) util.sleep(1) os.execute("killall -cont cvm") end diff --git a/dialog.lua b/dialog.lua index 632b9b146..b46303d90 100644 --- a/dialog.lua +++ b/dialog.lua @@ -1,7 +1,43 @@ require "widget" require "font" +-- some definitions for developers +MSG_AUX = 1 +MSG_WARN = 2 +MSG_ERROR = 3 +MSG_CONFIRM = 4 +MSG_BUG = 5 + InfoMessage = { + InfoMethod = { --[[ + The items define how to inform user about various types of events, the values should be 0..3: + the lowest bit is responcible for showing popup windows, + while the 2nd bit allows using TTS-voice messages. + The current default values {1,1,1,1,1} correspond to previous kpdfviewer-settings + when every message is shown in popup window. ]] + 1, -- auxiliary messages = 0 or 2 (nothing or TTS) + 1, -- warnings = 1 or 2 (popup or TTS) + 1, -- errors = 1 or 3 (popup or popup & TTS) + 1, -- confirmations (must not be silent!) = 1 or 3 (popup or popup & TTS) + 1, -- bugs (must not be silent!) = 1 or 3 (popup or popup & TTS) + }, + -- images for various message types + Images = { + "resources/info-aux.png", + "resources/info-warn.png", + "resources/info-error.png", + "resources/info-confirm.png", + "resources/info-bug.png", + }, + ImageFile = "resources/info-warn.png", + -- TTS-related parameters + TTSspeed = nil, + SoundVolume = nil, + + --[[ as Kindle3-volume has nonlinear scale, one need to define the 'VolumeLevels'-values + TODO: test whether VolumeLevels are different for other sound-equipped Kindle models (K2, KDX) + by running self.VolumeLevels = self:getVolumeLevels() ]] + VolumeLevels = { 0, 1, 2, 4, 6, 7, 14, 21, 28, 35, 42, 49, 56, 63, 70, 77} } function InfoMessage:show(text,refresh_mode) @@ -14,7 +50,7 @@ function InfoMessage:show(text,refresh_mode) HorizontalGroup:new({ align = "center", ImageWidget:new({ - file = "resources/info-i.png" + file = self.ImageFile }), Widget:new({ dimen = { w = 10, h = 0 } @@ -54,3 +90,191 @@ function showInfoMsgWithDelay(text, msec, refresh_mode) Screen:restoreFromSavedBB() fb:refresh(refresh_mode) end + +--[[ Unified function to inform user about smth. It is generally intended to replace showInfoMsgWithDelay() & InfoMessage:show(). +Since the former function is used in Lua-code more often than the latter, I kept here the same sequence of parameters as used in +showInfoMsgWithDelay() + added two not obligatory parameters (see description below). + +Thus, this trick allows multiple text replaces thoughout the whole Lua-code: 'showInfoMsgWithDelay(' to 'InfoMessage:inform(' +Yet, it would be much better to accompany such replaces by adding the 'message_importance' and, if needed, by +'alternative_voice_message' that is not so restricted in length as 'text' + +But replacing InfoMessage:show(...) by InfoMessage:inform(...) MUST be accompanied by adding the 2nd parameter -- either msec=nil or msec=0. +Adding new parameters -- 'message_importance' & 'alternative_voice_message' -- is also appreciated. + +Brief description of the function parameters +-- text : is the text message for visual and (if 'alternative_voice_message' isn't defined) voice notification +-- msec : parameter to define visual notification method + msec=0: one calls InfoMessage:show(), otherwise one calls showInfoMsgWithDelay() + msec>0: with either predefines msec-value or + msec<0: autocalculated from the text length: one may eventually add user-configurable + parameter 'reading_speed' that would define the popup exposition for this regime +-- message_importance : parameter separating various messages on + 1 - not obligatory messages that might be readily avoided + 2 - warnings (important messages) + 3 - errors + 4 - confirmations + 5 - bugs +-- alternative_voice_message: not obligatory parameter that allows to send longer messages to TTS-engine + if not defined, the default 'text' will be TTS-voiced +]] + +function InfoMessage:inform(text, msec, refresh_mode, message_importance, alternative_voice_message) + -- temporary test for 'message_importance'; it might be further removed as soon + -- as every message will be properly marked by 'importance' + if not message_importance then message_importance = 5 end + local popup, voice = InfoMessage:getMethodForEvent(message_importance) + if voice then + alternative_voice_message = alternative_voice_message or text + say(alternative_voice_message) + -- here one may set pause -- it might be useful only if one sound message + -- is directly followed by another, otherwise it's just wasting of time. + --[[ if msec and msec ~=0 then + -- pause = 0.5sec + 40ms/character * string.len() / normalized_voice_speed + util.usleep(500000 + 40*alternative_voice_message:len*10000/self.TTSspeed) + end ]] + end + if not popup then return end -- to avoid drawing popup window + self.ImageFile = self.Images[message_importance] -- select proper image for window + if not msec or msec == 0 then + InfoMessage:show(text, refresh_mode) + else + if msec < 0 then msec = 500 + string.len(text) * 50 end + showInfoMsgWithDelay(text, msec, refresh_mode) + end +end + +function InfoMessage:getMethodForEvent(event) + local popup, voice = true, true + if self.InfoMethod[event] %2 == 0 then popup = false end + if self.InfoMethod[event] < 2 then voice = false end + return popup, voice +end + +-- GUI-methods for user to choose the way to inform about various events -- + +function InfoMessage:chooseMethodForEvent(event) + local popup, voice = self:getMethodForEvent(event) + local items_menu = SelectMenu:new{ + menu_title = "Choose the way how to inform you", + item_array = {"Avoid any notifications", + "Show popup window", + "Use TTS-voice", + "Popup window and TTS-voice", + }, + current_entry = (popup and 1 or 0) + (voice and 1 or 0) * 2, + } + local item_no = items_menu:choose(0, fb.bb:getHeight()) + if item_no then + self.InfoMethod[event] = item_no - 1 + -- just to illustrate the way how the selected method works; might be removed + self:inform("Event = "..event..", Method = "..self.InfoMethod[event], -1500, 1, event, + "You have chosen the method number "..self.InfoMethod[event].." for the event item number "..event) + end +end + +function InfoMessage:chooseEventForMethod(event) + event = event or 0 + local item_no = 0 + local event_list = { + "Messages (e.g. 'Scanning folder...')", + "Warnings (e.g. 'Already first jump!')", + "Errors (e.g. 'Zip contains improper content!')", + "Confirmations (e.g. 'Press Y to confirm deleting')", + "Bugs", + } + while item_no ~= event and item_no < #event_list do + item_no = item_no + 1 + end + local event_menu = SelectMenu:new{ + menu_title = "Select the event type to tune", + item_array = event_list, + current_entry = item_no - 1, + } + item_no = event_menu:choose(0, G_height) + return item_no +end + +function InfoMessage:chooseNotificatonMethods() + local event = 1 -- default: auxiliary messages + while event do + event = self:chooseEventForMethod(event) + if event then self:chooseMethodForEvent(event) end + end +end + +---------------- audio-related functions ---------------- + +function InfoMessage:incrTTSspeed(direction) -- either +1 or -1 + -- make sure new TTS-speed is within reasonable range + self.TTSspeed = math.max(self.TTSspeed + direction * 20, 40) -- min = 40% + self.TTSspeed = math.min(self.TTSspeed, 200) -- max = 200% + -- set new value & give an example for more convenient tuning + os.execute('lipc-set-prop com.lab126.tts TtsISpeed '..self.TTSspeed) + say("The current voice speed is "..self.TTSspeed.." percent.") +end + +function InfoMessage:getTTSspeed() + local tmp = io.popen('lipc-get-prop com.lab126.tts TtsISpeed', "r") + local speed = tmp:read("*number") + tmp:close() + return speed or 100 -- nominal TTS-speed +end + +function InfoMessage:incrSoundVolume(direction) -- either +1 or -1 + -- make sure that new volume is within reasonable range + self.SoundVolume = math.max(self.SoundVolume + direction, 1) + self.SoundVolume = math.min(self.SoundVolume, #self.VolumeLevels) + -- set new value & give an example for more convenient tuning + os.execute('lipc-set-prop com.lab126.audio Volume '..(self.SoundVolume-1)) + -- that is not exactly the volume percents, but more conventional values, + -- than abstract units returned by 'lipc-get-prop com.lab126.audio Volume' + local percents = math.floor(100*self.VolumeLevels[self.SoundVolume]/self.VolumeLevels[#self.VolumeLevels]) + say("The current sound volume is "..percents.." percent.") +end + +function InfoMessage:getSoundVolume() + local tmp = io.popen('lipc-get-prop com.lab126.audio Volume', "r") + local volume = tmp:read("*number") + tmp:close() + local i = 1 + while self.VolumeLevels[i] < volume and i < #self.VolumeLevels do + i = i + 1 + end + return i or 16 -- maximum volume +end + +--[[ -- to determine self.VolumeLevels in various Kindle-models +function InfoMessage:getVolumeLevels() + local levels, v, i = {}, 0, 0 + while i < 16 do -- proper K3-range 0..15 might be different for other models + os.execute('lipc-set-prop com.lab126.audio Volume '..i) + v = self:getSoundVolume() + table.insert(levels, v) + i = i + 1 + end + return levels +end ]] + +function say(text) + os.execute("say \""..text.."\"") +end + +-- The read/write global InfoMessage settings. When properly tested, the +-- condition 'if FileChooser.filemanager_expert_mode == ...' might be deleted + +function InfoMessage:initInfoMessageSettings() + if FileChooser.filemanager_expert_mode == FileChooser.ROOT_MODE then + InfoMessage.InfoMethod = G_reader_settings:readSetting("info_message_methods") or InfoMessage.InfoMethod + InfoMessage.TTSspeed = G_reader_settings:readSetting("tts_speed") or InfoMessage:getTTSspeed() + InfoMessage.SoundVolume = G_reader_settings:readSetting("sound_volume") or InfoMessage:getSoundVolume() + end +end + +function InfoMessage:saveInfoMessageSettings() + if FileChooser.filemanager_expert_mode == FileChooser.ROOT_MODE then + G_reader_settings:saveSetting("info_message_methods", InfoMessage.InfoMethod) + G_reader_settings:saveSetting("sound_volume", InfoMessage.SoundVolume-1) + G_reader_settings:saveSetting("tts_speed", InfoMessage.TTSspeed) + end +end diff --git a/filechooser.lua b/filechooser.lua index fadec7e33..7cde6e144 100644 --- a/filechooser.lua +++ b/filechooser.lua @@ -29,33 +29,27 @@ FileChooser = { pagedirty = true, markerdirty = false, perpage, + clipboard = lfs.currentdir() .. "/clipboard", -- NO finishing slash + before_clipboard, -- NuPogodi, 30.09.12: to store the path where jump to clipboard was made from - -- NuPogodi, 04.09.2012: introduced modes that configures the filechoser - -- for users with various purposes & skills + -- modes that configures the filechoser for users with various purposes & skills filemanager_expert_mode, -- default value is defined in reader.lua -- the definitions BEGINNERS_MODE = 1, -- the filemanager content is restricted by files with reader-related extensions; safe renaming (no extension) ADVANCED_MODE = 2, -- no extension-based filtering; renaming with extensions; appreciable danger to crash crengine by improper docs ROOT_MODE = 3, -- TODO: all functions (including non-stable and dangerous) - } -function getProperTitleLength(txt,font_face,max_width) - local tw = TextWidget:new({ text = txt, face = font_face}) - -- 1st approximation for a point where to start title - local n = math.floor(string.len(txt) * (1 - max_width / tw:getSize().w)) - 2 - n = math.max(n, 1) - while tw:getSize().w >= max_width do - tw:free() - tw = TextWidget:new({ text = string.sub(txt,n,-1), face = font_face}) - n = n + 1 +-- NuPogodi, 29.09.12: simplified the code +function getProperTitleLength(txt, font_face, max_width) + while sizeUtf8Text(0, G_width, font_face, txt, true).x > max_width do + txt = txt:sub(2, -1) end - return string.sub(txt,n-1,-1) + return txt end function BatteryLevel() - -- NuPogodi, 18.05.12: This command seems to work even without Amazon Kindle framework local p = io.popen("gasgauge-info -s 2> /dev/null", "r") -- io.popen() _never_ fails! local battery = p:read("*a") or "?" if battery == "" then battery = "?" end @@ -63,36 +57,30 @@ function BatteryLevel() return string.gsub(battery, "[\n\r]+", "") end +-- NuPogodi, 29.09.12: avoid using widgets function DrawTitle(text,lmargin,y,height,color,font_face) local r = 6 -- radius for round corners color = 3 -- redefine to ignore the input for background color - - fb.bb:paintRect(1, 1, fb.bb:getWidth() - 2, height - r, color) - blitbuffer.paintBorder(fb.bb, 1, height/2, fb.bb:getWidth() - 2, height/2, height/2, color, r) - -- to have a horisontal gap between text & background rectangle - t = BatteryLevel() .. os.date(" %H:%M") - local tw = TextWidget:new({ text = t, face = font_face}) - twidth = tw:getSize().w - renderUtf8Text(fb.bb, fb.bb:getWidth()-twidth-lmargin, height-10, font_face, t, true) - tw:free() - - tw = TextWidget:new({ text = text, face = font_face}) - local max_width = fb.bb:getWidth() - 2*lmargin - twidth - if tw:getSize().w < max_width then + fb.bb:paintRect(1, 1, G_width-2, height - r, color) + blitbuffer.paintBorder(fb.bb, 1, height/2, G_width-2, height/2, height/2, color, r) + local t = BatteryLevel() .. os.date(" %H:%M") + r = sizeUtf8Text(0, G_width, font_face, t, true).x + renderUtf8Text(fb.bb, G_width-r-lmargin, height-10, font_face, t, true) + r = G_width - r - 2 * lmargin - 10 -- let's leave small gap + if sizeUtf8Text(0, G_width, font_face, text, true).x <= r then renderUtf8Text(fb.bb, lmargin, height-10, font_face, text, true) else - local w = renderUtf8Text(fb.bb, lmargin, height-10, font_face, "...", true) - local txt = getProperTitleLength(text, font_face, max_width-w) - renderUtf8Text(fb.bb, w+lmargin, height-10, font_face, txt, true) + t = renderUtf8Text(fb.bb, lmargin, height-10, font_face, "...", true) + text = getProperTitleLength(text, font_face, r-t) + renderUtf8Text(fb.bb, lmargin+t, height-10, font_face, text, true) end - tw:free() end function DrawFooter(text,font_face,h) local y = G_height - 7 - -- NuPogodi, 29.09.12: just dirty fix to have the same footer everywhere + -- just dirty fix to have the same footer everywhere local x = FileChooser.margin_H --(G_width / 2) - 50 - renderUtf8Text(fb.bb, x, y, font_face, text.." - Press H for help", true) + renderUtf8Text(fb.bb, x, y, font_face, text.." - Press H for help", true) end function DrawFileItem(name,x,y,image) @@ -106,11 +94,11 @@ function DrawFileItem(name,x,y,image) -- then drawing filenames local cface = Font:getFace("cfont", 22) local xleft = x + iw:getSize().w + 9 -- the gap between icon & filename - local width = fb.bb:getWidth() - xleft - x + local width = G_width - xleft - x -- now printing the name - if sizeUtf8Text(xleft, fb.bb:getWidth() - x, cface, name, true).x < width then + if sizeUtf8Text(xleft, G_width - x, cface, name, true).x < width then renderUtf8Text(fb.bb, xleft, y, cface, name, true) - else + else local lgap = sizeUtf8Text(0, width, cface, " ...", true).x local handle = renderUtf8TextWidth(fb.bb, xleft, y, cface, name, true, width - lgap - x) renderUtf8Text(fb.bb, handle.x + lgap + x, y, cface, " ...", true) @@ -162,6 +150,10 @@ end function FileChooser:setPath(newPath) local curr_path = self.path + if self.before_clipboard then -- back from clipboard + newPath = self.before_clipboard + self.before_clipboard = nil + end self.path = getAbsolutePath(newPath) local readdir_ok, exc = pcall(self.readDir,self) if(not readdir_ok) then @@ -173,8 +165,20 @@ function FileChooser:setPath(newPath) if self.items == 0 then return nil end - self.page = 1 - self.current = 1 + -- NuPogodi, 02.10.12: 1) changing the current item position ONLY IF the path was changed + -- 2) trying to set marker under the folder that was just left by ".." + if self.path ~= curr_path then + local i = 2 + while i <= #self.dirs and not string.find(curr_path, self.dirs[i]) do + i = i + 1 + end + if i <= #self.dirs then -- found + self.current, self.page = gotoTargetItem(i, self.items, self.current, self.page, self.perpage) + else -- set defaults + self.page = 1 + self.current = 1 + end + end return true end end @@ -210,7 +214,7 @@ function FileChooser:choose(ypos, height) local msg = self.exception_message and self.exception_message:match("[^%:]+:%d+: (.*)") or self.path self.exception_message = nil -- draw header - DrawTitle(msg,self.margin_H,ypos,self.title_H,4,tface) + DrawTitle(msg,self.margin_H,ypos,self.title_H,3,tface) self.markerdirty = true end @@ -260,7 +264,7 @@ function FileChooser:choose(ypos, height) end -- while end --- NuPogodi, 20.05.12: add available commands +-- add available commands function FileChooser:addAllCommands() self.commands = Commands:new{} @@ -270,35 +274,8 @@ function FileChooser:addAllCommands() self.pagedirty = true end ) - self.commands:add({KEY_PGFWD, KEY_LPGFWD}, nil, ">", - "goto next page", - function(self) - if self.page < (self.items / self.perpage) then - if self.current + self.page*self.perpage > self.items then - self.current = self.items - self.page*self.perpage - end - self.page = self.page + 1 - self.pagedirty = true - else - self.current = self.items - (self.page-1)*self.perpage - self.markerdirty = true - end - end - ) - self.commands:add({KEY_PGBCK, KEY_LPGBCK}, nil, "<", - "goto previous page", - function(self) - if self.page > 1 then - self.page = self.page - 1 - self.pagedirty = true - else - self.current = 1 - self.markerdirty = true - end - end - ) self.commands:add(KEY_FW_DOWN, nil, "joypad down", - "goto next item", + "next item", function(self) if self.current == self.perpage then if self.page < (self.items / self.perpage) then @@ -316,7 +293,7 @@ function FileChooser:addAllCommands() end ) self.commands:add(KEY_FW_UP, nil, "joypad up", - "goto previous item", + "previous item", function(self) if self.current == 1 then if self.page > 1 then @@ -330,16 +307,69 @@ function FileChooser:addAllCommands() end end ) - self.commands:add({KEY_FW_RIGHT, KEY_I}, nil, "joypad right", + -- NuPogodi, 01.10.12: fast jumps to items at positions 10, 20, .. 90, 0% within the list + local numeric_keydefs, i = {} + for i=1, 10 do numeric_keydefs[i]=Keydef:new(KEY_1+i-1, nil, tostring(i%10)) end + self.commands:addGroup("[1, 2 .. 9, 0]", numeric_keydefs, + "item at position 0%, 10% .. 90%, 100%", + function(self) + local target_item = math.ceil(self.items * (keydef.keycode-KEY_1) / 9) + self.current, self.page, self.markerdirty, self.pagedirty = + gotoTargetItem(target_item, self.items, self.current, self.page, self.perpage) + end + ) + self.commands:add({KEY_PGFWD, KEY_LPGFWD}, nil, ">", + "next page", + function(self) + if self.page < (self.items / self.perpage) then + if self.current + self.page*self.perpage > self.items then + self.current = self.items - self.page*self.perpage + end + self.page = self.page + 1 + self.pagedirty = true + else + self.current = self.items - (self.page-1)*self.perpage + self.markerdirty = true + end + end + ) + self.commands:add({KEY_PGBCK, KEY_LPGBCK}, nil, "<", + "previous page", + function(self) + if self.page > 1 then + self.page = self.page - 1 + self.pagedirty = true + else + self.current = 1 + self.markerdirty = true + end + end + ) + self.commands:add(KEY_G, nil, "G", -- NuPogodi, 01.10.12: goto page No. + "goto page", + function(self) + local n = math.ceil(self.items / self.perpage) + local page = NumInputBox:input(G_height-100, 100, "Page:", "current page "..self.page.." of "..n, true) + if pcall(function () page = math.floor(page) end) -- convert string to number + and page ~= self.page and page > 0 and page <= n then + self.page = page + if self.current + (page-1)*self.perpage > self.items then + self.current = self.items - (page-1)*self.perpage + end + end + self.pagedirty = true + end + ) + self.commands:add(KEY_FW_RIGHT, nil, "joypad right", "show document information", function(self) local folder = self.dirs[self.perpage*(self.page-1)+self.current] if folder then if folder == ".." then - showInfoMsgWithDelay(" ", 1000, 1) + warningUnsupportedFunction() else folder = self.path.."/"..folder - if FileInfo:show(folder) == "goto" then + if FileInfo:show(folder) == "goto" then self:setPath(folder) end end @@ -365,39 +395,29 @@ function FileChooser:addAllCommands() self.pagedirty = true end ) --- NuPogodi, 23.05.12: modified to delete both files and empty folders + -- modified to delete both files and empty folders self.commands:add(KEY_DEL, nil, "Del", "delete selected item", function(self) local pos = self.perpage*(self.page-1)+self.current - local folder = self.dirs[pos] - if folder == ".." then - showInfoMsgWithDelay(".. cannot be deleted! ",1500,1) - elseif folder then - InfoMessage:show("Press \'Y\' to confirm",0) - if self:ReturnKey() == KEY_Y then - if lfs.rmdir(self.path.."/"..folder) then - table.remove(self.dirs, offset) - self:setPath(self.path) - else - showInfoMsgWithDelay("Cannot be deleted!",1500,1) - end + local confirm = "Please, press key Y to confirm deleting" + if pos > #self.dirs then -- file + if InfoMessage.InfoMethod[MSG_CONFIRM] == 0 then -- silent regime + self:deleteFileAtPosition(pos) + else + InfoMessage:inform("Press 'Y' to confirm ", nil, 0, MSG_CONFIRM, confirm) + if self:ReturnKey() == KEY_Y then self:deleteFileAtPosition(pos) end end - else - InfoMessage:show("Press \'Y\' to confirm",0) - if self:ReturnKey() == KEY_Y then - pos = pos - #self.dirs - local fullpath = self.path.."/"..self.files[pos] - -- delete the file itself - os.remove(fullpath) - -- and its history file, if any - os.remove(DocToHistory(fullpath)) - -- to avoid showing just deleted file - table.remove(self.files, pos) - self.items = self.items - 1 - self.current = self.current - 1 + elseif self.dirs[pos] == ".." then + warningUnsupportedFunction() + else -- other folders + if InfoMessage.InfoMethod[MSG_CONFIRM] == 0 then -- silent regime + self:deleteFolderAtPosition(pos) + else + InfoMessage:inform("Press 'Y' to confirm ", nil, 0, MSG_CONFIRM, confirm) + if self:ReturnKey() == KEY_Y then self:deleteFolderAtPosition(pos) end end - end -- if folder == ".." + end self.pagedirty = true end -- function ) @@ -427,14 +447,48 @@ function FileChooser:addAllCommands() end end ) - -- NuPogodi, 04.09.12: menu to switch the filechooser mode self.commands:add(KEY_M, MOD_ALT, "M", "set mode for filemanager", function(self) self:changeFileChooserMode() end ) - self.commands:add({KEY_F, KEY_AA}, nil, "F", +-- NuPogodi, 25.09.12: new functions to tune the way how to inform user about the reader events + local popup_text = "Unstable... For experts only! " + local voice_text = "This function is still under development and available only for experts and beta testers." + self.commands:add(KEY_I, nil, "I", + "change the way to inform about events", + function(self) + if self.filemanager_expert_mode == self.ROOT_MODE then + InfoMessage:chooseNotificatonMethods() + self.pagedirty = true + else + InfoMessage:inform(popup_text, -1, 1, MSG_WARN, voice_text) + end + end + ) + self.commands:addGroup("Vol-/+", {Keydef:new(KEY_VPLUS,nil), Keydef:new(KEY_VMINUS,nil)}, + "decrease/increase sound volume", + function(self) + if self.filemanager_expert_mode == self.ROOT_MODE then + InfoMessage:incrSoundVolume(keydef.keycode == KEY_VPLUS and 1 or -1) + else + InfoMessage:inform(popup_text, -1, 1, MSG_WARN, voice_text) + end + end + ) + self.commands:addGroup(MOD_SHIFT.."Vol-/+", {Keydef:new(KEY_VPLUS,MOD_SHIFT), Keydef:new(KEY_VMINUS,MOD_SHIFT)}, + "decrease/increase TTS-engine speed", + function(self) + if self.filemanager_expert_mode == self.ROOT_MODE then + InfoMessage:incrTTSspeed(keydef.keycode == KEY_VPLUS and 1 or -1) + else + InfoMessage:inform(popup_text, -1, 1, MSG_WARN, voice_text) + end + end + ) +------------ end of changes (NuPogodi, 25.09.12) ------------ + self.commands:add({KEY_F, KEY_AA}, nil, "F, Aa", "change font faces", function(self) Font:chooseFonts() @@ -447,7 +501,7 @@ function FileChooser:addAllCommands() HelpPage:show(0, G_height, self.commands) self.pagedirty = true end - ) + ) self.commands:add(KEY_L, nil, "L", "show last documents", function(self) @@ -462,16 +516,17 @@ function FileChooser:addAllCommands() function(self) local keywords = InputBox:input(0, 0, "Search:") if keywords then - InfoMessage:show("Searching... ",0) + InfoMessage:inform("Searching... ", nil, 1, MSG_AUX) FileSearcher:init( self.path ) FileSearcher:choose(keywords) end self.pagedirty = true - end -- function + end ) self.commands:add(KEY_C, MOD_SHIFT, "C", - "copy file to \'clipboard\'", + "copy file to 'clipboard'", function(self) + -- TODO (NuPogodi, 27.09.12): overwrite? local file = self:FullFileName() if file then lfs.mkdir(self.clipboard) @@ -479,29 +534,34 @@ function FileChooser:addAllCommands() local fn = self.files[self.perpage*(self.page-1)+self.current - #self.dirs] os.execute("cp "..self:InQuotes(DocToHistory(file)).." " ..self:InQuotes(DocToHistory(self.clipboard.."/"..fn)) ) - showInfoMsgWithDelay("File copied to clipboard ", 1000, 1) + InfoMessage:inform("File copied to clipboard ", 1000, 1, MSG_WARN, + "The file has been copied to clipboard.") end end - ) + ) self.commands:add(KEY_X, MOD_SHIFT, "X", - "move file to \'clipboard\'", + "move file to 'clipboard'", function(self) + -- TODO (NuPogodi, 27.09.12): overwrite? local file = self:FullFileName() if file then lfs.mkdir(self.clipboard) local fn = self.files[self.perpage*(self.page-1)+self.current - #self.dirs] os.rename(file, self.clipboard.."/"..fn) os.rename(DocToHistory(file), DocToHistory(self.clipboard.."/"..fn)) - InfoMessage:show("File moved to clipboard ", 0) + InfoMessage:inform("File moved to clipboard ", nil, 0, MSG_WARN, + "The file has been moved to clipboard.") self:setPath(self.path) self.pagedirty = true end end ) self.commands:add(KEY_V, MOD_SHIFT, "V", - "paste file(s) from \'clipboard\'", + "paste file(s) from 'clipboard'", function(self) - InfoMessage:show("moving file(s) from clipboard ", 0) + -- TODO (NuPogodi, 27.09.12): first test whether the clipboard is empty & answer respectively + -- TODO (NuPogodi, 27.09.12): overwrite? + InfoMessage:inform("Moving files from clipboard...", nil, 0, MSG_AUX) for f in lfs.dir(self.clipboard) do if lfs.attributes(self.clipboard.."/"..f, "mode") == "file" then os.rename(self.clipboard.."/"..f, self.path.."/"..f) @@ -513,11 +573,15 @@ function FileChooser:addAllCommands() end ) self.commands:add(KEY_B, MOD_SHIFT, "B", - "show content of \'clipboard\'", + "show content of 'clipboard'", function(self) + -- NuPogodi, 30.09.12: exit back from clipboard to last folder by '..' + local current_path = self.path lfs.mkdir(self.clipboard) - self:setPath(self.clipboard) - -- TODO: exit back from clipboard to last folder - Redefine Exit on FW_Right? + if self.clipboard ~= self.path then + self:setPath(self.clipboard) + self.before_clipboard = current_path + end self.pagedirty = true end ) @@ -536,13 +600,12 @@ function FileChooser:addAllCommands() self.commands:add(KEY_K, MOD_SHIFT, "K", "run calculator", function(self) - local CalcBox = InputBox:new{ calcmode = true } + local CalcBox = InputBox:new{ inputmode = MODE_CALC } CalcBox:input(0, 0, "Calc ") self.pagedirty = true end ) - self.commands:add(KEY_HOME, nil, "Home", - "exit", + self.commands:addGroup("Home, Alt + Back", { Keydef:new(KEY_HOME, nil),Keydef:new(KEY_BACK, MOD_ALT)}, "exit", function(self) return "break" end @@ -551,19 +614,14 @@ end -- returns full filename or nil (if folder) function FileChooser:FullFileName() - local file - local folder = self.dirs[self.perpage*(self.page-1)+self.current] - if folder == ".." then - showInfoMsgWithDelay(" ",1000,1) - elseif folder then - showInfoMsgWithDelay(" ",1000,1) - else - file=self.path.."/"..self.files[self.perpage*(self.page-1)+self.current - #self.dirs] + if self.current > #self.dirs then + return self.path.."/"..self.files[self.perpage*(self.page-1)+self.current - #self.dirs] end - return file + warningUnsupportedFunction() + return nil end --- returns the keycode of released key and (if debug) shows the keycode on screen -function FileChooser:ReturnKey(debug) +-- returns the keycode of released key +function FileChooser:ReturnKey() while true do ev = input.saveWaitForEvent() ev.code = adjustKeyEvents(ev) @@ -571,7 +629,6 @@ function FileChooser:ReturnKey(debug) break end end - if debug then showInfoMsgWithDelay("Keycode = "..ev.code,1000,1) end return ev.code end @@ -582,11 +639,11 @@ end --[[ NuPogodi, 04.09.2012: to make it more easy for users with various purposes and skills. ATM, one may leave only silent toggling between BEGINNERS_MODE <> ADVANCED_MODE -- But, in future, one more (the so called ROOT_MODE) might also be rather useful. -Shitch this mode on should allow developers & beta-testers to use some unstable +Switching this mode on should allow developers & beta-testers to use some unstable and/or dangerous functions able to crash the reader. ]] function FileChooser:changeFileChooserMode() - local face_list = { "safe mode for beginners", "advanced mode for experienced users", "expert mode for beta-testers & developers" } + local face_list = { "Safe mode for beginners", "Advanced mode for experienced users", "Expert mode for beta-testers & developers" } local modes_menu = SelectMenu:new{ menu_title = "Select proper mode to manage files", item_array = face_list, @@ -594,30 +651,58 @@ function FileChooser:changeFileChooserMode() } local m = modes_menu:choose(0, G_height) if m and m ~= self.filemanager_expert_mode then - --[[ TODO: to allow multiline-rendering for info messages & to include detailed description of the selected mode - local msg = "Press 'Y' to accept new mode..." - if m==self.BEGINNERS_MODE then - msg = "You have selected safe mode for beginners: the filemanager shows only files with the reader-related extensions (*.pdf, *.djvu, etc.); ".. - "safe renaming (no extensions); unstable or dangerous functions are NOT included. "..msg - elseif m==self.ADVANCED_MODE then - msg = "You have selected advanced mode for experienced users: the filemanager shows all files; ".. - "you may rename not only their names, but also the extensions; the files with unknown extensions would be sent to CREReader ".. - "and could crash the reader. Please, use it in your own risk. "..msg - else -- ROOT_MODE - msg = "You have selected the most advanced and dangerous mode. I hope You know what you are doing. God bless You. "..msg - end - InfoMessage:show(msg, 1) - if self:ReturnKey() == KEY_Y then ]] if (self.filemanager_expert_mode == self.BEGINNERS_MODE and m > self.BEGINNERS_MODE) or (m == self.BEGINNERS_MODE and self.filemanager_expert_mode > self.BEGINNERS_MODE) then self.filemanager_expert_mode = m -- make sure that new mode is set before... - self:setPath(self.path) -- refreshing the folder content + self:setPath(self.path) -- refreshing the folder content else self.filemanager_expert_mode = m end G_reader_settings:saveSetting("filemanager_expert_mode", self.filemanager_expert_mode) - -- end end + -- NuPogodi, 26.09.2012: temporary place; when properly tested, might be commented / deleted the following line + InfoMessage:initInfoMessageSettings() self.pagedirty = true end +-- NuPogodi, 28.09.12: two following functions are extracted just to make the code more compact +function FileChooser:deleteFolderAtPosition(pos) + if lfs.rmdir(self.path.."/"..self.dirs[pos]) then + table.remove(self.dirs, pos) -- to avoid showing just deleted file + self.items = #self.dirs + #self.files + self.current, self.page = gotoTargetItem(pos, self.items, pos, self.page, self.perpage) + else + InfoMessage:inform("Folder can't be deleted! ", 1500, 1, MSG_ERROR, + "This folder can not be deleted! Please, make sure that it is empty.") + end +end + +function FileChooser:deleteFileAtPosition(pos) + local fullpath = self.path.."/"..self.files[pos-#self.dirs] + os.remove(fullpath) -- delete the file itself + os.remove(DocToHistory(fullpath)) -- and its history file, if any + table.remove(self.files, pos-#self.dirs) -- to avoid showing just deleted file + self.items = self.items - 1 + self.current, self.page = gotoTargetItem(pos, self.items, pos, self.page, self.perpage) +end + +-- NuPogodi, 01.10.12: jump to defined item in the itemlist +function gotoTargetItem(target_item, all_items, current_item, current_page, perpage) + target_item = math.max(math.min(target_item, all_items), 1) + local target_page = math.ceil(target_item/perpage) + local target_curr = (target_item -1) % perpage + 1 + local pagedirty, markerdirty = false, false + if target_page ~= current_page then + current_page = target_page + pagedirty = true + markerdirty = true + elseif target_curr ~= current_item then + markerdirty = true + end + return target_curr, current_page, markerdirty, pagedirty +end + +function warningUnsupportedFunction() + InfoMessage:inform("Unsupported function! ", 2000, 1, MSG_WARN, + "The requested function is not supported.") +end diff --git a/filehistory.lua b/filehistory.lua index ca71b3837..995bb136b 100644 --- a/filehistory.lua +++ b/filehistory.lua @@ -6,16 +6,13 @@ require "inputbox" require "dialog" require "filesearcher" require "settings" +require "dialog" FileHistory = { - -- title height - title_H = 40, - -- spacing between lines - spacing = 36, - -- foot height - foot_H = 28, - -- horisontal margin - margin_H = 10, + title_H = 40, -- title height + spacing = 36, -- spacing between lines + foot_H = 28, -- foot height + margin_H = 10, -- horisontal margin -- state buffer history_files = {}, @@ -25,6 +22,7 @@ FileHistory = { page = 0, current = 1, oldcurrent = 1, + commands = nil, } function FileHistory:init(history_path) @@ -33,14 +31,16 @@ function FileHistory:init(history_path) else self:setPath("./history") end - self:addAllCommands() + + -- to initialize only once + if not self.commands then self:addAllCommands() end end function FileHistory:setPath(newPath) self.path = newPath self:readDir("-c ") self.items = #self.files - if self.items == 0 then + if self.items == 1 then return nil end self.page = 1 @@ -49,24 +49,22 @@ function FileHistory:setPath(newPath) end function FileHistory:readDir(order_criteria) - self.history_files = {} - self.files = {} + self.history_files = { {dir=self.path, name=".."} } + self.files = { {dir=self.path, name=".."} } local p = io.popen("ls "..order_criteria.."-1 "..self.path) for f in p:lines() do -- insert history files - file_entry = {dir=self.path, name=f} - table.insert(self.history_files, file_entry) + table.insert(self.history_files, {dir=self.path, name=f}) -- and corresponding path & file items - file_entry = {dir=HistoryToPath(f), name=HistoryToName(f)} - table.insert(self.files, file_entry) + table.insert(self.files, {dir=HistoryToPath(f), name=HistoryToName(f)}) end p:close() end function FileHistory:setSearchResult(keywords) - self.result = {} + self.result = { {dir=self.path, name=".."} } if keywords == "" or keywords == " " then - -- show all history + -- show all history self.result = self.files else -- select history files with keywords in the filename @@ -80,6 +78,7 @@ function FileHistory:setSearchResult(keywords) self.items = #self.result self.page = 1 self.current = 1 + return self.items end function FileHistory:prevItem() @@ -113,28 +112,6 @@ end function FileHistory:addAllCommands() self.commands = Commands:new{} - -- search among last documents - self.commands:add(KEY_S, nil, "S", - "search among files", - function(self) - old_keywords = self.keywords - self.keywords = InputBox:input(G_height - 100, 100, - "Search:", old_keywords) - if self.keywords then - self:setSearchResult(self.keywords) - else - self.keywords = old_keywords - end - self.pagedirty = true - end - ) - self.commands:add(KEY_L, nil, "L", - "last documents", - function(self) - self:setSearchResult("") - self.pagedirty = true - end - ) self.commands:add(KEY_H, nil, "H", "show help page", function(self) @@ -142,26 +119,29 @@ function FileHistory:addAllCommands() self.pagedirty = true end ) - self.commands:add({KEY_FW_RIGHT, KEY_I}, nil, "joypad right", - "document details", - function(self) - file_entry = self.result[self.perpage*(self.page-1)+self.current] - FileInfo:show(file_entry.dir,file_entry.name) - self.pagedirty = true - end - ) self.commands:add(KEY_FW_UP, nil, "joypad up", - "goto previous item", + "previous item", function(self) self:prevItem() end ) self.commands:add(KEY_FW_DOWN, nil, "joypad down", - "goto next item", + "next item", function(self) self:nextItem() end ) + -- NuPogodi, 01.10.12: fast jumps to items at positions 10, 20, .. 90, 0% within the list + local numeric_keydefs, i = {} + for i=1, 10 do numeric_keydefs[i]=Keydef:new(KEY_1+i-1, nil, tostring(i%10)) end + self.commands:addGroup("[1, 2 .. 9, 0]", numeric_keydefs, + "item at position 0%, 10% .. 90%, 100%", + function(self) + local target_item = math.ceil(self.items * (keydef.keycode-KEY_1) / 9) + self.current, self.page, self.markerdirty, self.pagedirty = + gotoTargetItem(target_item, self.items, self.current, self.page, self.perpage) + end + ) self.commands:add({KEY_PGFWD, KEY_LPGFWD}, nil, ">", "next page", function(self) @@ -189,7 +169,58 @@ function FileHistory:addAllCommands() end end ) - self.commands:add({KEY_F, KEY_AA}, nil, "F", + self.commands:add(KEY_G, nil, "G", -- NuPogodi, 01.10.12: goto page No. + "goto page", + function(self) + local n = math.ceil(self.items / self.perpage) + local page = NumInputBox:input(G_height-100, 100, "Page:", "current page "..self.page.." of "..n, true) + if pcall(function () page = math.floor(page) end) -- convert string to number + and page ~= self.page and page > 0 and page <= n then + self.page = page + if self.current + (page-1)*self.perpage > self.items then + self.current = self.items - (page-1)*self.perpage + end + end + self.pagedirty = true + end + ) + self.commands:add(KEY_FW_RIGHT, nil, "joypad right", + "document details", + function(self) + local file_entry = self.result[self.perpage*(self.page-1)+self.current] + if file_entry.name == ".." then + warningUnsupportedFunction() + return + end -- do not show details + FileInfo:show(file_entry.dir,file_entry.name) + self.pagedirty = true + end + ) + self.commands:add(KEY_S, nil, "S", + "invoke search inputbox", + function(self) + -- NuPogodi, 30.09.12: be sure that something is found + local old_keywords = self.keywords + local old_data = self.result + local old_page, old_current = self.page, self.current + self.keywords = InputBox:input(G_height - 100, 100, "Search:", old_keywords) + if self.keywords then + self:setSearchResult(self.keywords) + end + if #self.result < 2 then + InfoMessage:inform("No hits! Try another keyword. ", 2000, 1, MSG_WARN, + "The search has given no results! Please, try another keyword.") + -- restoring the original data + self.result = old_data + self.items = #self.result + self.keywords = old_keywords + self.page = old_page + self.current = old_current + end + self.pagedirty = true + end + ) + self.commands:add({KEY_F, KEY_AA}, nil, "F, Aa", "change font faces", function(self) Font:chooseFonts() @@ -197,35 +228,42 @@ function FileHistory:addAllCommands() end ) self.commands:add({KEY_ENTER, KEY_FW_PRESS}, nil, "Enter", - "open selected item", + "open selected document", function(self) - if #self.result == 0 then - showInfoMsgWithDelay("No files to open", 1500, 1) - return - end - file_entry = self.result[self.perpage*(self.page-1)+self.current] + local file_entry = self.result[self.perpage*(self.page-1)+self.current] + if file_entry.name == ".." then return "break" end -- quit file_full_path = file_entry.dir .. "/" .. file_entry.name - openFile(file_full_path) --reset height and item index if screen has been rotated local item_no = self.perpage * (self.page - 1) + self.current self.perpage = math.floor(G_height / self.spacing) - 2 self.current = item_no % self.perpage self.page = math.floor(item_no / self.perpage) + 1 - self.pagedirty = true end ) self.commands:add({KEY_DEL}, nil, "Del", "delete history entry", function(self) - file_entry = self.result[self.perpage*(self.page-1)+self.current] - if not file_entry then return end + local file_entry = self.result[self.perpage*(self.page-1)+self.current] + if file_entry.name == ".." then + warningUnsupportedFunction() + return + end -- do not delete local file_to_del = file_entry.dir .. "/" .. file_entry.name - os.remove(DocToHistory(file_to_del)) - -- to avoid showing just deleted file - self:init() - self:setSearchResult(self.keywords) + if InfoMessage.InfoMethod[MSG_CONFIRM] == 0 then -- silent regime + os.remove(DocToHistory(file_to_del)) + self:init() + self:setSearchResult(self.keywords) + else + InfoMessage:inform("Press 'Y' to confirm ", nil, 0, MSG_CONFIRM, + "Please, press key Y to delete the book history") + if FileChooser:ReturnKey() == KEY_Y then + os.remove(DocToHistory(file_to_del)) + self:init() + self:setSearchResult(self.keywords) + end + end self.pagedirty = true end ) @@ -235,7 +273,7 @@ function FileHistory:addAllCommands() self.pagedirty = true end ) - self.commands:add({KEY_BACK, KEY_HOME}, nil, "Back", + self.commands:add({KEY_BACK, KEY_HOME}, nil, "Back, '..', Home", "back", function(self) return "break" @@ -248,7 +286,11 @@ function FileHistory:choose(keywords) self.pagedirty = true self.markerdirty = false - self:setSearchResult(keywords) + -- NuPogodi, 30.09.12: immediate quit (no redraw), if empty + if self:setSearchResult(keywords) < 2 then -- only ".." + InfoMessage:inform("No reading history! ", 2000, 1, MSG_WARN, "The reading history is empty!") + return nil + end while true do local cface = Font:getFace("cfont", 22) @@ -258,37 +300,25 @@ function FileHistory:choose(keywords) if self.pagedirty then self.markerdirty = true fb.bb:paintRect(0, 0, G_width, G_height, 0) - -- draw header local header = "Last Documents" if self.keywords ~= "" and self.keywords ~= " " then --header = header .. " (filter: \'" .. string.upper(self.keywords) .. "\')" header = "Search Results for \'"..string.upper(self.keywords).."\'" end - DrawTitle(header,self.margin_H,0,self.title_H,4,tface) - + DrawTitle(header,self.margin_H,0,self.title_H,3,tface) -- draw found results - local c - if self.items == 0 then -- nothing found - y = self.title_H + self.spacing * 2 - renderUtf8Text(fb.bb, self.margin_H, y, cface, - "Sorry, no files found.", true) - self.markerdirty = false - else -- found something, draw it - for c = 1, self.perpage do - local i = (self.page - 1) * self.perpage + c - if i <= self.items then - y = self.title_H + (self.spacing * c) + 4 - local ftype = string.lower(string.match(self.result[i].name, ".+%.([^.]+)") or "") - DrawFileItem(self.result[i].name,self.margin_H,y,ftype) - end + for c = 1, self.perpage do + local i = (self.page - 1) * self.perpage + c + if i <= self.items then + y = self.title_H + (self.spacing * c) + 4 + local ftype = string.lower(string.match(self.result[i].name, ".+%.([^.]+)") or "") + DrawFileItem(self.result[i].name,self.margin_H,y,ftype) end end - -- draw footer all_page = math.ceil(self.items/self.perpage) DrawFooter("Page "..self.page.." of "..all_page,fface,self.foot_H) - end if self.markerdirty then diff --git a/font.lua b/font.lua index 44666ffd3..006e33d8b 100644 --- a/font.lua +++ b/font.lua @@ -9,8 +9,6 @@ Font = { hpkfont = "droid/DroidSansMono.ttf", -- help page: font for displaying keys hfont = "droid/DroidSans.ttf", -- help page: font for displaying help messages infont = "droid/DroidSansMono.ttf", -- inputbox: use mono for better distance controlling - -- pgfont = "droid/DroidSans.ttf", -- was in use in heppage to render footer - -- to be repalced by ffont }, fontdir = os.getenv("FONTDIR") or "./fonts", -- face table diff --git a/inputbox.lua b/inputbox.lua index 091c9937c..043c7956a 100644 --- a/inputbox.lua +++ b/inputbox.lua @@ -3,6 +3,9 @@ require "rendertext" require "keys" require "graphics" +MODE_CALC = 1 +MODE_TERM = 2 + ---------------------------------------------------- -- General inputbox ---------------------------------------------------- @@ -37,7 +40,7 @@ InputBox = { shiftmode = true, -- toggle chars <-> capitals, lowest bit in (layout-2) symbolmode = false, -- toggle chars <-> symbols, middle bit in (layout-2) utf8mode = false, -- toggle english <-> national, highest bit in (layout-2) - calcmode = false, -- toggle calculator mode + inputmode, -- define mode: input <> calculator <> terminal calcfunctions = nil, -- math functions for calculator helppage } @@ -128,7 +131,7 @@ function InputBox:input(ypos, height, title, d_text, is_hint) -- my own position, at the bottom screen edge ypos = fb.bb:getHeight() - 165 -- some corrections for calculator mode - if self.calcmode then + if self.inputmode == MODE_CALC then self:setCalcMode() end @@ -366,7 +369,7 @@ function InputBox:CharlistToString() end function InputBox:addAllCommands() - -- if already initialized, we (re)define only calcmode-dependent commands + -- if already initialized, we (re)define only inputmode-dependent commands if self.commands then self:ModeDependentCommands() self:DrawVirtualKeyboard() @@ -378,7 +381,7 @@ function InputBox:addAllCommands() self:addCharCommands(self.layout) -- adding the rest commands (independent of the selected layout) self.commands:add(KEY_H, MOD_ALT, "H", - "show helppage", + "show help page", function(self) self:showHelpPage(self.commands) end @@ -436,7 +439,7 @@ function InputBox:addAllCommands() end ) self.commands:addGroup("up/down", { Keydef:new(KEY_FW_DOWN, nil), Keydef:new(KEY_FW_UP, nil) }, - "goto previous/next VK-layout", + "previous/next VK-layout", function(self) if keydef.keycode == KEY_FW_DOWN then if self.layout == self.max_layout then self:addCharCommands(self.min_layout) @@ -469,10 +472,10 @@ function InputBox:addAllCommands() self:addCharCommands() end ) - -- NuPogodi, 02.06.12: calcmode-dependent commands are collected + -- NuPogodi, 02.06.12: inputmode-dependent commands are collected self:ModeDependentCommands() -- here - self.commands:add({KEY_BACK, KEY_HOME}, nil, "Back", + self.commands:add({KEY_BACK, KEY_HOME}, nil, "Back, Home", "back", function(self) self.input_string = nil @@ -552,9 +555,8 @@ function InputBox:showHelpPage(list, title) self.cursor:clear() -- hide cursor fb.bb:dimRect(self.input_start_x-5, self.input_start_y-19, self.input_slot_w, self.fheight, self.input_bg) fb:refresh(1, self.input_start_x-5, self.ypos, self.input_slot_w, self.h) - -- now start the helppage with own list of commands and own title HelpPage:show(0, fb.bb:getHeight()-165, list, title) - -- on the helppage-exit, making inactive helpage + -- on the help page-exit, making inactive helpage fb.bb:dimRect(0, 40, fb.bb:getWidth(), fb.bb:getHeight()-205, self.input_bg) -- and active input slot self:refreshText() @@ -595,13 +597,13 @@ end -- define whether we need to calculate the result or to return 'self.input_string' function InputBox:ModeDependentCommands() - if self.calcmode then + if self.inputmode == MODE_CALC then -- define what to do with the input_string - self.commands:add({KEY_FW_PRESS, KEY_ENTER}, nil, "joypad center", + self.commands:add({KEY_FW_PRESS, KEY_ENTER}, nil, "Enter", "calculate the result", function(self) if #self.input_string == 0 then - showInfoMsgWithDelay("No input ", 1000, 1) + InfoMessage:inform("No input! ", 1000, 1, MSG_WARN, "There is nothing to calculate") else local s = self:PrepareStringToCalc() if pcall(function () f = assert(loadstring("r = tostring("..s..")")) end) and pcall(f) then @@ -618,7 +620,7 @@ function InputBox:ModeDependentCommands() self.cursor:draw() fb:refresh(1, self.input_start_x-5, self.input_start_y-25, self.input_slot_w, self.h-25) else - showInfoMsgWithDelay("Invalid input ", 1000, 1) + InfoMessage:inform("Invalid input! ", 1000, 1, MSG_WARN) end -- if pcall end end -- function @@ -633,7 +635,7 @@ function InputBox:ModeDependentCommands() end ) else -- return input_string & close input box - self.commands:add({KEY_FW_PRESS, KEY_ENTER}, nil, "joypad center", + self.commands:add({KEY_FW_PRESS, KEY_ENTER}, nil, "Enter", "submit input content", function(self) if self.input_string == "" then @@ -644,7 +646,7 @@ function InputBox:ModeDependentCommands() ) -- delete calculator-specific help self.commands:del(KEY_M, MOD_ALT, "M") - end -- if self.calcmode + end -- if self.inputmode end ---------------------------------------------------- diff --git a/kite/KPDFviewer (last file) b/kite/KPDFviewer (last file) index 5d9acab14..de0c410d9 100755 --- a/kite/KPDFviewer (last file) +++ b/kite/KPDFviewer (last file) @@ -1,3 +1,3 @@ #!/bin/sh -/mnt/us/launchpad/kpdf.sh +/mnt/us/kindlepdfviewer/kpdf.sh diff --git a/kite/KPDFviewer File Manager b/kite/KPDFviewer File Manager index 3374ef3ae..f5f935234 100755 --- a/kite/KPDFviewer File Manager +++ b/kite/KPDFviewer File Manager @@ -1,3 +1,3 @@ #!/bin/sh -/mnt/us/launchpad/kpdf.sh /mnt/us/documents +/mnt/us/kindlepdfviewer/kpdf.sh /mnt/us/documents diff --git a/kpdf.sh b/kpdf.sh new file mode 100644 index 000000000..c1f81c700 --- /dev/null +++ b/kpdf.sh @@ -0,0 +1,37 @@ +#!/bin/sh +export LC_ALL="en_US.UTF-8" + +echo unlock > /proc/keypad +echo unlock > /proc/fiveway + +# we're always starting from our working directory +cd /mnt/us/kindlepdfviewer/ + +# bind-mount system fonts +if ! grep /mnt/us/kindlepdfviewer/fonts/host /proc/mounts; then + mount -o bind /usr/java/lib/fonts /mnt/us/kindlepdfviewer/fonts/host +fi + +# check if we are supposed to shut down the Amazon framework +if test "$1" == "--framework_stop"; then + shift 1 + /etc/init.d/framework stop +fi + +# stop cvm +killall -stop cvm + +# finally call reader +./reader.lua "$1" 2> /mnt/us/kindlepdfviewer/crash.log || cat /mnt/us/kindlepdfviewer/crash.log + +# unmount system fonts +if grep /mnt/us/kindlepdfviewer/fonts/host /proc/mounts; then + umount /mnt/us/kindlepdfviewer/fonts/host +fi + +# always try to continue cvm +killall -cont cvm || /etc/init.d/framework start + +# cleanup hanging process +killall lipc-wait-event + diff --git a/launchpad/kpdf.ini b/launchpad/kpdf.ini index ab2a1b0d9..53be1fc3f 100755 --- a/launchpad/kpdf.ini +++ b/launchpad/kpdf.ini @@ -1,11 +1,11 @@ [Actions] # start kindlepdfviewer with filebrowser in /mnt/us/documents -P D = !/mnt/us/launchpad/kpdf.sh /mnt/us/documents +P D = !/mnt/us/kindlepdfviewer/kpdf.sh /mnt/us/documents # start kindlepdfviewer with last document -P P = !/mnt/us/launchpad/kpdf.sh +P P = !/mnt/us/kindlepdfviewer/kpdf.sh # start kindlepdfviewer without framework in /mnt/us/documents -P K = !/mnt/us/launchpad/kpdf.sh --framework_stop /mnt/us/documents +P K = !/mnt/us/kindlepdfviewer/kpdf.sh --framework_stop /mnt/us/documents # start kindlepdfviewer without framework on last read document -P L = !/mnt/us/launchpad/kpdf.sh --framework_stop +P L = !/mnt/us/kindlepdfviewer/kpdf.sh --framework_stop # restart amazon framework - when it got irritated P R = !/etc/init.d/framework restart diff --git a/reader.lua b/reader.lua index 320434a48..afddbc65f 100755 --- a/reader.lua +++ b/reader.lua @@ -42,7 +42,7 @@ function openFile(filename) reader = ext:getReader(file_type) if reader then - InfoMessage:show("Opening document... ", 0) + InfoMessage:inform("Opening document... ", nil, 0, MSG_AUX) reader:preLoadSettings(filename) local ok, err = reader:open(filename) if ok then @@ -54,9 +54,9 @@ function openFile(filename) else if err then Debug("openFile(): "..err) - showInfoMsgWithDelay(err:sub(1,30), 2000, 1) + InfoMessage:inform(err:sub(1,30), 2000, 1, MSG_ERROR) else - showInfoMsgWithDelay("Error opening document ", 2000, 1) + InfoMessage:inform("Error opening document! ", 2000, 1, MSG_ERROR) end end end @@ -140,6 +140,8 @@ end -- set up the mode to manage files FileChooser.filemanager_expert_mode = G_reader_settings:readSetting("filemanager_expert_mode") or 1 +InfoMessage:initInfoMessageSettings() + -- initialize global settings shared among all readers UniReader:initGlobalSettings(G_reader_settings) -- initialize specific readers @@ -171,6 +173,7 @@ end -- save reader settings G_reader_settings:saveSetting("fontmap", Font.fontmap) +InfoMessage:saveInfoMessageSettings() G_reader_settings:close() -- @TODO dirty workaround, find a way to force native system poll diff --git a/resources/info-aux.png b/resources/info-aux.png new file mode 100644 index 0000000000000000000000000000000000000000..afee09ee58ddc37401decbffb73dcd246e9f1623 GIT binary patch literal 1109 zcmV-b1giUqP)%dedCsPG0h089N9(xE=dFdsTMaoE(>L>?>qDX`Vcw5tnjvnVQVDd1glLB__A`xb{q(swwL`hLLPrV{v%5WgZ}^I z;{*FYfqgtd47sI$16OA?!p=y|sCbWpcWFaWsx2L>B=Z>PIrUt|P3F6@j%u$z1a=nX z<`q|N$z7?6(_ef5I)+(sr#RQZ`f`4hAnh&C6VSYh5mJ%}q3TdM;y%}K%j(Vp@MHaGf-s7MQ5QMw4PcYu6eG&3QG8CSlw45dhdd6}8gc1QmoKHqU@dsGZ1HQ zu-6PG^b03I0a0G1!8*Hx{X#JWW=$AGxAI^k6zo`cfb2)#e4X9|K3- z$&!p^xgCQZlv4Ff_A+k8R1O*L((T8*eC+gjMpu@2@apw4*XwF|n>o{^o=Ntp3#R`=*X2N?Gb+jLVX@ z32sffTI~BJO|vX>iWO9a%?H3Pz-6_mSulwa5AUr~{{gtQOj$2Od5WW6YWgu)3#jwi zJh}_5W@-C{;@=i53&Q2nh0wCJlD7@G&&JZz*6}>wwmt9T bzTE!-NI`3pcXg6N00000NkvXXu0mjfMWqY% literal 0 HcmV?d00001 diff --git a/resources/info-bug.png b/resources/info-bug.png new file mode 100644 index 0000000000000000000000000000000000000000..990e0466f67335c2c93d82ee3e3c2399712d8c40 GIT binary patch literal 1037 zcmV+o1oHcdP)hZIjSLz8O<_fXUh zEa*@c8x^xUtZ|#9un4CmRM1OX2N#2(_nbD1%pT@a=%V6OpP_GL*+{9WFQs!xh~AI? zd(RUi60JG^8JMgC)pek=4*a>QN0or5cY%P2e{0{G==U&j&9 zAo#UE65vY-Fk&}eWblpsxmfh+yv2E;vDX;?CQ0yY(s@HHT4p?OLr1-z%+NM=H_I0& zT0e|MkJG8mc~MnLZUVyL$ho?>kR6CFx(tX{MCtQLF>fvGrTvOC7E2-m*0o;K4W^?&DJjN>17)=70 z9e~-;8@)RV@RubHO%9F_)OyXXJ}e$4;3d;EZBZxK0%X&0yH0n!1ax^S;!aHY*l^Rg zDK20UK+bRkxs?6tJx4$ikUzr_*A_0tH2i!6XW;8iDP25yXE?2W4d@})Yz5v4;H$W7 zS(*THtK7T}X1Ay&zB4(T0N}DTx(=ecMlHrH&29nkLSqtL2c26QW9)g(4g&tHY`Fgb zv-;b(AtrFni;dBWSJJsET5Q;SS|~N#6=1fferI6@P|8MX&BUXT4eU}QU00000NkvXX Hu0mjfXJg#k literal 0 HcmV?d00001 diff --git a/resources/info-confirm.png b/resources/info-confirm.png new file mode 100644 index 0000000000000000000000000000000000000000..85a6585ed6090c93505aa6d412dbced7c0dae68b GIT binary patch literal 858 zcmV-g1Eu_lP)+#pbQ-vRjRO1V}c{xqQfcY)@OQqdwF z)&eBpas@D{qDbK)7T^Yo0Y3qlig0=g(GwKFOBG{_NV!f=A*urPRz>uT>o}2vip>&e zgRhFgTn&Nmx`?=mzgGa$k4piq=U$->4$W0OCj<9-f{2Jk+@b*9Cvcj9CoG8Q7ey}J zfnrvyGB60d0_UoOFL3HPsRK(4Jcae*c?6uK5Tt49c{^~(z&V)W=c)T>AxP2Sd0lwO z!1Wna;B9F8X8<=a_nh3xcVPS^1M(D1VO$tiR@PF?k;uESkT3vCsE8CEk0+&md5(g~ zFHitJDxxySl=|f)xRWqIiVmzhOpIQKec-23qA(kdGG1{Nq6!3*WZgj;ljIxvQc+$-}8 z8w@o7INyN{jT1Z zyb>tcwJ9~Zn3n^CU#9sx%jL57Y`SUr|p z91mMjDB0j(?4O86wooGYhXvD+qMHEIKHbPkG1LwLa{rqE)8K>T7~qBu;|styr$qqp zr?(jR@NZ--9n%ak$O`bmkrl09bJXz`#-KM!>s|FjfJ;9Tos9 z@au&D&ON&jSwM z|Kl6yz&&kuBMT;lwfdr8F)-vu!P1tO`3QMbM6`O_odG)`Mtgm{gPW|NV<8?o!@$CP zifBb3ck$CMUQ@>c4CX*#j5k;y*GZKa@+se>cPp~7SD{jXaLpXFjS3qgZf!5L58&~T6#CSySN2T(jDYc@M zW(6uB5tLgpL;|kqEohrFLk~MaTdd8TqQwv{L*JCbrx@D2uM~33LjpCUhHfC*3uuuD&JRh@ ztKCu_w!rg>3=q+^D=MMU0qbLytAKI=JZ+KTbEU}5`3f8fya1ILs08+F3*}n|e5wTk z2+`By>IZjcAFcs_^1S(b(N;q6L{|VC0Ql2XgiULS1(;r>H-~oFOS}olD&7C^)BKwG Y7t(RorN`jwBme*a07*qoM6N<$f+sGkX8-^I literal 0 HcmV?d00001 diff --git a/resources/info-i.png b/resources/info-i.png deleted file mode 100644 index 69192d4ed5c74babd5465540786b0d7e85ea9638..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 538 zcmV+#0_FXQP)FMb%E-r0tZKR~6U0q!q92}aOn%CFY{r&yAy1F(tHhg@1NGo{O0004{ zNkl3H>DSQA6dMzk6TUls7MjwC>qAy^g zje$ZOvI}9r#Ci&{yN!mz2e5EvxXeFyuAJykb@DOHo#CEy;fjg#aN`ANoOz#e_X;Xo z6_cG)M9Eo69t28nC-@CoR&?G)BLCw22S!d}ylV0S`3Yw5u9z2_mqWYrYrFf*yDQ$& z9;XJCwBj*T?0|nmr8s3S%nR7z;zGWO>git*mCGUXZHLx|D$Fz1#1buf9KH!(LUpCQD>5L${yH837JEE<0=t}AW_ObL9hA8J#A zu`$9d1$P=S6ZA1l-Q+qR_*Ih&WTe(a20WQ3Ciml&xwMhQ6Z9mu(UG`>p~N0siMbuf z`gUl}=RCI}S>KAi`E2{_An})8vJLbm+76U!&FL+;WQBas7K}G=_NSVjzu3TRe`G@B z;))y|To@OZ)<@4_#0MNb99;j)t=6soxhs!3xP-AM4^4UG$^%;-_uo4#T&GISTJk~l c^n8YNmh18BHpdzR$Sa}6XF|Gh5 zIF&?+iIdpt3Q&S&N9I zIWR56$tWevUyK0LG<%^4x6~9cm?ntm4tpRC4KgS|teG;L^C(SEh%yk#0FYz@z*fl+ zMd^5{FnP%^27q&FpTVD%GUkPfLF{p^0OWiTIEROg3h))R{Ehl30wH!vz&(Ll{!YFZ zq$mMYSy$i1<4iHr2j)odS^=Fv1W@E@2n{1#1$3VgvE%aw!hOCgKtP~?4n}AjXi^#w zXu-N$1h5JX3KD>t1Fbc?MF0w9?YB%21ma*f1C*kaW)x-3kB&`Wi8shFfzG z=%5Y)7sen^fKq5$ZfFhzI*ersY+(>^H)p;q$UNF}tALA7ll#6uJL}K-NZoPWs-fL# zwa#)nTdS+9L#FVYV%!0s?UXYJxMhP|1yDm(#sORf+LUfi", "next page", function(sm) if sm.page < (sm.items / sm.perpage) then @@ -129,7 +121,7 @@ function SelectMenu:addAllCommands() end end ) - self.commands:add({KEY_PGBCK, KEY_LPGBCK}, nil, "prev", + self.commands:add({KEY_PGBCK, KEY_LPGBCK}, nil, "<", "previous page", function(sm) if sm.page > 1 then @@ -188,7 +180,7 @@ function SelectMenu:addAllCommands() table.insert(KEY_Q_to_P, Keydef:new(i, nil, "")) end self.commands:addGroup("Q to P", KEY_Q_to_P, - "Select item with Q to P key as shortcut", + "select item with Q to P key as shortcut", function(sm, keydef) sm.selected_item = sm:getItemIndexByShortCut( sm.item_shortcuts[ keydef.keycode - KEY_Q + 1 ], sm.perpage) @@ -199,7 +191,7 @@ function SelectMenu:addAllCommands() table.insert(KEY_A_to_L, Keydef:new(i, nil, "")) end self.commands:addGroup("A to L", KEY_A_to_L, - "Select item with A to L key as shortcut", + "select item with A to L key as shortcut", function(sm, keydef) sm.selected_item = sm:getItemIndexByShortCut( sm.item_shortcuts[ keydef.keycode - KEY_A + 11 ], sm.perpage) @@ -210,55 +202,55 @@ function SelectMenu:addAllCommands() table.insert(KEY_Z_to_M, Keydef:new(i, nil, "")) end self.commands:addGroup("Z to M", KEY_Z_to_M, - "Select item with Z to M key as shortcut", + "select item with Z to M key as shortcut", function(sm, keydef) sm.selected_item = sm:getItemIndexByShortCut( sm.item_shortcuts[ keydef.keycode - KEY_Z + 21 ], sm.perpage) end ) self.commands:add(KEY_SLASH, nil, "/", - "Select item with / key as shortcut", + "select item with / key as shortcut", function(sm) sm.selected_item = sm:getItemIndexByShortCut("/", sm.perpage) end ) self.commands:add(KEY_DOT, nil, ".", - "Select item with dot key as shortcut", + "select item with dot key as shortcut", function(sm) sm.selected_item = sm:getItemIndexByShortCut(".", sm.perpage) end ) self.commands:add(KEY_SYM, nil, "Sym", - "Select item with Sym key as shortcut", + "select item with Sym key as shortcut", function(sm) sm.selected_item = sm:getItemIndexByShortCut("Sym", sm.perpage) end ) self.commands:add(KEY_ENTER, nil, "Enter", - "Select item with Enter key as shortcut", + "select item with Enter key as shortcut", function(sm) sm.selected_item = sm:getItemIndexByShortCut("Ent", sm.perpage) end ) - self.commands:add(KEY_BACK, nil, "Back", - "Exit menu", - function(sm) - return "break" - end - ) self.commands:add(KEY_H,MOD_ALT,"H", "show help page", function(sm) HelpPage:show(0, G_height, sm.commands) sm.pagedirty = true end) + self.commands:add({KEY_BACK,KEY_HOME}, nil, "Back, Home", + "exit menu", + function(sm) + return "break" + end + ) end function SelectMenu:clearCommands() self.commands = Commands:new{} - self.commands:add(KEY_BACK, nil, "", - "Exit menu", + self.commands:add({KEY_BACK,KEY_HOME}, nil, "Back, Home", + "exit menu", function(sm) return "break" end) @@ -352,7 +344,7 @@ function SelectMenu:choose(ypos, height) end -- for c=1, self.perpage end -- if self.items == 0 - local footer = "Page "..self.page.." of "..(math.ceil(self.items / self.perpage)).." Press Alt-H for help" + local footer = "Page "..self.page.." of "..(math.ceil(self.items / self.perpage)).." - Press Alt-H for help" renderUtf8Text(fb.bb, self.margin_H, height-7, fface, footer, true) end From 8b45833e2d5acdd7425d937590baf8735cc7d7ac Mon Sep 17 00:00:00 2001 From: NiLuJe Date: Wed, 3 Oct 2012 01:17:21 +0200 Subject: [PATCH 17/21] Kill a bit of cruft, explain the reasonning behind the kill/traps, and fix a potential buffering issue eating the first slider event --- input.c | 29 +++++++++++++---------------- kpdfview.c | 9 --------- slider_watcher.c | 13 +++---------- 3 files changed, 16 insertions(+), 35 deletions(-) diff --git a/input.c b/input.c index 126f94a9c..7f79d1986 100644 --- a/input.c +++ b/input.c @@ -50,7 +50,8 @@ void slider_handler(int sig) { /* Kill lipc-wait-event properly on exit */ if(pclose_arg.pid != 0) { - kill(pclose_arg.pid, SIGTERM); + // Be a little more gracious, lipc seems to handle SIGINT properly + kill(pclose_arg.pid, SIGINT); } } @@ -84,7 +85,7 @@ static int openInputDevice(lua_State *L) { return luaL_error(L, "cannot fork() slider event listener"); } if(childpid == 0) { - // Setup signal handler, to cleanup on exit + // We send a SIGTERM to this child on exit, trap it to kill lipc properly. signal(SIGTERM, slider_handler); FILE *fp; @@ -99,25 +100,21 @@ static int openInputDevice(lua_State *L) { ev.code = key_code; ev.value = 1; - /* listen power slider events */ - char *exec_file = "lipc-wait-event"; - char *arg1 = "-m"; // Hang for ever, don't exit on the first one: we're in a dedicated child, and we'll be killed/closed on exit, so we don't care - char *arg2 = "-s"; - char *arg3 = "0"; - char *arg4 = "com.lab126.powerd"; - char *arg5 = "goingToScreenSaver,outOfScreenSaver,charging,notCharging"; - char *arg6 = (char *) NULL; - char *argv[] = {exec_file, arg1, arg2, arg3, arg4, arg5, arg6}; + /* listen power slider events (listen for ever for multiple events) */ + char *argv[] = {"lipc-wait-event", "-m", "-s", "0", "com.lab126.powerd", "goingToScreenSaver,outOfScreenSaver,charging,notCharging", (char *) NULL}; /* @TODO 07.06 2012 (houqp) * plugin and out event can only be watched by: lipc-wait-event com.lab126.hal usbPlugOut,usbPlugIn */ - fp = popen_noshell(exec_file, (const char * const *)argv, "r", &pclose_arg, 0); + fp = popen_noshell("lipc-wait-event", (const char * const *)argv, "r", &pclose_arg, 0); if (!fp) { err(EXIT_FAILURE, "popen_noshell()"); } + /* Flush to get rid of buffering issues? */ + fflush(fp); + while(fgets(std_out, sizeof(std_out)-1, fp)) { if(std_out[0] == 'g') { ev.code = CODE_IN_SAVER; @@ -147,13 +144,13 @@ static int openInputDevice(lua_State *L) { if (status == -1) { err(EXIT_FAILURE, "pclose_noshell()"); } else { - printf("Power slider event listener child exited with status %d.\n", status); + printf("lipc-wait-event exited with status %d.\n", status); if WIFEXITED(status) { - printf("Child exited normally with status: %d.\n", WEXITSTATUS(status)); + printf("lipc-wait-event exited normally with status: %d.\n", WEXITSTATUS(status)); } if WIFSIGNALED(status) { - printf("Child terminated by signal: %d.\n", WTERMSIG(status)); + printf("lipc-wait-event terminated by signal: %d.\n", WTERMSIG(status)); } } @@ -193,7 +190,7 @@ static int closeInputDevices(lua_State *L) { } if(slider_pid != -1) { /* kill and wait for child process */ - kill(slider_pid, SIGTERM); + kill(slider_pid, SIGTERM); // We could kill -slider_pid (note the minus) to kill the whole process group, but we trap SIGTERM to handle things nicely waitpid(-1, NULL, 0); } return 0; diff --git a/kpdfview.c b/kpdfview.c index 08ec30283..15858df33 100644 --- a/kpdfview.c +++ b/kpdfview.c @@ -83,15 +83,6 @@ int main(int argc, char **argv) { } } - /* Make popen_noshell & valgrind happy */ - if (fflush(stdout) != 0) - err(EXIT_FAILURE, "fflush(stdout)"); - if (fflush(stderr) != 0) - err(EXIT_FAILURE, "fflush(stderr)"); - close(STDIN_FILENO); - close(STDOUT_FILENO); - close(STDERR_FILENO); - return 0; } diff --git a/slider_watcher.c b/slider_watcher.c index cca6827a5..3d16c06ad 100644 --- a/slider_watcher.c +++ b/slider_watcher.c @@ -65,16 +65,9 @@ main ( int argc, char *argv[] ) ev.value = 1; /* listen power slider events */ - char *exec_file = "lipc-wait-event"; - char *arg1 = "-m"; - char *arg2 = "-s"; - char *arg3 = "0"; - char *arg4 = "com.lab126.powerd"; - char *arg5 = "goingToScreenSaver,outOfScreenSaver"; - char *arg6 = (char *) NULL; - char *chargv[] = {exec_file, arg1, arg2, arg3, arg4, arg5, arg6}; - - fp = popen_noshell(exec_file, (const char * const *)chargv, "r", &pclose_arg, 0); + char *argv[] = {"lipc-wait-event", "-m", "-s", "0", "com.lab126.powerd", "goingToScreenSaver,outOfScreenSaver", (char *) NULL}; + + fp = popen_noshell("lipc-wait-event", (const char * const *)chargv, "r", &pclose_arg, 0); if (!fp) { err(EXIT_FAILURE, "popen_noshell()"); } From 3c181ead772fff5973f3259a49744a1d4525d945 Mon Sep 17 00:00:00 2001 From: NiLuJe Date: Wed, 3 Oct 2012 01:23:34 +0200 Subject: [PATCH 18/21] Actually, no that's wrong, it's not the parent process, it's a fork. --- input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/input.c b/input.c index 7f79d1986..d781c5b10 100644 --- a/input.c +++ b/input.c @@ -190,7 +190,7 @@ static int closeInputDevices(lua_State *L) { } if(slider_pid != -1) { /* kill and wait for child process */ - kill(slider_pid, SIGTERM); // We could kill -slider_pid (note the minus) to kill the whole process group, but we trap SIGTERM to handle things nicely + kill(slider_pid, SIGTERM); waitpid(-1, NULL, 0); } return 0; From a2ce5dd101eeaff97cfe4f3f0843175fdad8deaa Mon Sep 17 00:00:00 2001 From: NiLuJe Date: Wed, 3 Oct 2012 01:37:20 +0200 Subject: [PATCH 19/21] Hello, tiny stupid typo! --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a82a81871..5480ae96e 100644 --- a/Makefile +++ b/Makefile @@ -36,7 +36,7 @@ BASE_CFLAGS:=-O2 -ffast-math -pipe -fomit-frame-pointer -fno-stack-protector -U_ #BASE_CFLAGS:=-O0 -g ARM_ARCH:=-march=armv6j -mtune=arm1136jf-s -mfpu=vfp HOST_ARCH:=-march=native -HOSTCFLAGS:=$(HOST_CFLAGS) $(BASE_CFLAGS) +HOSTCFLAGS:=$(HOST_ARCH) $(BASE_CFLAGS) CFLAGS:=$(BASE_CFLAGS) CXXFLAGS:=$(BASE_CFLAGS) -fno-use-cxa-atexit LDFLAGS:=-Wl,-O1 -Wl,--as-needed From 9acdedcaebed81bf4ac239213ea56ed9e7a5b85d Mon Sep 17 00:00:00 2001 From: NiLuJe Date: Wed, 3 Oct 2012 01:41:32 +0200 Subject: [PATCH 20/21] Forgot to revert that too, useless now :). --- kpdfview.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/kpdfview.c b/kpdfview.c index 15858df33..0f8a674f2 100644 --- a/kpdfview.c +++ b/kpdfview.c @@ -15,13 +15,9 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#include -#include -#include -#include #include #include -#include +#include #include #include From c7fa11e7e9f29e7ac8e0c180f4b7a624deeee383 Mon Sep 17 00:00:00 2001 From: NiLuJe Date: Wed, 3 Oct 2012 01:46:56 +0200 Subject: [PATCH 21/21] Slight syntax tweak --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 5480ae96e..17a987b01 100644 --- a/Makefile +++ b/Makefile @@ -124,7 +124,7 @@ kpdfview: kpdfview.o einkfb.o pdf.o blitbuffer.o drawcontext.o input.o $(POPENNS $(CRENGINELIBS) \ $(STATICLIBSTDCPP) \ $(LDFLAGS) \ - -o kpdfview -lm -ldl -lpthread $(EMU_LDFLAGS) $(DYNAMICLIBSTDCPP) + -o $@ -lm -ldl -lpthread $(EMU_LDFLAGS) $(DYNAMICLIBSTDCPP) slider_watcher.o: %.o: %.c $(CC) -c $(CFLAGS) $< -o $@