mirror of
https://github.com/koreader/koreader.git
synced 2025-08-10 00:52:38 +00:00
Merge remote branch 'hwhw/master'
* hwhw/master: (183 commits) show infomessage on document open use InfoMessage for empty TOC, jump history and highlights copy resources dir on customupdate add resource: info icon display document open error message added infomessage dialog implementation bugfix, removed old test constant Added widget abstraction framework added interface to get blitbuffers from JPEG/PNG files fix typo added reading of pan_margin settings fix full screen refresh command, close #99 add: sleep and usleep in util module fix: add back KEY_FW{LEFT,RIGHT} commands to NumInputBox reverted removal of last-doc shortcut, introduced framework restart kill our own child process. not quite finished. added line spacing setting for crereader remove page:getPageText debug dump to improve performance on device remove page:getPageText debug dump to improve performance on device display crash.log on error ... Conflicts: ft.c helppage.lua inputbox.lua rendertext.lua rendertext_example.lua unireader.lua
This commit is contained in:
7
.gitignore
vendored
7
.gitignore
vendored
@@ -3,10 +3,17 @@ lua
|
||||
lua-*
|
||||
.reader.kpdfview.lua
|
||||
mupdf-thirdparty.zip
|
||||
djvulibre*
|
||||
kpdfview
|
||||
*.o
|
||||
|
||||
kindlepdfviewer-*.zip
|
||||
|
||||
/.cproject
|
||||
/.project
|
||||
/.reader.kpdfview
|
||||
|
||||
kpvcrlib/CMakeCache.txt
|
||||
kpvcrlib/CMakeFiles/
|
||||
kpvcrlib/cmake_install.cmake
|
||||
kpvcrlib/Makefile
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -7,3 +7,6 @@
|
||||
[submodule "djvulibre"]
|
||||
path = djvulibre
|
||||
url = git://djvu.git.sourceforge.net/gitroot/djvu/djvulibre.git
|
||||
[submodule "kpvcrlib/crengine"]
|
||||
path = kpvcrlib/crengine
|
||||
url = git://crengine.git.sourceforge.net/gitroot/crengine/crengine
|
||||
|
||||
102
Makefile
102
Makefile
@@ -5,15 +5,21 @@ MUPDFDIR=mupdf
|
||||
MUPDFTARGET=build/debug
|
||||
MUPDFLIBDIR=$(MUPDFDIR)/$(MUPDFTARGET)
|
||||
DJVUDIR=djvulibre
|
||||
KPVCRLIGDIR=kpvcrlib
|
||||
CRENGINEDIR=$(KPVCRLIGDIR)/crengine
|
||||
|
||||
FREETYPEDIR=$(MUPDFDIR)/thirdparty/freetype-2.4.8
|
||||
LFSDIR=luafilesystem
|
||||
|
||||
# must point to directory with *.ttf fonts for crengine
|
||||
TTF_FONTS_DIR=$(MUPDFDIR)/fonts
|
||||
|
||||
# set this to your ARM cross compiler:
|
||||
|
||||
CC:=arm-unknown-linux-gnueabi-gcc
|
||||
CXX:=arm-unknown-linux-gnueabi-g++
|
||||
HOST:=arm-unknown-linux-gnueabi
|
||||
HOST:=arm-none-linux-gnueabi
|
||||
CC:=$(HOST)-gcc
|
||||
CXX:=$(HOST)-g++
|
||||
STRIP:=$(HOST)-strip
|
||||
ifdef SBOX_UNAME_MACHINE
|
||||
CC:=gcc
|
||||
CXX:=g++
|
||||
@@ -21,11 +27,18 @@ endif
|
||||
HOSTCC:=gcc
|
||||
HOSTCXX:=g++
|
||||
|
||||
CFLAGS:=-O3
|
||||
CFLAGS:=-O3 $(SYSROOT)
|
||||
CXXFLAGS:=-O3 $(SYSROOT)
|
||||
LDFLAGS:= $(SYSROOT)
|
||||
ARM_CFLAGS:=-march=armv6
|
||||
# use this for debugging:
|
||||
#CFLAGS:=-O0 -g
|
||||
|
||||
DYNAMICLIBSTDCPP:=-lstdc++
|
||||
ifdef STATICLIBSTDCPP
|
||||
DYNAMICLIBSTDCPP:=
|
||||
endif
|
||||
|
||||
# you can configure an emulation for the (eink) framebuffer here.
|
||||
# the application won't use the framebuffer (and the special e-ink ioctls)
|
||||
# in that case.
|
||||
@@ -55,16 +68,27 @@ KPDFREADER_CFLAGS=$(CFLAGS) -I$(LUADIR)/src -I$(MUPDFDIR)/
|
||||
|
||||
MUPDFLIBS := $(MUPDFLIBDIR)/libfitz.a
|
||||
DJVULIBS := $(DJVUDIR)/build/libdjvu/.libs/libdjvulibre.a
|
||||
CRENGINELIBS := $(CRENGINEDIR)/crengine/libcrengine.a \
|
||||
$(CRENGINEDIR)/thirdparty/chmlib/libchmlib.a \
|
||||
$(CRENGINEDIR)/thirdparty/libpng/libpng.a \
|
||||
# we don't support dictionary lookup corrently
|
||||
#$(CRENGINEDIR)/thirdparty/antiword/libantiword.a
|
||||
THIRDPARTYLIBS := $(MUPDFLIBDIR)/libfreetype.a \
|
||||
$(MUPDFLIBDIR)/libjpeg.a \
|
||||
$(MUPDFLIBDIR)/libopenjpeg.a \
|
||||
$(MUPDFLIBDIR)/libjbig2dec.a \
|
||||
$(MUPDFLIBDIR)/libz.a
|
||||
$(MUPDFLIBDIR)/libopenjpeg.a \
|
||||
$(MUPDFLIBDIR)/libjbig2dec.a \
|
||||
$(MUPDFLIBDIR)/libjpeg.a \
|
||||
$(MUPDFLIBDIR)/libz.a
|
||||
|
||||
#@TODO patch crengine to use the latest libjpeg 04.04 2012 (houqp)
|
||||
#$(MUPDFLIBDIR)/libjpeg.a \
|
||||
#$(CRENGINEDIR)/thirdparty/libjpeg/libjpeg.a \
|
||||
|
||||
LUALIB := $(LUADIR)/src/liblua.a
|
||||
|
||||
kpdfview: kpdfview.o einkfb.o pdf.o blitbuffer.o drawcontext.o input.o util.o ft.o lfs.o $(MUPDFLIBS) $(THIRDPARTYLIBS) $(LUALIB) $(DJVULIBS) djvu.o
|
||||
$(CC) -lm -ldl -lpthread $(EMU_LDFLAGS) -lstdc++ \
|
||||
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) -lm -ldl -lpthread $(EMU_LDFLAGS) $(DYNAMICLIBSTDCPP) \
|
||||
kpdfview.o \
|
||||
einkfb.o \
|
||||
pdf.o \
|
||||
@@ -74,44 +98,67 @@ kpdfview: kpdfview.o einkfb.o pdf.o blitbuffer.o drawcontext.o input.o util.o ft
|
||||
util.o \
|
||||
ft.o \
|
||||
lfs.o \
|
||||
mupdfimg.o \
|
||||
$(MUPDFLIBS) \
|
||||
$(THIRDPARTYLIBS) \
|
||||
$(LUALIB) \
|
||||
djvu.o \
|
||||
$(DJVULIBS) \
|
||||
cre.o \
|
||||
$(CRENGINELIBS) \
|
||||
$(STATICLIBSTDCPP) \
|
||||
-o kpdfview
|
||||
|
||||
einkfb.o input.o: %.o: %.c
|
||||
$(CC) -c $(KPDFREADER_CFLAGS) $(EMU_CFLAGS) $< -o $@
|
||||
slider_watcher: slider_watcher.c
|
||||
$(CC) $(CFLAGS) $< -o $@
|
||||
|
||||
ft.o: %.o: %.c
|
||||
$(CC) -c $(KPDFREADER_CFLAGS) -I$(FREETYPEDIR)/include -I$(MUPDFDIR)/fitz $< -o $@
|
||||
|
||||
kpdfview.o pdf.o blitbuffer.o util.o drawcontext.o: %.o: %.c
|
||||
$(CC) -c $(KPDFREADER_CFLAGS) -I$(LFSDIR)/src $< -o $@
|
||||
kpdfview.o pdf.o blitbuffer.o util.o drawcontext.o einkfb.o input.o mupdfimg.o: %.o: %.c
|
||||
$(CC) -c $(KPDFREADER_CFLAGS) $(EMU_CFLAGS) -I$(LFSDIR)/src $< -o $@
|
||||
|
||||
djvu.o: %.o: %.c
|
||||
$(CC) -c $(KPDFREADER_CFLAGS) -I$(DJVUDIR)/ $< -o $@
|
||||
|
||||
cre.o: %.o: %.cpp
|
||||
$(CC) -c -I$(CRENGINEDIR)/crengine/include/ -Ilua/src $< -o $@ -lstdc++
|
||||
|
||||
lfs.o: $(LFSDIR)/src/lfs.c
|
||||
$(CC) -c $(CFLAGS) -I$(LUADIR)/src -I$(LFSDIR)/src $(LFSDIR)/src/lfs.c -o $@
|
||||
|
||||
fetchthirdparty:
|
||||
-rm -Rf lua lua-5.1.4*
|
||||
-rm -Rf lua lua-5.1.4
|
||||
-rm -Rf mupdf/thirdparty
|
||||
test -d mupdf && (cd mupdf; git checkout .)
|
||||
git submodule init
|
||||
git submodule update
|
||||
ln -sf kpvcrlib/crengine/cr3gui/data data
|
||||
test -d fonts || ln -sf $(TTF_FONTS_DIR) fonts
|
||||
# CREngine patch: disable fontconfig
|
||||
grep USE_FONTCONFIG $(CRENGINEDIR)/crengine/include/crsetup.h && grep -v USE_FONTCONFIG $(CRENGINEDIR)/crengine/include/crsetup.h > /tmp/new && mv /tmp/new $(CRENGINEDIR)/crengine/include/crsetup.h
|
||||
test -f mupdf-thirdparty.zip || wget http://www.mupdf.com/download/mupdf-thirdparty.zip
|
||||
unzip mupdf-thirdparty.zip -d mupdf
|
||||
# dirty patch in MuPDF's thirdparty liby for CREngine
|
||||
cd mupdf/thirdparty/jpeg-*/ && \
|
||||
patch -N -p0 < ../../../kpvcrlib/jpeg_compress_struct_size.patch &&\
|
||||
patch -N -p0 < ../../../kpvcrlib/jpeg_decompress_struct_size.patch
|
||||
# MuPDF patch: use external fonts
|
||||
cd mupdf && patch -N -p1 < ../mupdf.patch
|
||||
test -f lua-5.1.4.tar.gz || wget http://www.lua.org/ftp/lua-5.1.4.tar.gz
|
||||
tar xvzf lua-5.1.4.tar.gz && ln -s lua-5.1.4 lua
|
||||
|
||||
clean:
|
||||
-rm -f *.o kpdfview
|
||||
-rm -f *.o kpdfview slider_watcher
|
||||
|
||||
cleanthirdparty:
|
||||
make -C $(LUADIR) clean
|
||||
make -C $(MUPDFDIR) clean
|
||||
#make -C $(CRENGINEDIR)/thirdparty/antiword clean
|
||||
make -C $(CRENGINEDIR)/thirdparty/chmlib clean
|
||||
make -C $(CRENGINEDIR)/thirdparty/libpng clean
|
||||
make -C $(CRENGINEDIR)/crengine clean
|
||||
make -C $(KPVCRLIGDIR) clean
|
||||
-rm -rf $(DJVUDIR)/build
|
||||
-rm -f $(MUPDFDIR)/fontdump.host
|
||||
-rm -f $(MUPDFDIR)/cmapdump.host
|
||||
@@ -128,21 +175,26 @@ $(MUPDFDIR)/cmapdump.host:
|
||||
|
||||
$(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)" make -C mupdf CC="$(CC)" CMAPDUMP=cmapdump.host FONTDUMP=fontdump.host MUPDF= XPS_APPS=
|
||||
CFLAGS="$(CFLAGS) -DNOBUILTINFONT" make -C mupdf CC="$(CC)" CMAPDUMP=cmapdump.host FONTDUMP=fontdump.host MUPDF= XPS_APPS= verbose=1
|
||||
|
||||
$(DJVULIBS):
|
||||
-mkdir $(DJVUDIR)/build
|
||||
ifdef EMULATE_READER
|
||||
cd $(DJVUDIR)/build && ../configure --disable-desktopfiles --disable-shared --enable-static
|
||||
else
|
||||
cd $(DJVUDIR)/build && ../configure --disable-desktopfiles --disable-shared --enable-static --host=$(HOST)
|
||||
cd $(DJVUDIR)/build && ../configure --disable-desktopfiles --disable-shared --enable-static --host=$(HOST) --disable-xmltools --disable-desktopfiles
|
||||
endif
|
||||
make -C $(DJVUDIR)/build
|
||||
|
||||
$(CRENGINELIBS):
|
||||
cd $(KPVCRLIGDIR) && rm -rf CMakeCache.txt CMakeFiles && \
|
||||
CFLAGS="$(CFLAGS)" CC="$(CC)" CXX="$(CXX)" cmake . && \
|
||||
make
|
||||
|
||||
$(LUALIB):
|
||||
make -C lua/src CC="$(CC)" CFLAGS="$(CFLAGS)" MYCFLAGS=-DLUA_USE_LINUX MYLIBS="-Wl,-E" liblua.a
|
||||
|
||||
thirdparty: $(MUPDFLIBS) $(THIRDPARTYLIBS) $(LUALIB) $(DJVULIBS)
|
||||
thirdparty: $(MUPDFLIBS) $(THIRDPARTYLIBS) $(LUALIB) $(DJVULIBS) $(CRENGINELIBS)
|
||||
|
||||
INSTALL_DIR=kindlepdfviewer
|
||||
|
||||
@@ -152,11 +204,19 @@ install:
|
||||
scp launchpad/* root@192.168.2.2:/mnt/us/launchpad/
|
||||
|
||||
VERSION?=$(shell git rev-parse --short HEAD)
|
||||
customupdate: kpdfview
|
||||
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)
|
||||
mkdir $(INSTALL_DIR)
|
||||
cp -p README.TXT COPYING kpdfview *.lua $(INSTALL_DIR)
|
||||
zip -r kindlepdfviewer-$(VERSION).zip $(INSTALL_DIR) launchpad/
|
||||
mkdir $(INSTALL_DIR)/data
|
||||
cp -rpL data/*.css $(INSTALL_DIR)/data
|
||||
cp -rpL fonts $(INSTALL_DIR)
|
||||
cp -r resources $(INSTALL_DIR)
|
||||
mkdir $(INSTALL_DIR)/fonts/host
|
||||
zip -9 -r kindlepdfviewer-$(VERSION).zip $(INSTALL_DIR) launchpad/
|
||||
rm -Rf $(INSTALL_DIR)
|
||||
@echo "copy kindlepdfviewer-$(VERSION).zip to /mnt/us/customupdates and install with shift+shift+I"
|
||||
|
||||
120
blitbuffer.c
120
blitbuffer.c
@@ -366,6 +366,124 @@ static int paintRect(lua_State *L) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int invertRect(lua_State *L) {
|
||||
BlitBuffer *dst = (BlitBuffer*) luaL_checkudata(L, 1, "blitbuffer");
|
||||
int x = luaL_checkint(L, 2);
|
||||
int y = luaL_checkint(L, 3);
|
||||
int w = luaL_checkint(L, 4);
|
||||
int h = luaL_checkint(L, 5);
|
||||
uint8_t *dstptr;
|
||||
|
||||
int cy, cx;
|
||||
if(w <= 0 || h <= 0 || x >= dst->w || y >= dst->h) {
|
||||
return 0;
|
||||
}
|
||||
if(x + w > dst->w) {
|
||||
w = dst->w - x;
|
||||
}
|
||||
if(y + h > dst->h) {
|
||||
h = dst->h - y;
|
||||
}
|
||||
|
||||
if(x & 1) {
|
||||
/* This will invert the leftmost column
|
||||
* in the case when x is odd. After this,
|
||||
* x will become even. */
|
||||
dstptr = (uint8_t*)(dst->data +
|
||||
y * dst->pitch +
|
||||
x / 2);
|
||||
for(cy = 0; cy < h; cy++) {
|
||||
*dstptr ^= 0x0F;
|
||||
dstptr += dst->pitch;
|
||||
}
|
||||
x++;
|
||||
w--;
|
||||
}
|
||||
dstptr = (uint8_t*)(dst->data +
|
||||
y * dst->pitch +
|
||||
x / 2);
|
||||
for(cy = 0; cy < h; cy++) {
|
||||
for(cx = 0; cx < w/2; cx++) {
|
||||
*(dstptr+cx) ^= 0xFF;
|
||||
}
|
||||
dstptr += dst->pitch;
|
||||
}
|
||||
if(w & 1) {
|
||||
/* This will invert the rightmost column
|
||||
* in the case when (w & 1) && !(x & 1) or
|
||||
* !(w & 1) && (x & 1). */
|
||||
dstptr = (uint8_t*)(dst->data +
|
||||
y * dst->pitch +
|
||||
(x + w) / 2);
|
||||
for(cy = 0; cy < h; cy++) {
|
||||
*dstptr ^= 0xF0;
|
||||
dstptr += dst->pitch;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dimRect(lua_State *L) {
|
||||
BlitBuffer *dst = (BlitBuffer*) luaL_checkudata(L, 1, "blitbuffer");
|
||||
int x = luaL_checkint(L, 2);
|
||||
int y = luaL_checkint(L, 3);
|
||||
int w = luaL_checkint(L, 4);
|
||||
int h = luaL_checkint(L, 5);
|
||||
uint8_t *dstptr;
|
||||
|
||||
int cy, cx;
|
||||
if(w <= 0 || h <= 0 || x >= dst->w || y >= dst->h) {
|
||||
return 0;
|
||||
}
|
||||
if(x + w > dst->w) {
|
||||
w = dst->w - x;
|
||||
}
|
||||
if(y + h > dst->h) {
|
||||
h = dst->h - y;
|
||||
}
|
||||
|
||||
if(x & 1) {
|
||||
/* This will dimm the leftmost column
|
||||
* in the case when x is odd. After this,
|
||||
* x will become even. */
|
||||
dstptr = (uint8_t*)(dst->data +
|
||||
y * dst->pitch +
|
||||
x / 2);
|
||||
for(cy = 0; cy < h; cy++) {
|
||||
int px = *dstptr & 0x0F;
|
||||
*dstptr &= 0xF0 | px >> 1;
|
||||
dstptr += dst->pitch;
|
||||
}
|
||||
x++;
|
||||
w--;
|
||||
}
|
||||
dstptr = (uint8_t*)(dst->data +
|
||||
y * dst->pitch +
|
||||
x / 2);
|
||||
for(cy = 0; cy < h; cy++) {
|
||||
for(cx = 0; cx < w/2; cx++) {
|
||||
*(dstptr+cx) =
|
||||
( *(dstptr+cx) >> 1 ) & 0xF0 |
|
||||
( *(dstptr+cx) & 0x0F ) >> 1;
|
||||
}
|
||||
dstptr += dst->pitch;
|
||||
}
|
||||
if(w & 1) {
|
||||
/* This will dimm the rightmost column
|
||||
* in the case when (w & 1) && !(x & 1) or
|
||||
* !(w & 1) && (x & 1). */
|
||||
dstptr = (uint8_t*)(dst->data +
|
||||
y * dst->pitch +
|
||||
(x + w) / 2);
|
||||
for(cy = 0; cy < h; cy++) {
|
||||
int px = *dstptr & 0xF0;
|
||||
*dstptr &= 0x0F | ( px >> 1 & 0xF0 );
|
||||
dstptr += dst->pitch;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct luaL_Reg blitbuffer_func[] = {
|
||||
{"new", newBlitBuffer},
|
||||
{NULL, NULL}
|
||||
@@ -378,6 +496,8 @@ static const struct luaL_Reg blitbuffer_meth[] = {
|
||||
{"addblitFrom", addblitToBuffer},
|
||||
{"blitFullFrom", blitFullToBuffer},
|
||||
{"paintRect", paintRect},
|
||||
{"invertRect", invertRect},
|
||||
{"dimRect", dimRect},
|
||||
{"free", freeBlitBuffer},
|
||||
{"__gc", freeBlitBuffer},
|
||||
{NULL, NULL}
|
||||
|
||||
95
commands.lua
95
commands.lua
@@ -5,6 +5,7 @@ Keydef = {
|
||||
modifier = nil,
|
||||
descr = nil
|
||||
}
|
||||
|
||||
function Keydef:_new(obj)
|
||||
-- obj definition
|
||||
obj = obj or {}
|
||||
@@ -13,6 +14,7 @@ function Keydef:_new(obj)
|
||||
self.__tostring=Keydef.tostring
|
||||
return obj
|
||||
end
|
||||
|
||||
function Keydef:new(keycode,modifier,descr)
|
||||
obj = Keydef:_new()
|
||||
obj.keycode = keycode
|
||||
@@ -20,13 +22,16 @@ function Keydef:new(keycode,modifier,descr)
|
||||
obj.descr = descr
|
||||
return obj
|
||||
end
|
||||
|
||||
function Keydef:display()
|
||||
return (self.modifier or "")..(self.descr or "")
|
||||
end
|
||||
|
||||
function Keydef:tostring()
|
||||
return ((self.modifier and self.modifier.."+") or "").."["..(self.keycode or "").."]"..(self.descr or "")
|
||||
end
|
||||
|
||||
|
||||
Command = {
|
||||
keydef = nil,
|
||||
keygroup = nil,
|
||||
@@ -34,6 +39,7 @@ Command = {
|
||||
help = nil,
|
||||
order = nil
|
||||
}
|
||||
|
||||
function Command:_new(obj)
|
||||
-- obj definition
|
||||
obj = obj or {}
|
||||
@@ -42,6 +48,7 @@ function Command:_new(obj)
|
||||
self.__tostring=Command.tostring
|
||||
return obj
|
||||
end
|
||||
|
||||
function Command:new(keydef, func, help, keygroup, order)
|
||||
obj = Command:_new()
|
||||
obj.keydef = keydef
|
||||
@@ -52,6 +59,7 @@ function Command:new(keydef, func, help, keygroup, order)
|
||||
--print("creating command: ["..tostring(keydef).."] keygroup:["..(keygroup or "").."] help:"..help)
|
||||
return obj
|
||||
end
|
||||
|
||||
function Command:tostring()
|
||||
return tostring(self.keydef)..": "..(self.help or "<no help defined>")
|
||||
end
|
||||
@@ -61,15 +69,54 @@ Commands = {
|
||||
map = {},
|
||||
size = 0
|
||||
}
|
||||
|
||||
function Commands:add(keycode,modifier,keydescr,help,func)
|
||||
local keydef = Keydef:new(keycode,modifier,keydescr)
|
||||
self:_addImpl(keydef,help,func)
|
||||
if type(keycode) == "table" then
|
||||
for i=1,#keycode,1 do
|
||||
local keydef = Keydef:new(keycode[i],modifier,keydescr)
|
||||
self:_addImpl(keydef,help,func)
|
||||
end
|
||||
else
|
||||
local keydef = Keydef:new(keycode,modifier,keydescr)
|
||||
self:_addImpl(keydef,help,func)
|
||||
end
|
||||
end
|
||||
|
||||
function Commands:addGroup(keygroup,keys,help,func)
|
||||
for _k,keydef in pairs(keys) do
|
||||
self:_addImpl(keydef,help,func,keygroup)
|
||||
end
|
||||
end
|
||||
|
||||
--@TODO handle MOD_ANY 06.04 2012 (houqp)
|
||||
function Commands:del(keycode, modifier, keydescr)
|
||||
local keydef = nil
|
||||
|
||||
if not keydescr then
|
||||
for k,v in pairs(self.map) do
|
||||
if v.keydef.keycode == keycode
|
||||
and v.keydef.modifier == modifier then
|
||||
keydef = k
|
||||
break
|
||||
end
|
||||
end -- EOF for
|
||||
else
|
||||
keydef = Keydef:new(keycode, modifier, keydescr)
|
||||
end -- EOF if
|
||||
|
||||
self.map[keydef] = nil
|
||||
end
|
||||
|
||||
function Commands:delGroup(keygroup)
|
||||
if keygroup then
|
||||
for k,v in pairs(self.map) do
|
||||
if v.keygroup == keygroup then
|
||||
self.map[k] = nil
|
||||
end
|
||||
end -- EOF for
|
||||
end
|
||||
end
|
||||
|
||||
function Commands:_addImpl(keydef,help,func,keygroup)
|
||||
if keydef.modifier==MOD_ANY then
|
||||
self:addGroup(keygroup or keydef.descr,{Keydef:new(keydef.keycode,nil), Keydef:new(keydef.keycode,MOD_SHIFT), Keydef:new(keydef.keycode,MOD_ALT)},help,func)
|
||||
@@ -88,25 +135,51 @@ function Commands:_addImpl(keydef,help,func,keygroup)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Commands:get(keycode,modifier)
|
||||
return self.map[Keydef:new(keycode, modifier)]
|
||||
end
|
||||
|
||||
function Commands:getByKeydef(keydef)
|
||||
return self.map[keydef]
|
||||
end
|
||||
|
||||
function Commands:new(obj)
|
||||
-- payload
|
||||
local mt = {}
|
||||
setmetatable(self.map,mt)
|
||||
mt.__index=function (table, key)
|
||||
return rawget(table,(key.modifier or "").."@#@"..(key.keycode or ""))
|
||||
end
|
||||
mt.__newindex=function (table, key, value)
|
||||
return rawset(table,(key.modifier or "").."@#@"..(key.keycode or ""),value)
|
||||
end
|
||||
-- obj definition
|
||||
obj = obj or {}
|
||||
obj.map = {}
|
||||
obj.size = 0
|
||||
setmetatable(obj, self)
|
||||
self.__index = self
|
||||
|
||||
-- payload
|
||||
local mt = {}
|
||||
mt.__index = function(table, key)
|
||||
return rawget(table,(key.modifier or "").."@#@"..(key.keycode or ""))
|
||||
end
|
||||
mt.__newindex = function(table, key, value)
|
||||
return rawset(table,(key.modifier or "").."@#@"..(key.keycode or ""),value)
|
||||
end
|
||||
setmetatable(obj.map, mt)
|
||||
|
||||
obj:add(KEY_INTO_SCREEN_SAVER, nil, "Slider",
|
||||
"toggle screen saver",
|
||||
function()
|
||||
Screen:saveCurrentBB()
|
||||
Screen.kpv_rotation_mode = Screen.cur_rotation_mode
|
||||
fb:setOrientation(Screen.native_rotation_mode)
|
||||
--os.execute("killall -cont cvm")
|
||||
end
|
||||
)
|
||||
obj:add(KEY_OUTOF_SCREEN_SAVER, nil, "Slider",
|
||||
"toggle screen saver",
|
||||
function()
|
||||
util.sleep(3)
|
||||
--os.execute("killall -stop cvm")
|
||||
fb:setOrientation(Screen.kpv_rotation_mode)
|
||||
Screen:restoreFromSavedBB()
|
||||
fb:refresh(0)
|
||||
end
|
||||
)
|
||||
return obj
|
||||
end
|
||||
|
||||
486
cre.cpp
Normal file
486
cre.cpp
Normal file
@@ -0,0 +1,486 @@
|
||||
/*
|
||||
KindlePDFViewer: CREngine abstraction for Lua
|
||||
Copyright (C) 2012 Hans-Werner Hilse <hilse@web.de>
|
||||
Qingping Hou <qingping.hou@gmail.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
extern "C" {
|
||||
#include "blitbuffer.h"
|
||||
#include "drawcontext.h"
|
||||
#include "cre.h"
|
||||
}
|
||||
|
||||
#include "crengine.h"
|
||||
|
||||
|
||||
typedef struct CreDocument {
|
||||
LVDocView *text_view;
|
||||
ldomDocument *dom_doc;
|
||||
} CreDocument;
|
||||
|
||||
|
||||
static int openDocument(lua_State *L) {
|
||||
const char *file_name = luaL_checkstring(L, 1);
|
||||
const char *style_sheet = luaL_checkstring(L, 2);
|
||||
|
||||
int width = luaL_checkint(L, 3);
|
||||
int height = luaL_checkint(L, 4);
|
||||
lString8 css;
|
||||
|
||||
CreDocument *doc = (CreDocument*) lua_newuserdata(L, sizeof(CreDocument));
|
||||
luaL_getmetatable(L, "credocument");
|
||||
lua_setmetatable(L, -2);
|
||||
|
||||
doc->text_view = new LVDocView();
|
||||
//doc->text_view->setBackgroundColor(0xFFFFFF);
|
||||
//doc->text_view->setTextColor(0x000000);
|
||||
if (LVLoadStylesheetFile(lString16(style_sheet), css)){
|
||||
if (!css.empty()){
|
||||
doc->text_view->setStyleSheet(css);
|
||||
}
|
||||
}
|
||||
doc->text_view->setViewMode(DVM_SCROLL, -1);
|
||||
doc->text_view->Resize(width, height);
|
||||
doc->text_view->LoadDocument(file_name);
|
||||
doc->dom_doc = doc->text_view->getDocument();
|
||||
doc->text_view->Render();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int getGammaIndex(lua_State *L) {
|
||||
lua_pushinteger(L, fontMan->GetGammaIndex());
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int setGammaIndex(lua_State *L) {
|
||||
int index = luaL_checkint(L, 1);
|
||||
|
||||
fontMan->SetGammaIndex(index);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int closeDocument(lua_State *L) {
|
||||
CreDocument *doc = (CreDocument*) luaL_checkudata(L, 1, "credocument");
|
||||
delete doc->text_view;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int getNumberOfPages(lua_State *L) {
|
||||
CreDocument *doc = (CreDocument*) luaL_checkudata(L, 1, "credocument");
|
||||
|
||||
lua_pushinteger(L, doc->text_view->getPageCount());
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int getCurrentPage(lua_State *L) {
|
||||
CreDocument *doc = (CreDocument*) luaL_checkudata(L, 1, "credocument");
|
||||
|
||||
lua_pushinteger(L, doc->text_view->getCurPage());
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int getPageFromXPointer(lua_State *L) {
|
||||
CreDocument *doc = (CreDocument*) luaL_checkudata(L, 1, "credocument");
|
||||
const char *xpointer_str = luaL_checkstring(L, 2);
|
||||
|
||||
int page = 0;
|
||||
ldomXPointer xp = doc->dom_doc->createXPointer(lString16(xpointer_str));
|
||||
|
||||
page = doc->text_view->getBookmarkPage(xp);
|
||||
lua_pushinteger(L, page);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int getCurrentPos(lua_State *L) {
|
||||
CreDocument *doc = (CreDocument*) luaL_checkudata(L, 1, "credocument");
|
||||
|
||||
lua_pushinteger(L, doc->text_view->GetPos());
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
//static int getPosFromXPointer(lua_State *L) {
|
||||
//CreDocument *doc = (CreDocument*) luaL_checkudata(L, 1, "credocument");
|
||||
//const char *xpointer_str = luaL_checkstring(L, 2);
|
||||
|
||||
//lvRect rc;
|
||||
//int pos;
|
||||
|
||||
//ldomXPointer *xp = NULL;
|
||||
//xp = doc->dom_doc->createXPointer(lString16(xpointer_str));
|
||||
//getCursorDocRect(*xp, rc);
|
||||
//pos =
|
||||
|
||||
//return 1;
|
||||
//}
|
||||
|
||||
static int getCurrentPercent(lua_State *L) {
|
||||
CreDocument *doc = (CreDocument*) luaL_checkudata(L, 1, "credocument");
|
||||
|
||||
lua_pushinteger(L, doc->text_view->getPosPercent());
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int getXPointer(lua_State *L) {
|
||||
CreDocument *doc = (CreDocument*) luaL_checkudata(L, 1, "credocument");
|
||||
|
||||
ldomXPointer xp = doc->text_view->getBookmark();
|
||||
lua_pushstring(L, UnicodeToLocal(xp.toString()).c_str());
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int getFullHeight(lua_State *L) {
|
||||
CreDocument *doc = (CreDocument*) luaL_checkudata(L, 1, "credocument");
|
||||
|
||||
lua_pushinteger(L, doc->text_view->GetFullHeight());
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* helper function for getTableOfContent()
|
||||
*/
|
||||
static int walkTableOfContent(lua_State *L, LVTocItem *toc, int *count) {
|
||||
LVTocItem *toc_tmp = NULL;
|
||||
int i = 0,
|
||||
nr_child = toc->getChildCount();
|
||||
|
||||
for(i = 0; i < nr_child; i++) {
|
||||
toc_tmp = toc->getChild(i);
|
||||
lua_pushnumber(L, (*count)++);
|
||||
|
||||
/* set subtable, Toc entry */
|
||||
lua_newtable(L);
|
||||
lua_pushstring(L, "page");
|
||||
lua_pushnumber(L, toc_tmp->getPercent());
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_pushstring(L, "xpointer");
|
||||
lua_pushstring(L, UnicodeToLocal(
|
||||
toc_tmp->getXPointer().toString()).c_str()
|
||||
);
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_pushstring(L, "depth");
|
||||
lua_pushnumber(L, toc_tmp->getLevel());
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_pushstring(L, "title");
|
||||
lua_pushstring(L, UnicodeToLocal(toc_tmp->getName()).c_str());
|
||||
lua_settable(L, -3);
|
||||
|
||||
|
||||
/* set Toc entry to Toc table */
|
||||
lua_settable(L, -3);
|
||||
|
||||
if (toc_tmp->getChildCount() > 0) {
|
||||
walkTableOfContent(L, toc_tmp, count);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a table like this:
|
||||
* {
|
||||
* {
|
||||
* page=12,
|
||||
* xpointer = "/body/DocFragment[11].0",
|
||||
* depth=1,
|
||||
* title="chapter1"
|
||||
* },
|
||||
* {
|
||||
* page=54,
|
||||
* xpointer = "/body/DocFragment[13].0",
|
||||
* depth=1,
|
||||
* title="chapter2"
|
||||
* },
|
||||
* }
|
||||
*
|
||||
* Warnning: not like pdf or djvu support, page here refers to the
|
||||
* percent of height within the document, not the real page number.
|
||||
* We use page here just to keep consistent with other readers.
|
||||
*
|
||||
*/
|
||||
static int getTableOfContent(lua_State *L) {
|
||||
CreDocument *doc = (CreDocument*) luaL_checkudata(L, 1, "credocument");
|
||||
|
||||
LVTocItem * toc = doc->text_view->getToc();
|
||||
int count = 0;
|
||||
|
||||
lua_newtable(L);
|
||||
walkTableOfContent(L, toc, &count);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a table like this:
|
||||
* {
|
||||
* "FreeMono",
|
||||
* "FreeSans",
|
||||
* "FreeSerif",
|
||||
* }
|
||||
*
|
||||
*/
|
||||
static int getFontFaces(lua_State *L) {
|
||||
int i = 0;
|
||||
lString16Collection face_list;
|
||||
|
||||
fontMan->getFaceList(face_list);
|
||||
|
||||
lua_newtable(L);
|
||||
for (i = 0; i < face_list.length(); i++)
|
||||
{
|
||||
lua_pushnumber(L, i+1);
|
||||
lua_pushstring(L, UnicodeToLocal(face_list[i]).c_str());
|
||||
lua_settable(L, -3);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int setFontFace(lua_State *L) {
|
||||
CreDocument *doc = (CreDocument*) luaL_checkudata(L, 1, "credocument");
|
||||
const char *face = luaL_checkstring(L, 2);
|
||||
|
||||
doc->text_view->setDefaultFontFace(lString8(face));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gotoPage(lua_State *L) {
|
||||
CreDocument *doc = (CreDocument*) luaL_checkudata(L, 1, "credocument");
|
||||
int pageno = luaL_checkint(L, 2);
|
||||
|
||||
doc->text_view->goToPage(pageno);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gotoPercent(lua_State *L) {
|
||||
CreDocument *doc = (CreDocument*) luaL_checkudata(L, 1, "credocument");
|
||||
int percent = luaL_checkint(L, 2);
|
||||
|
||||
doc->text_view->SetPos(percent * doc->text_view->GetFullHeight() / 10000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gotoPos(lua_State *L) {
|
||||
CreDocument *doc = (CreDocument*) luaL_checkudata(L, 1, "credocument");
|
||||
int pos = luaL_checkint(L, 2);
|
||||
|
||||
doc->text_view->SetPos(pos);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gotoXPointer(lua_State *L) {
|
||||
CreDocument *doc = (CreDocument*) luaL_checkudata(L, 1, "credocument");
|
||||
const char *xpointer_str = luaL_checkstring(L, 2);
|
||||
|
||||
ldomXPointer xp = doc->dom_doc->createXPointer(lString16(xpointer_str));
|
||||
|
||||
doc->text_view->goToBookmark(xp);
|
||||
/* CREngine does not call checkPos() immediately after goToBookmark,
|
||||
* so I have to manually update the pos in order to get a correct
|
||||
* return from GetPos() call. */
|
||||
doc->text_view->SetPos(xp.toPoint().y);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* zoom font by given delta and return zoomed font size */
|
||||
static int zoomFont(lua_State *L) {
|
||||
CreDocument *doc = (CreDocument*) luaL_checkudata(L, 1, "credocument");
|
||||
int delta = luaL_checkint(L, 2);
|
||||
|
||||
doc->text_view->ZoomFont(delta);
|
||||
|
||||
lua_pushnumber(L, doc->text_view->getFontSize());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int setFontSize(lua_State *L) {
|
||||
CreDocument *doc = (CreDocument*) luaL_checkudata(L, 1, "credocument");
|
||||
int size = luaL_checkint(L, 2);
|
||||
|
||||
doc->text_view->setFontSize(size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setDefaultInterlineSpace(lua_State *L) {
|
||||
CreDocument *doc = (CreDocument*) luaL_checkudata(L, 1, "credocument");
|
||||
int space = luaL_checkint(L, 2);
|
||||
|
||||
doc->text_view->setDefaultInterlineSpace(space);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setStyleSheet(lua_State *L) {
|
||||
CreDocument *doc = (CreDocument*) luaL_checkudata(L, 1, "credocument");
|
||||
const char* style_sheet_data = luaL_checkstring(L, 2);
|
||||
|
||||
doc->text_view->setStyleSheet(lString8(style_sheet_data));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int toggleFontBolder(lua_State *L) {
|
||||
CreDocument *doc = (CreDocument*) luaL_checkudata(L, 1, "credocument");
|
||||
|
||||
doc->text_view->doCommand(DCMD_TOGGLE_BOLD);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cursorRight(lua_State *L) {
|
||||
//CreDocument *doc = (CreDocument*) luaL_checkudata(L, 1, "credocument");
|
||||
|
||||
//LVDocView *tv = doc->text_view;
|
||||
|
||||
//ldomXPointer p = tv->getCurrentPageMiddleParagraph();
|
||||
//lString16 s = p.toString();
|
||||
//printf("~~~~~~~~~~%s\n", UnicodeToLocal(s).c_str());
|
||||
|
||||
//tv->selectRange(*(tv->selectFirstPageLink()));
|
||||
//ldomXRange *r = tv->selectNextPageLink(true);
|
||||
//lString16 s = r->getRangeText();
|
||||
//printf("------%s\n", UnicodeToLocal(s).c_str());
|
||||
|
||||
//tv->selectRange(*r);
|
||||
//tv->updateSelections();
|
||||
|
||||
//LVPageWordSelector sel(doc->text_view);
|
||||
//doc->text_view->doCommand(DCMD_SELECT_FIRST_SENTENCE);
|
||||
//sel.moveBy(DIR_RIGHT, 2);
|
||||
//sel.updateSelection();
|
||||
//printf("---------------- %s\n", UnicodeToLocal(sel.getSelectedWord()->getText()).c_str());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int drawCurrentPage(lua_State *L) {
|
||||
CreDocument *doc = (CreDocument*) luaL_checkudata(L, 1, "credocument");
|
||||
DrawContext *dc = (DrawContext*) luaL_checkudata(L, 2, "drawcontext");
|
||||
BlitBuffer *bb = (BlitBuffer*) luaL_checkudata(L, 3, "blitbuffer");
|
||||
|
||||
int w = bb->w,
|
||||
h = bb->h;
|
||||
/* Set DrawBuf to 4bpp */
|
||||
LVGrayDrawBuf drawBuf(w, h, 4);
|
||||
|
||||
doc->text_view->Resize(w, h);
|
||||
doc->text_view->Render();
|
||||
doc->text_view->Draw(drawBuf);
|
||||
|
||||
uint8_t *bbptr = (uint8_t*)bb->data;
|
||||
uint8_t *pmptr = (uint8_t*)drawBuf.GetScanLine(0);
|
||||
int i,x;
|
||||
|
||||
for (i = 0; i < h; i++) {
|
||||
for (x = 0; x < (bb->w / 2); x++) {
|
||||
/* When DrawBuf is set to 4bpp mode, CREngine still put every
|
||||
* four bits in one byte, but left the last 4 bits zero*/
|
||||
bbptr[x] = ~(pmptr[x*2] | (pmptr[x*2+1] >> 4));
|
||||
}
|
||||
if(bb->w & 1) {
|
||||
bbptr[x] = 255 - (pmptr[x*2] & 0xF0);
|
||||
}
|
||||
bbptr += bb->pitch;
|
||||
pmptr += w;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int registerFont(lua_State *L) {
|
||||
const char *fontfile = luaL_checkstring(L, 1);
|
||||
if ( !fontMan->RegisterFont(lString8(fontfile)) ) {
|
||||
return luaL_error(L, "cannot register font <%s>", fontfile);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct luaL_Reg cre_func[] = {
|
||||
{"openDocument", openDocument},
|
||||
{"getFontFaces", getFontFaces},
|
||||
{"getGammaIndex", getGammaIndex},
|
||||
{"setGammaIndex", setGammaIndex},
|
||||
{"registerFont", registerFont},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
static const struct luaL_Reg credocument_meth[] = {
|
||||
/*--- get methods ---*/
|
||||
{"getPages", getNumberOfPages},
|
||||
{"getCurrentPage", getCurrentPage},
|
||||
{"getPageFromXPointer", getPageFromXPointer},
|
||||
{"getCurrentPos", getCurrentPos},
|
||||
{"getCurrentPercent", getCurrentPercent},
|
||||
{"getXPointer", getXPointer},
|
||||
{"getFullHeight", getFullHeight},
|
||||
{"getToc", getTableOfContent},
|
||||
/*--- set methods ---*/
|
||||
{"setFontFace", setFontFace},
|
||||
{"setFontSize", setFontSize},
|
||||
{"setDefaultInterlineSpace", setDefaultInterlineSpace},
|
||||
{"setStyleSheet", setStyleSheet},
|
||||
/* --- control methods ---*/
|
||||
{"gotoPage", gotoPage},
|
||||
{"gotoPercent", gotoPercent},
|
||||
{"gotoPos", gotoPos},
|
||||
{"gotoXPointer", gotoXPointer},
|
||||
{"zoomFont", zoomFont},
|
||||
{"toggleFontBolder", toggleFontBolder},
|
||||
//{"cursorLeft", cursorLeft},
|
||||
//{"cursorRight", cursorRight},
|
||||
{"drawCurrentPage", drawCurrentPage},
|
||||
{"close", closeDocument},
|
||||
{"__gc", closeDocument},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
int luaopen_cre(lua_State *L) {
|
||||
luaL_newmetatable(L, "credocument");
|
||||
lua_pushstring(L, "__index");
|
||||
lua_pushvalue(L, -2);
|
||||
lua_settable(L, -3);
|
||||
luaL_register(L, NULL, credocument_meth);
|
||||
lua_pop(L, 1);
|
||||
luaL_register(L, "cre", cre_func);
|
||||
|
||||
|
||||
/* initialize font manager for CREngine */
|
||||
InitFontManager(lString8());
|
||||
|
||||
#ifdef DEBUG_CRENGINE
|
||||
CRLog::setStdoutLogger();
|
||||
CRLog::setLogLevel(CRLog::LL_DEBUG);
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
28
cre.h
Normal file
28
cre.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
KindlePDFViewer: CREngine abstraction for Lua
|
||||
Copyright (C) 2012 Hans-Werner Hilse <hilse@web.de>
|
||||
Qingping Hou <qingping.hou@gmail.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _CRENGING_H
|
||||
#define _CRENGING_H
|
||||
|
||||
#include <lua.h>
|
||||
#include <lualib.h>
|
||||
#include <lauxlib.h>
|
||||
|
||||
int luaopen_cre(lua_State *L);
|
||||
#endif
|
||||
375
crereader.lua
Normal file
375
crereader.lua
Normal file
@@ -0,0 +1,375 @@
|
||||
require "font"
|
||||
require "unireader"
|
||||
require "inputbox"
|
||||
require "selectmenu"
|
||||
|
||||
CREReader = UniReader:new{
|
||||
pos = nil,
|
||||
percent = 0,
|
||||
|
||||
gamma_index = 15,
|
||||
font_face = nil,
|
||||
|
||||
line_space_percent = 100,
|
||||
}
|
||||
|
||||
function CREReader:init()
|
||||
self:addAllCommands()
|
||||
self:adjustCreReaderCommands()
|
||||
-- we need to initialize the CRE font list
|
||||
local fonts = Font:getFontList()
|
||||
for _k, _v in ipairs(fonts) do
|
||||
local ok, err = pcall(cre.registerFont, Font.fontdir..'/'.._v)
|
||||
if not ok then
|
||||
print(err)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- open a CREngine supported file and its settings store
|
||||
function CREReader:open(filename)
|
||||
local ok
|
||||
local file_type = string.lower(string.match(filename, ".+%.([^.]+)"))
|
||||
-- these two format use the same css file
|
||||
if file_type == "html" then
|
||||
file_type = "htm"
|
||||
end
|
||||
local style_sheet = "./data/"..file_type..".css"
|
||||
ok, self.doc = pcall(cre.openDocument, filename, style_sheet,
|
||||
G_width, G_height)
|
||||
if not ok then
|
||||
return false, self.doc -- will contain error message
|
||||
end
|
||||
|
||||
self.doc:setDefaultInterlineSpace(self.line_space_percent)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
----------------------------------------------------
|
||||
-- setting related methods
|
||||
----------------------------------------------------
|
||||
function CREReader:loadSpecialSettings()
|
||||
local font_face = self.settings:readSetting("font_face")
|
||||
self.font_face = font_face or "FreeSerif"
|
||||
self.doc:setFontFace(self.font_face)
|
||||
|
||||
local gamma_index = self.settings:readSetting("gamma_index")
|
||||
self.gamma_index = gamma_index or self.gamma_index
|
||||
cre.setGammaIndex(self.gamma_index)
|
||||
|
||||
local line_space_percent = self.settings:readSetting("line_space_percent")
|
||||
self.line_space_percent = line_space_percent or self.line_space_percent
|
||||
end
|
||||
|
||||
function CREReader:getLastPageOrPos()
|
||||
local last_percent = self.settings:readSetting("last_percent")
|
||||
if last_percent then
|
||||
return math.floor((last_percent * self.doc:getFullHeight()) / 10000)
|
||||
else
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
function CREReader:saveSpecialSettings()
|
||||
self.settings:savesetting("font_face", self.font_face)
|
||||
self.settings:savesetting("gamma_index", self.gamma_index)
|
||||
self.settings:savesetting("line_space_percent", self.line_space_percent)
|
||||
end
|
||||
|
||||
function CREReader:saveLastPageOrPos()
|
||||
self.settings:savesetting("last_percent", self.percent)
|
||||
end
|
||||
|
||||
----------------------------------------------------
|
||||
-- render related methods
|
||||
----------------------------------------------------
|
||||
-- we don't need setzoom in CREReader
|
||||
function CREReader:setzoom(page, preCache)
|
||||
return
|
||||
end
|
||||
|
||||
function CREReader:redrawCurrentPage()
|
||||
self:goto(self.pos)
|
||||
end
|
||||
|
||||
-- there is no zoom mode in CREReader
|
||||
function CREReader:setGlobalZoomMode()
|
||||
return
|
||||
end
|
||||
|
||||
----------------------------------------------------
|
||||
-- goto related methods
|
||||
----------------------------------------------------
|
||||
function CREReader:goto(pos, pos_type)
|
||||
local prev_xpointer = self.doc:getXPointer()
|
||||
local width, height = G_width, G_height
|
||||
|
||||
if pos_type == "xpointer" then
|
||||
self.doc:gotoXPointer(pos)
|
||||
pos = self.doc:getCurrentPos()
|
||||
else -- pos_type is PERCENT * 100
|
||||
pos = math.min(pos, self.doc:getFullHeight() - height)
|
||||
pos = math.max(pos, 0)
|
||||
self.doc:gotoPos(pos)
|
||||
end
|
||||
|
||||
-- add to jump_stack, distinguish jump from normal page turn
|
||||
-- NOTE:
|
||||
-- even though we have called gotoPos() or gotoXPointer() previously,
|
||||
-- self.pos hasn't been updated yet here, so we can still make use of it.
|
||||
if self.pos and math.abs(self.pos - pos) > height then
|
||||
self:addJump(prev_xpointer)
|
||||
end
|
||||
|
||||
self.doc:drawCurrentPage(self.nulldc, fb.bb)
|
||||
|
||||
print("## self.show_overlap "..self.show_overlap)
|
||||
if self.show_overlap < 0 then
|
||||
fb.bb:dimRect(0,0, width, -self.show_overlap)
|
||||
elseif self.show_overlap > 0 then
|
||||
fb.bb:dimRect(0,height - self.show_overlap, width, self.show_overlap)
|
||||
end
|
||||
self.show_overlap = 0
|
||||
|
||||
if self.rcount == self.rcountmax then
|
||||
print("full refresh")
|
||||
self.rcount = 1
|
||||
fb:refresh(0)
|
||||
else
|
||||
print("partial refresh")
|
||||
self.rcount = self.rcount + 1
|
||||
fb:refresh(1)
|
||||
end
|
||||
|
||||
self.pos = pos
|
||||
print("------", self.pos)
|
||||
self.pageno = self.doc:getCurrentPage()
|
||||
self.percent = self.doc:getCurrentPercent()
|
||||
end
|
||||
|
||||
function CREReader:gotoPercent(percent)
|
||||
self:goto(percent * self.doc:getFullHeight() / 10000)
|
||||
end
|
||||
|
||||
function CREReader:gotoTocEntry(entry)
|
||||
self:goto(entry.xpointer, "xpointer")
|
||||
end
|
||||
|
||||
function CREReader:nextView()
|
||||
self.show_overlap = -self.pan_overlap_vertical
|
||||
return self.pos + G_height - self.pan_overlap_vertical
|
||||
end
|
||||
|
||||
function CREReader:prevView()
|
||||
self.show_overlap = self.pan_overlap_vertical
|
||||
return self.pos - G_height + self.pan_overlap_vertical
|
||||
end
|
||||
|
||||
----------------------------------------------------
|
||||
-- jump stack related methods
|
||||
----------------------------------------------------
|
||||
function CREReader:isSamePage(p1, p2)
|
||||
return self.doc:getPageFromXPointer(p1) == self.doc:getPageFromXPointer(p2)
|
||||
end
|
||||
|
||||
function CREReader:showJumpStack()
|
||||
local menu_items = {}
|
||||
print(dump(self.jump_stack))
|
||||
for k,v in ipairs(self.jump_stack) do
|
||||
table.insert(menu_items,
|
||||
v.datetime.." -> page "..
|
||||
(self.doc:getPageFromXPointer(v.page)).." "..v.notes)
|
||||
end
|
||||
jump_menu = SelectMenu:new{
|
||||
menu_title = "Jump Keeper (current page: "..self.pageno..")",
|
||||
item_array = menu_items,
|
||||
no_item_msg = "No jump history.",
|
||||
}
|
||||
item_no = jump_menu:choose(0, fb.bb:getHeight())
|
||||
if item_no then
|
||||
local jump_item = self.jump_stack[item_no]
|
||||
self:goto(jump_item.page, "xpointer")
|
||||
else
|
||||
self:redrawCurrentPage()
|
||||
end
|
||||
end
|
||||
|
||||
----------------------------------------------------
|
||||
-- TOC related methods
|
||||
----------------------------------------------------
|
||||
function CREReader:getTocTitleOfCurrentPage()
|
||||
return self:getTocTitleByPage(self.percent)
|
||||
end
|
||||
|
||||
|
||||
----------------------------------------------------
|
||||
-- menu related methods
|
||||
----------------------------------------------------
|
||||
-- used in CREReader:showMenu()
|
||||
function CREReader:_drawReadingInfo()
|
||||
local ypos = G_height - 50
|
||||
local load_percent = self.percent/100
|
||||
|
||||
fb.bb:paintRect(0, ypos, G_width, 50, 0)
|
||||
|
||||
ypos = ypos + 15
|
||||
local face = Font:getFace("rifont", 22)
|
||||
local cur_section = self:getTocTitleOfCurrentPage()
|
||||
if cur_section ~= "" then
|
||||
cur_section = "Section: "..cur_section
|
||||
end
|
||||
renderUtf8Text(fb.bb, 10, ypos+6, face,
|
||||
"Position: "..load_percent.."%".." "..cur_section, true)
|
||||
|
||||
ypos = ypos + 15
|
||||
blitbuffer.progressBar(fb.bb, 10, ypos, G_width - 20, 15,
|
||||
5, 4, load_percent/100, 8)
|
||||
end
|
||||
|
||||
|
||||
|
||||
function CREReader:adjustCreReaderCommands()
|
||||
-- delete commands
|
||||
self.commands:delGroup("[joypad]")
|
||||
self.commands:del(KEY_G, nil, "G")
|
||||
self.commands:del(KEY_J, MOD_SHIFT, "J")
|
||||
self.commands:del(KEY_K, MOD_SHIFT, "K")
|
||||
self.commands:del(KEY_Z, nil, "Z")
|
||||
self.commands:del(KEY_Z, MOD_SHIFT, "Z")
|
||||
self.commands:del(KEY_Z, MOD_ALT, "Z")
|
||||
self.commands:del(KEY_A, nil, "A")
|
||||
self.commands:del(KEY_A, MOD_SHIFT, "A")
|
||||
self.commands:del(KEY_A, MOD_ALT, "A")
|
||||
self.commands:del(KEY_S, nil, "S")
|
||||
self.commands:del(KEY_S, MOD_SHIFT, "S")
|
||||
self.commands:del(KEY_S, MOD_ALT, "S")
|
||||
self.commands:del(KEY_D, nil, "D")
|
||||
self.commands:del(KEY_D, MOD_SHIFT, "D")
|
||||
self.commands:del(KEY_D, MOD_ALT, "D")
|
||||
self.commands:del(KEY_F, MOD_SHIFT, "F")
|
||||
self.commands:del(KEY_F, MOD_ALT, "F")
|
||||
self.commands:del(KEY_N, nil, "N") -- highlight
|
||||
self.commands:del(KEY_N, MOD_SHIFT, "N") -- show highlights
|
||||
|
||||
-- overwrite commands
|
||||
self.commands:add(KEY_PGFWD, MOD_SHIFT, ">",
|
||||
"increase font size",
|
||||
function(cr)
|
||||
cr.doc:zoomFont(1)
|
||||
cr:redrawCurrentPage()
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_PGBCK, MOD_SHIFT, "<",
|
||||
"decrease font size",
|
||||
function(cr)
|
||||
cr.doc:zoomFont(-1)
|
||||
cr:redrawCurrentPage()
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_PGFWD, MOD_ALT, ">",
|
||||
"increase line spacing",
|
||||
function(cr)
|
||||
self.line_space_percent = self.line_space_percent + 10
|
||||
if self.line_space_percent > 200 then
|
||||
self.line_space_percent = 200
|
||||
end
|
||||
print("line spacing set to", self.line_space_percent)
|
||||
cr.doc:setDefaultInterlineSpace(self.line_space_percent)
|
||||
cr:redrawCurrentPage()
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_PGBCK, MOD_ALT, "<",
|
||||
"decrease line spacing",
|
||||
function(cr)
|
||||
self.line_space_percent = self.line_space_percent - 10
|
||||
if self.line_space_percent < 100 then
|
||||
self.line_space_percent = 100
|
||||
end
|
||||
print("line spacing set to", self.line_space_percent)
|
||||
cr.doc:setDefaultInterlineSpace(self.line_space_percent)
|
||||
cr:redrawCurrentPage()
|
||||
end
|
||||
)
|
||||
local numeric_keydefs = {}
|
||||
for i=1,10 do
|
||||
numeric_keydefs[i]=Keydef:new(KEY_1+i-1, nil, tostring(i%10))
|
||||
end
|
||||
self.commands:addGroup("[1..0]", numeric_keydefs,
|
||||
"jump to <key>*10% of document",
|
||||
function(cr, keydef)
|
||||
print('jump to position: '..
|
||||
math.floor(cr.doc:getFullHeight()*(keydef.keycode-KEY_1)/9)..
|
||||
'/'..cr.doc:getFullHeight())
|
||||
cr:goto(math.floor(cr.doc:getFullHeight()*(keydef.keycode-KEY_1)/9))
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_F, nil, "F",
|
||||
"invoke font menu",
|
||||
function(cr)
|
||||
local face_list = cre.getFontFaces()
|
||||
|
||||
local fonts_menu = SelectMenu:new{
|
||||
menu_title = "Fonts Menu",
|
||||
item_array = face_list,
|
||||
}
|
||||
|
||||
local item_no = fonts_menu:choose(0, G_height)
|
||||
print(face_list[item_no])
|
||||
if item_no then
|
||||
cr.doc:setFontFace(face_list[item_no])
|
||||
self.font_face = face_list[item_no]
|
||||
end
|
||||
cr:redrawCurrentPage()
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_F, MOD_ALT, "F",
|
||||
"Toggle font bolder attribute",
|
||||
function(cr)
|
||||
cr.doc:toggleFontBolder()
|
||||
cr:redrawCurrentPage()
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_B, MOD_SHIFT, "B",
|
||||
"add jump",
|
||||
function(cr)
|
||||
cr:addJump(self.doc:getXPointer())
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_BACK,nil,"back",
|
||||
"back to last jump",
|
||||
function(cr)
|
||||
if #cr.jump_stack ~= 0 then
|
||||
cr:goto(cr.jump_stack[1].page, "xpointer")
|
||||
end
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_VPLUS, nil, "vol+",
|
||||
"increase gamma",
|
||||
function(cr)
|
||||
cre.setGammaIndex(self.gamma_index + 1)
|
||||
self.gamma_index = cre.getGammaIndex()
|
||||
cr:redrawCurrentPage()
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_VMINUS, nil, "vol-",
|
||||
"decrease gamma",
|
||||
function(cr)
|
||||
cre.setGammaIndex(self.gamma_index - 1)
|
||||
self.gamma_index = cre.getGammaIndex()
|
||||
cr:redrawCurrentPage()
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_FW_UP, nil, "joypad up",
|
||||
"pan "..self.shift_y.." pixels upwards",
|
||||
function(cr)
|
||||
cr:goto(cr.pos - cr.shift_y)
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_FW_DOWN, nil, "joypad down",
|
||||
"pan "..self.shift_y.." pixels downwards",
|
||||
function(cr)
|
||||
cr:goto(cr.pos + cr.shift_y)
|
||||
end
|
||||
)
|
||||
end
|
||||
43
dialog.lua
Normal file
43
dialog.lua
Normal file
@@ -0,0 +1,43 @@
|
||||
require "widget"
|
||||
require "font"
|
||||
|
||||
InfoMessage = {
|
||||
face = Font:getFace("infofont", 25)
|
||||
}
|
||||
|
||||
function InfoMessage:show(text)
|
||||
local dialog = CenterContainer:new({
|
||||
dimen = { w = G_width, h = G_height },
|
||||
FrameContainer:new({
|
||||
margin = 2,
|
||||
background = 0,
|
||||
HorizontalGroup:new({
|
||||
align = "center",
|
||||
ImageWidget:new({
|
||||
file = "resources/info-i.png"
|
||||
}),
|
||||
Widget:new({
|
||||
dimen = { w = 10, h = 0 }
|
||||
}),
|
||||
TextWidget:new({
|
||||
text = text,
|
||||
face = Font:getFace("cfont", 30)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
dialog:paintTo(fb.bb, 0, 0)
|
||||
dialog:free()
|
||||
end
|
||||
|
||||
function showInfoMsgWithDelay(text, msec, refresh_mode)
|
||||
if not refresh_mode then refresh_mode = 0 end
|
||||
Screen:saveCurrentBB()
|
||||
|
||||
InfoMessage:show(text)
|
||||
fb:refresh(refresh_mode)
|
||||
util.usleep(msec*1000)
|
||||
|
||||
Screen:restoreFromSavedBB()
|
||||
fb:refresh(refresh_mode)
|
||||
end
|
||||
149
djvu.c
149
djvu.c
@@ -71,7 +71,7 @@ static int handle(lua_State *L, ddjvu_context_t *ctx, int wait)
|
||||
|
||||
static int openDocument(lua_State *L) {
|
||||
const char *filename = luaL_checkstring(L, 1);
|
||||
/*const char *password = luaL_checkstring(L, 2);*/
|
||||
int cache_size = luaL_optint(L, 2, 10 << 20);
|
||||
|
||||
DjvuDocument *doc = (DjvuDocument*) lua_newuserdata(L, sizeof(DjvuDocument));
|
||||
luaL_getmetatable(L, "djvudocument");
|
||||
@@ -82,6 +82,9 @@ static int openDocument(lua_State *L) {
|
||||
return luaL_error(L, "cannot create context.");
|
||||
}
|
||||
|
||||
printf("## cache_size = %d\n", cache_size);
|
||||
ddjvu_cache_set_size(doc->context, (unsigned long)cache_size);
|
||||
|
||||
doc->doc_ref = ddjvu_document_create_by_filename_utf8(doc->context, filename, TRUE);
|
||||
while (! ddjvu_document_decoding_done(doc->doc_ref))
|
||||
handle(L, doc->context, True);
|
||||
@@ -206,7 +209,7 @@ static int openPage(lua_State *L) {
|
||||
static int getPageSize(lua_State *L) {
|
||||
DjvuPage *page = (DjvuPage*) luaL_checkudata(L, 1, "djvupage");
|
||||
DrawContext *dc = (DrawContext*) luaL_checkudata(L, 2, "drawcontext");
|
||||
|
||||
|
||||
lua_pushnumber(L, dc->zoom * page->info.width);
|
||||
lua_pushnumber(L, dc->zoom * page->info.height);
|
||||
|
||||
@@ -225,6 +228,128 @@ static int getUsedBBox(lua_State *L) {
|
||||
return 4;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return a table like following:
|
||||
* {
|
||||
* -- a line entry
|
||||
* 1 = {
|
||||
* 1 = {word="This", x0=377, y0=4857, x1=2427, y1=5089},
|
||||
* 2 = {word="is", x0=377, y0=4857, x1=2427, y1=5089},
|
||||
* 3 = {word="Word", x0=377, y0=4857, x1=2427, y1=5089},
|
||||
* 4 = {word="List", x0=377, y0=4857, x1=2427, y1=5089},
|
||||
* x0 = 377, y0 = 4857, x1 = 2427, y1 = 5089,
|
||||
* },
|
||||
*
|
||||
* -- an other line entry
|
||||
* 2 = {
|
||||
* 1 = {word="This", x0=377, y0=4857, x1=2427, y1=5089},
|
||||
* 2 = {word="is", x0=377, y0=4857, x1=2427, y1=5089},
|
||||
* x0 = 377, y0 = 4857, x1 = 2427, y1 = 5089,
|
||||
* },
|
||||
* }
|
||||
*/
|
||||
static int getPageText(lua_State *L) {
|
||||
DjvuDocument *doc = (DjvuDocument*) luaL_checkudata(L, 1, "djvudocument");
|
||||
int pageno = luaL_checkint(L, 2);
|
||||
|
||||
miniexp_t sexp, se_line, se_word;
|
||||
int i = 1, j = 1, counter_l = 1, counter_w=1,
|
||||
nr_line = 0, nr_word = 0;
|
||||
const char *word = NULL;
|
||||
|
||||
while ((sexp = ddjvu_document_get_pagetext(doc->doc_ref, pageno-1, "word"))
|
||||
== miniexp_dummy) {
|
||||
handle(L, doc->context, True);
|
||||
}
|
||||
|
||||
/* throuw page info and obtain lines info, after this, sexp's entries
|
||||
* are lines. */
|
||||
sexp = miniexp_cdr(sexp);
|
||||
/* get number of lines in a page */
|
||||
nr_line = miniexp_length(sexp);
|
||||
/* table that contains all the lines */
|
||||
lua_newtable(L);
|
||||
|
||||
counter_l = 1;
|
||||
for(i = 1; i <= nr_line; i++) {
|
||||
/* retrive one line entry */
|
||||
se_line = miniexp_nth(i, sexp);
|
||||
nr_word = miniexp_length(se_line);
|
||||
if(nr_word == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* subtable that contains words in a line */
|
||||
lua_pushnumber(L, counter_l);
|
||||
lua_newtable(L);
|
||||
counter_l++;
|
||||
|
||||
/* set line position */
|
||||
lua_pushstring(L, "x0");
|
||||
lua_pushnumber(L, miniexp_to_int(miniexp_nth(1, se_line)));
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_pushstring(L, "y0");
|
||||
lua_pushnumber(L, miniexp_to_int(miniexp_nth(2, se_line)));
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_pushstring(L, "x1");
|
||||
lua_pushnumber(L, miniexp_to_int(miniexp_nth(3, se_line)));
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_pushstring(L, "y1");
|
||||
lua_pushnumber(L, miniexp_to_int(miniexp_nth(4, se_line)));
|
||||
lua_settable(L, -3);
|
||||
|
||||
/* now loop through each word in the line */
|
||||
counter_w = 1;
|
||||
for(j = 1; j <= nr_word; j++) {
|
||||
/* retrive one word entry */
|
||||
se_word = miniexp_nth(j, se_line);
|
||||
/* check to see whether the entry is empty */
|
||||
word = miniexp_to_str(miniexp_nth(5, se_word));
|
||||
if (!word) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* create table that contains info for a word */
|
||||
lua_pushnumber(L, counter_w);
|
||||
lua_newtable(L);
|
||||
counter_w++;
|
||||
|
||||
/* set word info */
|
||||
lua_pushstring(L, "x0");
|
||||
lua_pushnumber(L, miniexp_to_int(miniexp_nth(1, se_word)));
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_pushstring(L, "y0");
|
||||
lua_pushnumber(L, miniexp_to_int(miniexp_nth(2, se_word)));
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_pushstring(L, "x1");
|
||||
lua_pushnumber(L, miniexp_to_int(miniexp_nth(3, se_word)));
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_pushstring(L, "y1");
|
||||
lua_pushnumber(L, miniexp_to_int(miniexp_nth(4, se_word)));
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_pushstring(L, "word");
|
||||
lua_pushstring(L, word);
|
||||
lua_settable(L, -3);
|
||||
|
||||
/* set word entry to line subtable */
|
||||
lua_settable(L, -3);
|
||||
} /* end of for (j) */
|
||||
|
||||
/* set line entry to page text table */
|
||||
lua_settable(L, -3);
|
||||
} /* end of for (i) */
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int closePage(lua_State *L) {
|
||||
DjvuPage *page = (DjvuPage*) luaL_checkudata(L, 1, "djvupage");
|
||||
if(page->page_ref != NULL) {
|
||||
@@ -325,6 +450,21 @@ static int drawPage(lua_State *L) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int getCacheSize(lua_State *L) {
|
||||
DjvuDocument *doc = (DjvuDocument*) luaL_checkudata(L, 1, "djvudocument");
|
||||
unsigned long size = ddjvu_cache_get_size(doc->context);
|
||||
printf("## ddjvu_cache_get_size = %d\n", size);
|
||||
lua_pushnumber(L, size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int cleanCache(lua_State *L) {
|
||||
DjvuDocument *doc = (DjvuDocument*) luaL_checkudata(L, 1, "djvudocument");
|
||||
printf("## ddjvu_cache_clear\n");
|
||||
ddjvu_cache_clear(doc->context);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct luaL_Reg djvu_func[] = {
|
||||
{"openDocument", openDocument},
|
||||
{NULL, NULL}
|
||||
@@ -333,8 +473,11 @@ static const struct luaL_Reg djvu_func[] = {
|
||||
static const struct luaL_Reg djvudocument_meth[] = {
|
||||
{"openPage", openPage},
|
||||
{"getPages", getNumberOfPages},
|
||||
{"getTOC", getTableOfContent},
|
||||
{"getToc", getTableOfContent},
|
||||
{"getPageText", getPageText},
|
||||
{"close", closeDocument},
|
||||
{"getCacheSize", getCacheSize},
|
||||
{"cleanCache", cleanCache},
|
||||
{"__gc", closeDocument},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
@@ -6,9 +6,81 @@ DJVUReader = UniReader:new{}
|
||||
-- DJVU does not support password yet
|
||||
function DJVUReader:open(filename)
|
||||
local ok
|
||||
ok, self.doc = pcall(djvu.openDocument, filename)
|
||||
ok, self.doc = pcall(djvu.openDocument, filename, self.cache_document_size)
|
||||
if not ok then
|
||||
return ok, self.doc -- this will be the error message instead
|
||||
end
|
||||
return ok
|
||||
end
|
||||
|
||||
function DJVUReader:init()
|
||||
self:addAllCommands()
|
||||
self:adjustDjvuReaderCommand()
|
||||
end
|
||||
|
||||
function DJVUReader:adjustDjvuReaderCommand()
|
||||
self.commands:del(KEY_J, MOD_SHIFT, "J")
|
||||
self.commands:del(KEY_K, MOD_SHIFT, "K")
|
||||
end
|
||||
|
||||
|
||||
----------------------------------------------------
|
||||
-- highlight support
|
||||
----------------------------------------------------
|
||||
function DJVUReader:getText(pageno)
|
||||
return self.doc:getPageText(pageno)
|
||||
end
|
||||
|
||||
----------------------------------------------------
|
||||
-- In djvulibre library, some coordinates starts from
|
||||
-- lower left conner, i.e. y is upside down in kpv's
|
||||
-- coordinate. So y0 should be taken with special care.
|
||||
----------------------------------------------------
|
||||
function DJVUReader:zoomedRectCoordTransform(x0, y0, x1, y1)
|
||||
local x,y = self:screenOffset()
|
||||
return
|
||||
x0 * self.globalzoom + x,
|
||||
self.cur_full_height - (y1 * self.globalzoom) + y,
|
||||
(x1 - x0) * self.globalzoom,
|
||||
(y1 - y0) * self.globalzoom
|
||||
end
|
||||
|
||||
-- y axel in djvulibre starts from bottom
|
||||
function DJVUReader:_isEntireWordInScreenHeightRange(w)
|
||||
return (w ~= nil) and
|
||||
(self.cur_full_height - (w.y1 * self.globalzoom) >=
|
||||
-self.offset_y) and
|
||||
(self.cur_full_height - (w.y0 * self.globalzoom) <=
|
||||
-self.offset_y + G_height)
|
||||
end
|
||||
|
||||
-- y axel in djvulibre starts from bottom
|
||||
function DJVUReader:_isEntireLineInScreenHeightRange(l)
|
||||
return (l ~= nil) and
|
||||
(self.cur_full_height - (l.y1 * self.globalzoom) >=
|
||||
-self.offset_y) and
|
||||
(self.cur_full_height - (l.y0 * self.globalzoom) <=
|
||||
-self.offset_y + G_height)
|
||||
end
|
||||
|
||||
-- y axel in djvulibre starts from bottom
|
||||
function DJVUReader:_isWordInScreenRange(w)
|
||||
if not w then
|
||||
return false
|
||||
end
|
||||
|
||||
is_entire_word_out_of_screen_height =
|
||||
(self.cur_full_height - (w.y0 * self.globalzoom) <=
|
||||
-self.offset_y)
|
||||
or (self.cur_full_height - (w.y1 * self.globalzoom) >=
|
||||
-self.offset_y + G_height)
|
||||
|
||||
is_entire_word_out_of_screen_width =
|
||||
(w.x0 * self.globalzoom >= -self.offset_x + G_width
|
||||
or w.x1 * self.globalzoom <= -self.offset_x)
|
||||
|
||||
return (not is_entire_word_out_of_screen_height) and
|
||||
(not is_entire_word_out_of_screen_width)
|
||||
end
|
||||
|
||||
|
||||
|
||||
10
einkfb.c
10
einkfb.c
@@ -150,6 +150,16 @@ static int einkUpdate(lua_State *L) {
|
||||
ioctl(fb->fd, FBIO_EINK_UPDATE_DISPLAY_AREA, &myarea);
|
||||
#else
|
||||
// for now, we only do fullscreen blits in emulation mode
|
||||
if (fxtype == 0) {
|
||||
// simmulate a full screen update in eink screen
|
||||
if(SDL_MUSTLOCK(fb->screen) && (SDL_LockSurface(fb->screen) < 0)) {
|
||||
return luaL_error(L, "can't lock surface.");
|
||||
}
|
||||
SDL_FillRect(fb->screen, NULL, 0x000000);
|
||||
if(SDL_MUSTLOCK(fb->screen)) SDL_UnlockSurface(fb->screen);
|
||||
SDL_Flip(fb->screen);
|
||||
}
|
||||
|
||||
if(SDL_MUSTLOCK(fb->screen) && (SDL_LockSurface(fb->screen) < 0)) {
|
||||
return luaL_error(L, "can't lock surface.");
|
||||
}
|
||||
|
||||
@@ -54,7 +54,11 @@ function FileChooser:readDir()
|
||||
table.insert(self.dirs, f)
|
||||
else
|
||||
local file_type = string.lower(string.match(f, ".+%.([^.]+)") or "")
|
||||
if file_type == "djvu" or file_type == "pdf" or file_type == "xps" or file_type == "cbz" then
|
||||
if file_type == "djvu"
|
||||
or file_type == "pdf" or file_type == "xps" or file_type == "cbz"
|
||||
or file_type == "epub" or file_type == "txt" or file_type == "rtf"
|
||||
or file_type == "htm" or file_type == "html"
|
||||
or file_type == "fb2" or file_type == "chm" then
|
||||
table.insert(self.files, f)
|
||||
end
|
||||
end
|
||||
@@ -118,8 +122,8 @@ function FileChooser:choose(ypos, height)
|
||||
end
|
||||
|
||||
while true do
|
||||
local cface, cfhash= Font:getFaceAndHash(25)
|
||||
local fface, ffhash = Font:getFaceAndHash(16, Font.ffont)
|
||||
local cface = Font:getFace("cfont", 25)
|
||||
local fface = Font:getFace("ffont", 16)
|
||||
|
||||
if pagedirty then
|
||||
fb.bb:paintRect(0, ypos, fb.bb:getWidth(), height, 0)
|
||||
@@ -128,17 +132,17 @@ function FileChooser:choose(ypos, height)
|
||||
local i = (self.page - 1) * perpage + c
|
||||
if i <= #self.dirs then
|
||||
-- resembles display in midnight commander: adds "/" prefix for directories
|
||||
renderUtf8Text(fb.bb, 39, ypos + self.spacing*c, cface, cfhash, "/", true)
|
||||
renderUtf8Text(fb.bb, 50, ypos + self.spacing*c, cface, cfhash, self.dirs[i], true)
|
||||
renderUtf8Text(fb.bb, 39, ypos + self.spacing*c, cface, "/", true)
|
||||
renderUtf8Text(fb.bb, 50, ypos + self.spacing*c, cface, self.dirs[i], true)
|
||||
elseif i <= self.items then
|
||||
renderUtf8Text(fb.bb, 50, ypos + self.spacing*c, cface, cfhash, self.files[i-#self.dirs], true)
|
||||
renderUtf8Text(fb.bb, 50, ypos + self.spacing*c, cface, self.files[i-#self.dirs], true)
|
||||
end
|
||||
end
|
||||
renderUtf8Text(fb.bb, 5, ypos + self.spacing * perpage + 42, fface, ffhash,
|
||||
renderUtf8Text(fb.bb, 5, ypos + self.spacing * perpage + 42, fface,
|
||||
"Page "..self.page.." of "..(math.floor(self.items / perpage)+1), true)
|
||||
local msg = self.exception_message and self.exception_message:match("[^%:]+:%d+: (.*)") or "Path: "..self.path
|
||||
self.exception_message = nil
|
||||
renderUtf8Text(fb.bb, 5, ypos + self.spacing * (perpage+1) + 27, fface, ffhash, msg, true)
|
||||
renderUtf8Text(fb.bb, 5, ypos + self.spacing * (perpage+1) + 27, fface, msg, true)
|
||||
markerdirty = true
|
||||
end
|
||||
if markerdirty then
|
||||
@@ -160,7 +164,7 @@ function FileChooser:choose(ypos, height)
|
||||
pagedirty = false
|
||||
end
|
||||
|
||||
local ev = input.waitForEvent()
|
||||
local ev = input.saveWaitForEvent()
|
||||
--print("key code:"..ev.code)
|
||||
ev.code = adjustKeyEvents(ev)
|
||||
if ev.type == EV_KEY and ev.value == EVENT_VALUE_KEY_PRESS then
|
||||
@@ -169,13 +173,13 @@ function FileChooser:choose(ypos, height)
|
||||
elseif ev.code == KEY_FW_DOWN then
|
||||
nextItem()
|
||||
elseif ev.code == KEY_F then -- invoke fontchooser menu
|
||||
fonts_menu = SelectMenu:new{
|
||||
local fonts_menu = SelectMenu:new{
|
||||
menu_title = "Fonts Menu",
|
||||
item_array = Font.fonts,
|
||||
item_array = Font:getFontList(),
|
||||
}
|
||||
local re = fonts_menu:choose(0, height)
|
||||
local re, font = fonts_menu:choose(0, height)
|
||||
if re then
|
||||
Font.cfont = Font.fonts[re]
|
||||
Font.fontmap["cfont"] = font
|
||||
Font:update()
|
||||
end
|
||||
pagedirty = true
|
||||
@@ -193,11 +197,11 @@ function FileChooser:choose(ypos, height)
|
||||
--]]
|
||||
return nil, function()
|
||||
FileSearcher:init( self.path )
|
||||
FileSearcher:choose(ypos, height, keywords)
|
||||
FileSearcher:choose(keywords)
|
||||
end
|
||||
end
|
||||
pagedirty = true
|
||||
elseif ev.code == KEY_PGFWD then
|
||||
elseif ev.code == KEY_PGFWD or ev.code == KEY_LPGFWD then
|
||||
if self.page < (self.items / perpage) then
|
||||
if self.current + self.page*perpage > self.items then
|
||||
self.current = self.items - self.page*perpage
|
||||
@@ -208,7 +212,7 @@ function FileChooser:choose(ypos, height)
|
||||
self.current = self.items - (self.page-1)*perpage
|
||||
markerdirty = true
|
||||
end
|
||||
elseif ev.code == KEY_PGBCK then
|
||||
elseif ev.code == KEY_PGBCK or ev.code == KEY_LPGBCK then
|
||||
if self.page > 1 then
|
||||
self.page = self.page - 1
|
||||
pagedirty = true
|
||||
|
||||
311
filesearcher.lua
311
filesearcher.lua
@@ -30,10 +30,16 @@ function FileSearcher:readDir()
|
||||
for __, d in pairs(self.dirs) do
|
||||
-- handle files in d
|
||||
for f in lfs.dir(d) do
|
||||
local file_type = string.lower(string.match(f, ".+%.([^.]+)") or "")
|
||||
if lfs.attributes(d.."/"..f, "mode") == "directory"
|
||||
and f ~= "." and f~= ".." and not string.match(f, "^%.[^.]") then
|
||||
table.insert(new_dirs, d.."/"..f)
|
||||
elseif string.match(f, ".+%.[pP][dD][fF]$") or string.match(f, ".+%.[dD][jJ][vV][uU]$") then
|
||||
elseif file_type == "djvu" or file_type == "pdf"
|
||||
or file_type == "xps" or file_type == "cbz"
|
||||
or file_type == "epub" or file_type == "txt"
|
||||
or file_type == "rtf" or file_type == "htm"
|
||||
or file_type == "html"
|
||||
or file_type == "fb2" or file_type == "chm" then
|
||||
file_entry = {dir=d, name=f,}
|
||||
table.insert(self.files, file_entry)
|
||||
--print("file:"..d.."/"..f)
|
||||
@@ -68,6 +74,7 @@ function FileSearcher:setSearchResult(keywords)
|
||||
end
|
||||
end
|
||||
end
|
||||
self.keywords = keywords
|
||||
self.items = #self.result
|
||||
self.page = 1
|
||||
self.current = 1
|
||||
@@ -79,42 +86,138 @@ function FileSearcher:init(search_path)
|
||||
else
|
||||
self:setPath("/mnt/us/documents")
|
||||
end
|
||||
self:addAllCommands()
|
||||
end
|
||||
|
||||
function FileSearcher:prevItem()
|
||||
if self.current == 1 then
|
||||
if self.page > 1 then
|
||||
self.current = self.perpage
|
||||
self.page = self.page - 1
|
||||
self.pagedirty = true
|
||||
end
|
||||
else
|
||||
self.current = self.current - 1
|
||||
self.markerdirty = true
|
||||
end
|
||||
end
|
||||
|
||||
function FileSearcher:choose(ypos, height, keywords)
|
||||
local perpage = math.floor(height / self.spacing) - 2
|
||||
local pagedirty = true
|
||||
local markerdirty = false
|
||||
|
||||
local prevItem = function ()
|
||||
if self.current == 1 then
|
||||
if self.page > 1 then
|
||||
self.current = perpage
|
||||
self.page = self.page - 1
|
||||
pagedirty = true
|
||||
end
|
||||
else
|
||||
self.current = self.current - 1
|
||||
markerdirty = true
|
||||
function FileSearcher:nextItem()
|
||||
if self.current == self.perpage then
|
||||
if self.page < (self.items / self.perpage) then
|
||||
self.current = 1
|
||||
self.page = self.page + 1
|
||||
self.pagedirty = true
|
||||
end
|
||||
else
|
||||
if self.page ~= math.floor(self.items / self.perpage) + 1
|
||||
or self.current + (self.page-1)*self.perpage < self.items then
|
||||
self.current = self.current + 1
|
||||
self.markerdirty = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local nextItem = function ()
|
||||
if self.current == perpage then
|
||||
if self.page < (self.items / perpage) then
|
||||
self.current = 1
|
||||
function FileSearcher:addAllCommands()
|
||||
self.commands = Commands:new{}
|
||||
|
||||
self.commands:add(KEY_FW_UP, nil, "joypad up",
|
||||
"goto previous item",
|
||||
function(self)
|
||||
self:prevItem()
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_FW_DOWN, nil, "joypad down",
|
||||
"goto next item",
|
||||
function(self)
|
||||
self:nextItem()
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_PGFWD, 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
|
||||
pagedirty = true
|
||||
end
|
||||
else
|
||||
if self.page ~= math.floor(self.items / perpage) + 1
|
||||
or self.current + (self.page-1)*perpage < self.items then
|
||||
self.current = self.current + 1
|
||||
markerdirty = true
|
||||
self.pagedirty = true
|
||||
else
|
||||
self.current = self.items - (self.page-1)*self.perpage
|
||||
self.markerdirty = true
|
||||
end
|
||||
end
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_PGBCK, 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_S, nil, "S",
|
||||
"invoke search inputbox",
|
||||
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_F, nil, "F",
|
||||
"font menu",
|
||||
function(self)
|
||||
local fonts_menu = SelectMenu:new{
|
||||
menu_title = "Fonts Menu",
|
||||
item_array = Font:getFontList(),
|
||||
}
|
||||
local re, font = fonts_menu:choose(0, G_height)
|
||||
if re then
|
||||
Font.fontmap["cfont"] = font
|
||||
Font:update()
|
||||
end
|
||||
self.pagedirty = true
|
||||
end
|
||||
)
|
||||
self.commands:add({KEY_ENTER, KEY_FW_PRESS}, nil, "",
|
||||
"select item",
|
||||
function(self)
|
||||
file_entry = self.result[self.perpage*(self.page-1)+self.current]
|
||||
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_BACK, KEY_HOME}, nil, "",
|
||||
"back to file browser",
|
||||
function(self)
|
||||
return "break"
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
function FileSearcher:choose(keywords)
|
||||
self.perpage = math.floor(G_height / self.spacing) - 2
|
||||
self.pagedirty = true
|
||||
self.markerdirty = false
|
||||
|
||||
|
||||
-- if given keywords, set new result according to keywords.
|
||||
-- Otherwise, display the previous search result.
|
||||
@@ -123,136 +226,92 @@ function FileSearcher:choose(ypos, height, keywords)
|
||||
end
|
||||
|
||||
while true do
|
||||
local cface, cfhash = Font:getFaceAndHash(22)
|
||||
local tface, tfhash = Font:getFaceAndHash(25, Font.tfont)
|
||||
local fface, ffhash = Font:getFaceAndHash(16, Font.ffont)
|
||||
local cface = Font:getFace("cfont", 22)
|
||||
local tface = Font:getFace("tfont", 25)
|
||||
local fface = Font:getFace("ffont", 16)
|
||||
|
||||
if pagedirty then
|
||||
markerdirty = true
|
||||
fb.bb:paintRect(0, ypos, fb.bb:getWidth(), height, 0)
|
||||
if self.pagedirty then
|
||||
self.markerdirty = true
|
||||
fb.bb:paintRect(0, 0, G_width, G_height, 0)
|
||||
|
||||
-- draw menu title
|
||||
renderUtf8Text(fb.bb, 30, ypos + self.title_H, tface, tfhash,
|
||||
"Search Result for: "..keywords, true)
|
||||
renderUtf8Text(fb.bb, 30, 0 + self.title_H, tface,
|
||||
"Search Result for: "..self.keywords, true)
|
||||
|
||||
-- draw results
|
||||
local c
|
||||
if self.items == 0 then -- nothing found
|
||||
y = ypos + self.title_H + self.spacing * 2
|
||||
renderUtf8Text(fb.bb, 20, y, cface, cfhash,
|
||||
y = self.title_H + self.spacing * 2
|
||||
renderUtf8Text(fb.bb, 20, y, cface,
|
||||
"Sorry, no match found.", true)
|
||||
renderUtf8Text(fb.bb, 20, y + self.spacing, cface, cfhash,
|
||||
renderUtf8Text(fb.bb, 20, y + self.spacing, cface,
|
||||
"Please try a different keyword.", true)
|
||||
markerdirty = false
|
||||
self.markerdirty = false
|
||||
else -- found something, draw it
|
||||
for c = 1, perpage do
|
||||
local i = (self.page - 1) * perpage + c
|
||||
for c = 1, self.perpage do
|
||||
local i = (self.page - 1) * self.perpage + c
|
||||
if i <= self.items then
|
||||
y = ypos + self.title_H + (self.spacing * c)
|
||||
renderUtf8Text(fb.bb, 50, y, cface, cfhash,
|
||||
y = self.title_H + (self.spacing * c)
|
||||
renderUtf8Text(fb.bb, 50, y, cface,
|
||||
self.result[i].name, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- draw footer
|
||||
y = ypos + self.title_H + (self.spacing * perpage) + self.foot_H
|
||||
x = (fb.bb:getWidth() / 2) - 50
|
||||
all_page = (math.floor(self.items / perpage)+1)
|
||||
renderUtf8Text(fb.bb, x, y, fface, ffhash,
|
||||
y = self.title_H + (self.spacing * self.perpage) + self.foot_H
|
||||
x = (G_width / 2) - 50
|
||||
all_page = (math.floor(self.items / self.perpage)+1)
|
||||
renderUtf8Text(fb.bb, x, y, fface,
|
||||
"Page "..self.page.." of "..all_page, true)
|
||||
end
|
||||
|
||||
if markerdirty then
|
||||
if not pagedirty then
|
||||
if self.markerdirty then
|
||||
if not self.pagedirty then
|
||||
if self.oldcurrent > 0 then
|
||||
y = ypos + self.title_H + (self.spacing * self.oldcurrent) + 10
|
||||
fb.bb:paintRect(30, y, fb.bb:getWidth() - 60, 3, 0)
|
||||
fb:refresh(1, 30, y, fb.bb:getWidth() - 60, 3)
|
||||
y = self.title_H + (self.spacing * self.oldcurrent) + 10
|
||||
fb.bb:paintRect(30, y, G_width - 60, 3, 0)
|
||||
fb:refresh(1, 30, y, G_width - 60, 3)
|
||||
end
|
||||
end
|
||||
-- draw new marker line
|
||||
y = ypos + self.title_H + (self.spacing * self.current) + 10
|
||||
fb.bb:paintRect(30, y, fb.bb:getWidth() - 60, 3, 15)
|
||||
if not pagedirty then
|
||||
fb:refresh(1, 30, y, fb.bb:getWidth() - 60, 3)
|
||||
y = self.title_H + (self.spacing * self.current) + 10
|
||||
fb.bb:paintRect(30, y, G_width - 60, 3, 15)
|
||||
if not self.pagedirty then
|
||||
fb:refresh(1, 30, y, G_width - 60, 3)
|
||||
end
|
||||
self.oldcurrent = self.current
|
||||
markerdirty = false
|
||||
self.markerdirty = false
|
||||
end
|
||||
|
||||
if pagedirty then
|
||||
fb:refresh(0, 0, ypos, fb.bb:getWidth(), height)
|
||||
pagedirty = false
|
||||
if self.pagedirty then
|
||||
fb:refresh(0)
|
||||
self.pagedirty = false
|
||||
end
|
||||
|
||||
local ev = input.waitForEvent()
|
||||
local ev = input.saveWaitForEvent()
|
||||
ev.code = adjustKeyEvents(ev)
|
||||
if ev.type == EV_KEY and ev.value == EVENT_VALUE_KEY_PRESS then
|
||||
if ev.code == KEY_FW_UP then
|
||||
prevItem()
|
||||
elseif ev.code == KEY_FW_DOWN then
|
||||
nextItem()
|
||||
elseif ev.code == KEY_PGFWD then
|
||||
if self.page < (self.items / perpage) then
|
||||
if self.current + self.page*perpage > self.items then
|
||||
self.current = self.items - self.page*perpage
|
||||
end
|
||||
self.page = self.page + 1
|
||||
pagedirty = true
|
||||
else
|
||||
self.current = self.items - (self.page-1)*perpage
|
||||
markerdirty = true
|
||||
end
|
||||
elseif ev.code == KEY_PGBCK then
|
||||
if self.page > 1 then
|
||||
self.page = self.page - 1
|
||||
pagedirty = true
|
||||
else
|
||||
self.current = 1
|
||||
markerdirty = true
|
||||
end
|
||||
elseif ev.code == KEY_S then
|
||||
old_keywords = keywords
|
||||
keywords = InputBox:input(height-100, 100, "Search:", old_keywords)
|
||||
if keywords then
|
||||
self:setSearchResult(keywords)
|
||||
else
|
||||
keywords = old_keywords
|
||||
end
|
||||
pagedirty = true
|
||||
elseif ev.code == KEY_F then -- invoke fontchooser menu
|
||||
fonts_menu = SelectMenu:new{
|
||||
menu_title = "Fonts Menu",
|
||||
item_array = Font.fonts,
|
||||
}
|
||||
local re = fonts_menu:choose(0, height)
|
||||
if re then
|
||||
Font.cfont = Font.fonts[re]
|
||||
Font:update()
|
||||
end
|
||||
pagedirty = true
|
||||
elseif ev.code == KEY_ENTER or ev.code == KEY_FW_PRESS then
|
||||
file_entry = self.result[perpage*(self.page-1)+self.current]
|
||||
file_full_path = file_entry.dir .. "/" .. file_entry.name
|
||||
keydef = Keydef:new(ev.code, getKeyModifier())
|
||||
print("key pressed: "..tostring(keydef))
|
||||
|
||||
-- rotation mode might be changed while reading, so
|
||||
-- record height_percent here
|
||||
local height_percent = height/fb.bb:getHeight()
|
||||
openFile(file_full_path)
|
||||
|
||||
--reset height and item index if screen has been rotated
|
||||
local old_perpage = perpage
|
||||
height = math.floor(fb.bb:getHeight()*height_percent)
|
||||
perpage = math.floor(height / self.spacing) - 2
|
||||
self.current = (old_perpage * (self.page - 1) +
|
||||
self.current) % perpage
|
||||
self.page = math.floor(self.items / perpage) + 1
|
||||
|
||||
pagedirty = true
|
||||
elseif ev.code == KEY_BACK or ev.code == KEY_HOME then
|
||||
return nil
|
||||
command = self.commands:getByKeydef(keydef)
|
||||
if command ~= nil then
|
||||
print("command to execute: "..tostring(command))
|
||||
ret_code = command.func(self, keydef)
|
||||
else
|
||||
print("command not found: "..tostring(command))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if ret_code == "break" then
|
||||
break
|
||||
end
|
||||
|
||||
if self.selected_item ~= nil then
|
||||
print("# selected "..self.selected_item)
|
||||
return self.selected_item
|
||||
end
|
||||
end -- if
|
||||
end -- while true
|
||||
return nil
|
||||
end
|
||||
|
||||
84
font.lua
84
font.lua
@@ -1,23 +1,43 @@
|
||||
|
||||
Font = {
|
||||
-- default font for menu contents
|
||||
cfont = "sans",
|
||||
-- default font for title
|
||||
tfont = "Helvetica-BoldOblique",
|
||||
-- default font for footer
|
||||
ffont = "sans",
|
||||
fontmap = {
|
||||
-- default font for menu contents
|
||||
cfont = "droid/DroidSans.ttf",
|
||||
-- default font for title
|
||||
tfont = "NimbusSanL-BoldItal.cff",
|
||||
-- default font for footer
|
||||
ffont = "droid/DroidSans.ttf",
|
||||
|
||||
-- built in fonts
|
||||
fonts = {"sans", "cjk", "mono",
|
||||
"Courier", "Courier-Bold", "Courier-Oblique", "Courier-BoldOblique",
|
||||
"Helvetica", "Helvetica-Oblique", "Helvetica-BoldOblique",
|
||||
"Times-Roman", "Times-Bold", "Times-Italic", "Times-BoldItalic",},
|
||||
-- default font for reading position info
|
||||
rifont = "droid/DroidSans.ttf",
|
||||
|
||||
-- default font for pagination display
|
||||
pgfont = "droid/DroidSans.ttf",
|
||||
|
||||
-- selectmenu: font for item shortcut
|
||||
scfont = "droid/DroidSansMono.ttf",
|
||||
|
||||
-- help page: font for displaying keys
|
||||
hpkfont = "droid/DroidSansMono.ttf",
|
||||
-- font for displaying help messages
|
||||
hfont = "droid/DroidSans.ttf",
|
||||
|
||||
-- font for displaying input content
|
||||
-- we have to use mono here for better distance controlling
|
||||
infont = "droid/DroidSansMono.ttf",
|
||||
|
||||
-- font for info messages
|
||||
infofont = "droid/DroidSans.ttf",
|
||||
},
|
||||
|
||||
fontdir = os.getenv("FONTDIR") or "./fonts",
|
||||
|
||||
-- face table
|
||||
faces = {},
|
||||
}
|
||||
|
||||
function Font:getFaceAndHash(size, font)
|
||||
|
||||
function Font:getFace(font, size)
|
||||
if not font then
|
||||
-- default to content font
|
||||
font = self.cfont
|
||||
@@ -26,21 +46,45 @@ function Font:getFaceAndHash(size, font)
|
||||
local face = self.faces[font..size]
|
||||
-- build face if not found
|
||||
if not face then
|
||||
for _k,_v in ipairs(self.fonts) do
|
||||
if font == _v then
|
||||
face = freetype.newBuiltinFace(font, size)
|
||||
self.faces[font..size] = face
|
||||
end
|
||||
local realname = self.fontmap[font]
|
||||
if not realname then
|
||||
realname = font
|
||||
end
|
||||
if not face then
|
||||
print("#! Font "..font.." not supported!!")
|
||||
realname = self.fontdir.."/"..realname
|
||||
ok, face = pcall(freetype.newFace, realname, size)
|
||||
if not ok then
|
||||
print("#! Font "..font.." ("..realname..") not supported: "..face)
|
||||
return nil
|
||||
end
|
||||
self.faces[font..size] = face
|
||||
end
|
||||
return face, font..size
|
||||
return { size = size, ftface = face, hash = font..size }
|
||||
end
|
||||
|
||||
function Font:_readList(target, dir, effective_dir)
|
||||
for f in lfs.dir(dir) do
|
||||
if lfs.attributes(dir.."/"..f, "mode") == "directory" and f ~= "." and f ~= ".." then
|
||||
self:_readList(target, dir.."/"..f, effective_dir..f.."/")
|
||||
else
|
||||
local file_type = string.lower(string.match(f, ".+%.([^.]+)") or "")
|
||||
if file_type == "ttf" or file_type == "cff" or file_type == "otf" then
|
||||
table.insert(target, effective_dir..f)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Font:getFontList()
|
||||
fontlist = {}
|
||||
self:_readList(fontlist, self.fontdir, "")
|
||||
table.sort(fontlist)
|
||||
return fontlist
|
||||
end
|
||||
|
||||
function Font:update()
|
||||
for _k, _v in ipairs(self.faces) do
|
||||
_v:done()
|
||||
end
|
||||
self.faces = {}
|
||||
clearGlyphCache()
|
||||
end
|
||||
|
||||
44
ft.c
44
ft.c
@@ -55,49 +55,6 @@ static int newFace(lua_State *L) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int newBuiltinFace(lua_State *L) {
|
||||
const char *fontname = luaL_checkstring(L, 1);
|
||||
int pxsize = luaL_optint(L, 2, 16*64);
|
||||
char *fontdata = NULL;
|
||||
|
||||
unsigned int size;
|
||||
/* we use compiled-in font data from mupdf build */
|
||||
if(!strcmp("mono", fontname)) {
|
||||
fontdata = pdf_lookup_substitute_font(1, 0, 0, 0, &size);
|
||||
} else if(!strcmp("sans", fontname)) {
|
||||
fontdata = pdf_lookup_substitute_font(0, 0, 0, 0, &size);
|
||||
} else if(!strcmp("cjk", fontname)) {
|
||||
fontdata = pdf_lookup_substitute_cjk_font(0, 0, &size);
|
||||
} else {
|
||||
fontdata = pdf_lookup_builtin_font(fontname, &size);
|
||||
}
|
||||
if(fontdata == NULL) {
|
||||
return luaL_error(L, "no such built-in font");
|
||||
}
|
||||
|
||||
FT_Face *face = (FT_Face*) lua_newuserdata(L, sizeof(FT_Face));
|
||||
luaL_getmetatable(L, "ft_face");
|
||||
lua_setmetatable(L, -2);
|
||||
|
||||
FT_Error error = FT_New_Memory_Face(freetypelib, (FT_Byte*)fontdata, size, 0, face);
|
||||
if(error) {
|
||||
return luaL_error(L, "freetype error");
|
||||
}
|
||||
|
||||
error = FT_Set_Pixel_Sizes(*face, 0, pxsize);
|
||||
if(error) {
|
||||
error = FT_Done_Face(*face);
|
||||
return luaL_error(L, "freetype error");
|
||||
}
|
||||
|
||||
if((*face)->charmap == NULL) {
|
||||
//TODO
|
||||
//fprintf(stderr, "no unicode charmap found, to be implemented.\n");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int renderGlyph(lua_State *L) {
|
||||
FT_Face *face = (FT_Face*) luaL_checkudata(L, 1, "ft_face");
|
||||
int ch = luaL_checkint(L, 2);
|
||||
@@ -215,7 +172,6 @@ static const struct luaL_Reg ft_face_meth[] = {
|
||||
|
||||
static const struct luaL_Reg ft_func[] = {
|
||||
{"newFace", newFace},
|
||||
{"newBuiltinFace", newBuiltinFace},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
106
graphics.lua
106
graphics.lua
@@ -6,6 +6,7 @@ blitbuffer.paintBorder = function (bb, x, y, w, h, bw, c)
|
||||
bb:paintRect(x+w-bw, y+bw, bw, h - 2*bw, c)
|
||||
end
|
||||
|
||||
|
||||
--[[
|
||||
Draw a progress bar according to following args:
|
||||
|
||||
@@ -27,3 +28,108 @@ blitbuffer.progressBar = function (bb, x, y, w, h,
|
||||
fb.bb:paintRect(x+load_m_w, y+load_m_h,
|
||||
(w-2*load_m_w)*load_percent, (h-2*load_m_h), c)
|
||||
end
|
||||
|
||||
|
||||
|
||||
------------------------------------------------
|
||||
-- Start of Cursor class
|
||||
------------------------------------------------
|
||||
|
||||
Cursor = {
|
||||
x_pos = 0,
|
||||
y_pos = 0,
|
||||
--color = 15,
|
||||
h = 10,
|
||||
w = nil,
|
||||
line_w = nil,
|
||||
is_cleared = true,
|
||||
}
|
||||
|
||||
function Cursor:new(o)
|
||||
o = o or {}
|
||||
o.x_pos = o.x_pos or self.x_pos
|
||||
o.y_pos = o.y_pos or self.y_pos
|
||||
o.line_width_factor = o.line_width_factor or 10
|
||||
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
|
||||
o:setHeight(o.h or self.h)
|
||||
return o
|
||||
end
|
||||
|
||||
function Cursor:setHeight(h)
|
||||
self.h = h
|
||||
self.w = self.h / 3
|
||||
self.line_w = math.floor(self.h / self.line_width_factor)
|
||||
end
|
||||
|
||||
function Cursor:_draw(x, y)
|
||||
local up_down_width = math.floor(self.line_w / 2)
|
||||
local body_h = self.h - (up_down_width * 2)
|
||||
-- paint upper horizontal line
|
||||
fb.bb:invertRect(x, y, self.w, up_down_width)
|
||||
-- paint middle vertical line
|
||||
fb.bb:invertRect(x + (self.w / 2) - up_down_width, y + up_down_width,
|
||||
self.line_w, body_h)
|
||||
-- paint lower horizontal line
|
||||
fb.bb:invertRect(x, y + body_h + up_down_width, self.w, up_down_width)
|
||||
end
|
||||
|
||||
function Cursor:draw()
|
||||
if self.is_cleared then
|
||||
self.is_cleared = false
|
||||
self:_draw(self.x_pos, self.y_pos)
|
||||
end
|
||||
end
|
||||
|
||||
function Cursor:clear()
|
||||
if not self.is_cleared then
|
||||
self.is_cleared = true
|
||||
self:_draw(self.x_pos, self.y_pos)
|
||||
end
|
||||
end
|
||||
|
||||
function Cursor:move(x_off, y_off)
|
||||
self.x_pos = self.x_pos + x_off
|
||||
self.y_pos = self.y_pos + y_off
|
||||
end
|
||||
|
||||
function Cursor:moveHorizontal(x_off)
|
||||
self.x_pos = self.x_pos + x_off
|
||||
end
|
||||
|
||||
function Cursor:moveVertical(x_off)
|
||||
self.y_pos = self.y_pos + y_off
|
||||
end
|
||||
|
||||
function Cursor:moveAndDraw(x_off, y_off)
|
||||
self:clear()
|
||||
self:move(x_off, y_off)
|
||||
self:draw()
|
||||
end
|
||||
|
||||
function Cursor:moveTo(x_pos, y_pos)
|
||||
self.x_pos = x_pos
|
||||
self.y_pos = y_pos
|
||||
end
|
||||
|
||||
function Cursor:moveToAndDraw(x_pos, y_pos)
|
||||
self:clear()
|
||||
self.x_pos = x_pos
|
||||
self.y_pos = y_pos
|
||||
self:draw()
|
||||
end
|
||||
|
||||
function Cursor:moveHorizontalAndDraw(x_off)
|
||||
self:clear()
|
||||
self:move(x_off, 0)
|
||||
self:draw()
|
||||
end
|
||||
|
||||
function Cursor:moveVerticalAndDraw(y_off)
|
||||
self:clear()
|
||||
self:move(0, y_off)
|
||||
self:draw()
|
||||
end
|
||||
|
||||
|
||||
63
helppage.lua
63
helppage.lua
@@ -7,22 +7,33 @@ require "selectmenu"
|
||||
require "commands"
|
||||
|
||||
HelpPage = {
|
||||
-- Other Class vars:
|
||||
|
||||
-- spacing between lines
|
||||
spacing = 25,
|
||||
|
||||
-- state buffer
|
||||
commands = nil,
|
||||
items = 0,
|
||||
page = 1
|
||||
page = 1,
|
||||
|
||||
-- font for displaying keys
|
||||
fsize = 20,
|
||||
face = Font:getFace("hpkfont", 20),
|
||||
|
||||
-- font for displaying help messages
|
||||
hfsize = 20,
|
||||
hface = Font:getFace("hfont", 20),
|
||||
|
||||
-- font for paging display
|
||||
ffsize = 15,
|
||||
fface = Font:getFace("pgfont", 15)
|
||||
}
|
||||
|
||||
-- Other Class vars:
|
||||
|
||||
-- font for displaying help messages
|
||||
HelpPage.sFace, HelpPage.sHash = Font:getFaceAndHash(20, "sans")
|
||||
-- font for displaying keys
|
||||
HelpPage.mFace, HelpPage.mHash = Font:getFaceAndHash(20, "sans")
|
||||
-- font for paging display
|
||||
HelpPage.fFace, HelpPage.fHash = Font:getFaceAndHash(15, "sans")
|
||||
|
||||
function HelpPage:show(ypos,height,commands)
|
||||
function HelpPage:show(ypos, height, commands)
|
||||
self.commands = {}
|
||||
self.items = 0
|
||||
local keys = {}
|
||||
@@ -37,17 +48,17 @@ function HelpPage:show(ypos,height,commands)
|
||||
end
|
||||
table.sort(self.commands,function(w1,w2) return w1.order<w2.order end)
|
||||
|
||||
local mFaceHeight, mFaceAscender = self.mFace:getHeightAndAscender();
|
||||
local fFaceHeight, fFaceAscender = self.fFace:getHeightAndAscender();
|
||||
--print(mFaceHeight.."-"..mFaceAscender)
|
||||
--print(fFaceHeight.."-"..fFaceAscender)
|
||||
mFaceHeight = math.ceil(mFaceHeight)
|
||||
mFaceAscender = math.ceil(mFaceAscender)
|
||||
fFaceHeight = math.ceil(fFaceHeight)
|
||||
fFaceAscender = math.ceil(fFaceAscender)
|
||||
local spacing = mFaceHeight + 5
|
||||
local faceHeight, faceAscender = self.face:getHeightAndAscender();
|
||||
local ffaceHeight, ffaceAscender = self.fface:getHeightAndAscender();
|
||||
--print(faceHeight.."-"..faceAscender)
|
||||
--print(ffaceHeight.."-"..ffaceAscender)
|
||||
faceHeight = math.ceil(faceHeight)
|
||||
faceAscender = math.ceil(faceAscender)
|
||||
ffaceHeight = math.ceil(ffaceHeight)
|
||||
ffaceAscender = math.ceil(ffaceAscender)
|
||||
local spacing = faceHeight + 5
|
||||
|
||||
local perpage = math.floor( (height - ypos - 1 * (fFaceHeight + 5)) / spacing )
|
||||
local perpage = math.floor( (height - ypos - 1 * (ffaceHeight + 5)) / spacing )
|
||||
local pagedirty = true
|
||||
|
||||
while true do
|
||||
@@ -62,19 +73,19 @@ function HelpPage:show(ypos,height,commands)
|
||||
local key = self.commands[i].shortcut
|
||||
for _k,aMod in pairs(MOD_TABLE) do
|
||||
local modStart, modEnd = key:find(aMod.v)
|
||||
print("key:"..key.." v:"..aMod.v.." d:"..aMod.d.." modstart:"..(modStart or "nil"))
|
||||
--print("key:"..key.." v:"..aMod.v.." d:"..aMod.d.." modstart:"..(modStart or "nil"))
|
||||
if(modStart ~= nil) then
|
||||
key = key:sub(1,modStart-1)..key:sub(modEnd+1)
|
||||
local box = sizeUtf8Text( x, fb.bb:getWidth(), self.mFace, self.mHash, aMod.d, true)
|
||||
local box = sizeUtf8Text( x, fb.bb:getWidth(), self.face, aMod.d, true)
|
||||
fb.bb:paintRect(x, ypos + spacing*c - box.y_top, box.x, box.y_top + box.y_bottom, 4);
|
||||
local pen_x = renderUtf8Text(fb.bb, x, ypos + spacing*c, self.mFace, self.mHash, aMod.d.." + ", true)
|
||||
local pen_x = renderUtf8Text(fb.bb, x, ypos + spacing*c, self.face, aMod.d.." + ", true)
|
||||
x = x + pen_x
|
||||
max_x = math.max(max_x, pen_x)
|
||||
end
|
||||
end
|
||||
local box = sizeUtf8Text( x, fb.bb:getWidth(), self.mFace, self.mHash, key , true)
|
||||
local box = sizeUtf8Text( x, fb.bb:getWidth(), self.face, key , true)
|
||||
fb.bb:paintRect(x, ypos + spacing*c - box.y_top, box.x, box.y_top + box.y_bottom, 4);
|
||||
local pen_x = renderUtf8Text(fb.bb, x, ypos + spacing*c, self.mFace, self.mHash, key, true)
|
||||
local pen_x = renderUtf8Text(fb.bb, x, ypos + spacing*c, self.face, self.mHash, key, true)
|
||||
x = x + pen_x
|
||||
max_x = math.max(max_x, x)
|
||||
end
|
||||
@@ -82,10 +93,10 @@ function HelpPage:show(ypos,height,commands)
|
||||
for c = 1, perpage do
|
||||
local i = (self.page - 1) * perpage + c
|
||||
if i <= self.items then
|
||||
renderUtf8Text(fb.bb, max_x + 20, ypos + spacing*c, self.sFace, self.sHash, self.commands[i].help, true)
|
||||
renderUtf8Text(fb.bb, max_x + 20, ypos + spacing*c, self.hface, self.commands[i].help, true)
|
||||
end
|
||||
end
|
||||
renderUtf8Text(fb.bb, 5, height - fFaceHeight + fFaceAscender - 5, self.fFace, self.fHash,
|
||||
renderUtf8Text(fb.bb, 5, height - ffaceHeight + ffaceAscender - 5, self.fFace,
|
||||
"Page "..self.page.." of "..math.ceil(self.items / perpage).." - click Back to close this page", true)
|
||||
markerdirty = true
|
||||
end
|
||||
@@ -94,7 +105,7 @@ function HelpPage:show(ypos,height,commands)
|
||||
pagedirty = false
|
||||
end
|
||||
|
||||
local ev = input.waitForEvent()
|
||||
local ev = input.saveWaitForEvent()
|
||||
--print("key code:"..ev.code)
|
||||
ev.code = adjustKeyEvents(ev)
|
||||
if ev.type == EV_KEY and ev.value == EVENT_VALUE_KEY_PRESS then
|
||||
|
||||
25
image.lua
Normal file
25
image.lua
Normal file
@@ -0,0 +1,25 @@
|
||||
Image = {}
|
||||
|
||||
function Image._getFileData(filename)
|
||||
local f = io.open(filename)
|
||||
local data = f:read("*a")
|
||||
f:close()
|
||||
return data
|
||||
end
|
||||
|
||||
function Image.fromPNG(filename)
|
||||
local img = mupdfimg.new()
|
||||
img:loadPNGData(Image._getFileData(filename))
|
||||
local bb = img:toBlitBuffer()
|
||||
img:free()
|
||||
return bb
|
||||
end
|
||||
|
||||
function Image.fromJPEG(filename)
|
||||
local img = mupdfimg.new()
|
||||
img:loadJPEGData(Image._getFileData(filename)(fimgdatailename))
|
||||
local bb = img:toBlitBuffer()
|
||||
img:free()
|
||||
return bb
|
||||
end
|
||||
|
||||
108
input.c
108
input.c
@@ -19,29 +19,100 @@
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <linux/input.h>
|
||||
#include "input.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <signal.h>
|
||||
|
||||
#define NUM_FDS 3
|
||||
int inputfds[3] = { -1, -1, -1 };
|
||||
#define OUTPUT_SIZE 21
|
||||
#define CODE_IN_SAVER 10000
|
||||
#define CODE_OUT_SAVER 10001
|
||||
|
||||
#define NUM_FDS 4
|
||||
int inputfds[4] = { -1, -1, -1, -1 };
|
||||
int slider_pid = -1;
|
||||
|
||||
int findFreeFdSlot() {
|
||||
int i;
|
||||
for(i=0; i<NUM_FDS; i++) {
|
||||
if(inputfds[i] == -1) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int openInputDevice(lua_State *L) {
|
||||
#ifndef EMULATE_READER
|
||||
int i;
|
||||
int fd;
|
||||
const char* inputdevice = luaL_checkstring(L, 1);
|
||||
|
||||
for(i=0; i<NUM_FDS; i++) {
|
||||
if(inputfds[i] == -1) {
|
||||
inputfds[i] = open(inputdevice, O_RDONLY | O_NONBLOCK, 0);
|
||||
if(inputfds[i] != -1) {
|
||||
ioctl(inputfds[i], EVIOCGRAB, 1);
|
||||
return 0;
|
||||
} else {
|
||||
return luaL_error(L, "error opening input device <%s>: %d", inputdevice, errno);
|
||||
fd = findFreeFdSlot();
|
||||
if(fd == -1) {
|
||||
return luaL_error(L, "no free slot for new input device <%s>", inputdevice);
|
||||
}
|
||||
|
||||
if(!strcmp("slider",inputdevice)) {
|
||||
/* special case: the power slider */
|
||||
int pipefd[2];
|
||||
int childpid;
|
||||
|
||||
pipe(pipefd);
|
||||
if((childpid = fork()) == -1) {
|
||||
return luaL_error(L, "cannot fork() slider event listener");
|
||||
}
|
||||
if(childpid == 0) {
|
||||
FILE *fp;
|
||||
char std_out[OUTPUT_SIZE] = "";
|
||||
struct input_event ev;
|
||||
int ret;
|
||||
__u16 key_code = 10000;
|
||||
|
||||
close(pipefd[0]);
|
||||
|
||||
ev.type = EV_KEY;
|
||||
ev.code = key_code;
|
||||
ev.value = 1;
|
||||
|
||||
/* listen power slider events */
|
||||
while(1) {
|
||||
fp = popen("exec lipc-wait-event com.lab126.powerd goingToScreenSaver,outOfScreenSaver", "r");
|
||||
if(fgets(std_out, OUTPUT_SIZE, fp) == NULL) {
|
||||
break;
|
||||
}
|
||||
pclose(fp);
|
||||
if(std_out[0] == 'g') {
|
||||
ev.code = CODE_IN_SAVER;
|
||||
} else if(std_out[0] == 'o') {
|
||||
ev.code = CODE_OUT_SAVER;
|
||||
} else {
|
||||
printf("Unrecognized event.\n");
|
||||
}
|
||||
/* fill event struct */
|
||||
gettimeofday(&ev.time, NULL);
|
||||
|
||||
/* generate event */
|
||||
if(write(pipefd[1], &ev, sizeof(struct input_event)) == -1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
exit(0); /* cannot be reached?! */
|
||||
} else {
|
||||
close(pipefd[1]);
|
||||
inputfds[fd] = pipefd[0];
|
||||
slider_pid = childpid;
|
||||
}
|
||||
} else {
|
||||
inputfds[fd] = open(inputdevice, O_RDONLY | O_NONBLOCK, 0);
|
||||
if(inputfds[fd] != -1) {
|
||||
ioctl(inputfds[fd], EVIOCGRAB, 1);
|
||||
return 0;
|
||||
} else {
|
||||
return luaL_error(L, "error opening input device <%s>: %d", inputdevice, errno);
|
||||
}
|
||||
}
|
||||
return luaL_error(L, "no free slot for new input device <%s>", inputdevice);
|
||||
#else
|
||||
if(SDL_Init(SDL_INIT_VIDEO) < 0) {
|
||||
return luaL_error(L, "cannot initialize SDL.");
|
||||
@@ -58,6 +129,11 @@ static int closeInputDevices(lua_State *L) {
|
||||
close(i);
|
||||
}
|
||||
}
|
||||
if(slider_pid != -1) {
|
||||
/* kill and wait for child process */
|
||||
kill(slider_pid, SIGTERM);
|
||||
waitpid(-1, NULL, 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -66,7 +142,7 @@ static int waitForInput(lua_State *L) {
|
||||
fd_set fds;
|
||||
struct timeval timeout;
|
||||
int i, num, nfds;
|
||||
int usecs = luaL_optint(L, 1, 0x7FFFFFFF);
|
||||
int usecs = luaL_optint(L, 1, -1); // we check for <0 later
|
||||
|
||||
timeout.tv_sec = (usecs/1000000);
|
||||
timeout.tv_usec = (usecs%1000000);
|
||||
@@ -81,7 +157,11 @@ static int waitForInput(lua_State *L) {
|
||||
nfds = inputfds[i] + 1;
|
||||
}
|
||||
|
||||
num = select(nfds, &fds, NULL, NULL, &timeout);
|
||||
/* when no value is given as argument, we pass
|
||||
* NULL to select() for the timeout value, setting no
|
||||
* timeout at all.
|
||||
*/
|
||||
num = select(nfds, &fds, NULL, NULL, (usecs < 0) ? NULL : &timeout);
|
||||
if(num < 0) {
|
||||
return luaL_error(L, "Waiting for input failed: %d\n", errno);
|
||||
}
|
||||
|
||||
452
inputbox.lua
452
inputbox.lua
@@ -1,9 +1,17 @@
|
||||
require "font"
|
||||
require "rendertext"
|
||||
require "keys"
|
||||
require "graphics"
|
||||
|
||||
|
||||
----------------------------------------------------
|
||||
-- General inputbox
|
||||
----------------------------------------------------
|
||||
|
||||
InputBox = {
|
||||
-- Class vars:
|
||||
h = 100,
|
||||
input_slot_w = nil,
|
||||
input_start_x = 145,
|
||||
input_start_y = nil,
|
||||
input_cur_x = nil, -- points to the start of next input pos
|
||||
@@ -15,44 +23,103 @@ InputBox = {
|
||||
shiftmode = false,
|
||||
altmode = false,
|
||||
|
||||
cursor = nil,
|
||||
|
||||
-- font for displaying input content
|
||||
face = freetype.newBuiltinFace("mono", 25),
|
||||
fhash = "m25",
|
||||
-- we have to use mono here for better distance controlling
|
||||
face = Font:getFace("infont", 25),
|
||||
fheight = 25,
|
||||
fwidth = 16,
|
||||
fwidth = 15,
|
||||
commands = nil,
|
||||
initialized = false,
|
||||
}
|
||||
|
||||
function InputBox:setDefaultInput(text)
|
||||
self.input_string = ""
|
||||
self:addString(text)
|
||||
--self.input_cur_x = self.input_start_x + (string.len(text) * self.fwidth)
|
||||
--self.input_string = text
|
||||
function InputBox:new(o)
|
||||
o = o or {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end
|
||||
|
||||
function InputBox:addString(str)
|
||||
for i = 1, #str do
|
||||
self:addChar(str:sub(i,i))
|
||||
function InputBox:init()
|
||||
if not self.initialized then
|
||||
self:addAllCommands()
|
||||
self.initialized = true
|
||||
end
|
||||
end
|
||||
|
||||
function InputBox:refreshText()
|
||||
-- clear previous painted text
|
||||
fb.bb:paintRect(140, self.input_start_y-19,
|
||||
self.input_slot_w, self.fheight, self.input_bg)
|
||||
-- paint new text
|
||||
renderUtf8Text(fb.bb, self.input_start_x, self.input_start_y,
|
||||
self.face,
|
||||
self.input_string, 0)
|
||||
end
|
||||
|
||||
function InputBox:addChar(char)
|
||||
renderUtf8Text(fb.bb, self.input_cur_x, self.input_start_y, self.face, self.fhash,
|
||||
char, true)
|
||||
fb:refresh(1, self.input_cur_x, self.input_start_y-19, self.fwidth, self.fheight)
|
||||
self.cursor:clear()
|
||||
|
||||
-- draw new text
|
||||
local cur_index = (self.cursor.x_pos + 3 - self.input_start_x)
|
||||
/ self.fwidth
|
||||
self.input_string = self.input_string:sub(1, cur_index)..char..
|
||||
self.input_string:sub(cur_index+1)
|
||||
self:refreshText()
|
||||
self.input_cur_x = self.input_cur_x + self.fwidth
|
||||
self.input_string = self.input_string .. char
|
||||
-- draw new cursor
|
||||
self.cursor:moveHorizontal(self.fwidth)
|
||||
self.cursor:draw()
|
||||
|
||||
fb:refresh(1, self.input_start_x-5, self.input_start_y-25,
|
||||
self.input_slot_w, self.h-25)
|
||||
end
|
||||
|
||||
function InputBox:delChar()
|
||||
if self.input_start_x == self.input_cur_x then
|
||||
return
|
||||
end
|
||||
|
||||
local cur_index = (self.cursor.x_pos + 3 - self.input_start_x)
|
||||
/ self.fwidth
|
||||
if cur_index == 0 then return end
|
||||
|
||||
self.cursor:clear()
|
||||
|
||||
-- draw new text
|
||||
self.input_string = self.input_string:sub(1, cur_index-1)..
|
||||
self.input_string:sub(cur_index+1, -1)
|
||||
self:refreshText()
|
||||
self.input_cur_x = self.input_cur_x - self.fwidth
|
||||
|
||||
--fill last character with blank rectangle
|
||||
fb.bb:paintRect(self.input_cur_x, self.input_start_y-19,
|
||||
self.fwidth, self.fheight, self.input_bg)
|
||||
fb:refresh(1, self.input_cur_x, self.input_start_y-19, self.fwidth, self.fheight)
|
||||
self.input_string = self.input_string:sub(0,-2)
|
||||
|
||||
-- draw new cursor
|
||||
self.cursor:moveHorizontal(-self.fwidth)
|
||||
self.cursor:draw()
|
||||
|
||||
fb:refresh(1, self.input_start_x-5, self.input_start_y-25,
|
||||
self.input_slot_w, self.h-25)
|
||||
end
|
||||
|
||||
function InputBox:clearText()
|
||||
self.cursor:clear()
|
||||
self.input_string = ""
|
||||
self:refreshText()
|
||||
self.cursor.x_pos = self.input_start_x - 3
|
||||
self.cursor:draw()
|
||||
|
||||
fb:refresh(1, self.input_start_x-5, self.input_start_y-25,
|
||||
self.input_slot_w, self.h-25)
|
||||
end
|
||||
|
||||
function InputBox:drawHelpMsg(ypos, w, h)
|
||||
return
|
||||
end
|
||||
|
||||
function InputBox:drawBox(ypos, w, h, title)
|
||||
@@ -61,139 +128,262 @@ function InputBox:drawBox(ypos, w, h, title)
|
||||
-- draw input slot
|
||||
fb.bb:paintRect(140, ypos + 10, w - 130, h - 20, self.input_bg)
|
||||
-- draw input title
|
||||
renderUtf8Text(fb.bb, 35, self.input_start_y, self.face, self.fhash,
|
||||
renderUtf8Text(fb.bb, 35, self.input_start_y, self.face,
|
||||
title, true)
|
||||
end
|
||||
|
||||
|
||||
--[[
|
||||
|| d_text default to nil (used to set default text in input slot)
|
||||
--]]
|
||||
----------------------------------------------------------------------
|
||||
-- InputBox:input()
|
||||
--
|
||||
-- @title: input prompt for the box
|
||||
-- @d_text: default to nil (used to set default text in input slot)
|
||||
----------------------------------------------------------------------
|
||||
function InputBox:input(ypos, height, title, d_text)
|
||||
local pagedirty = true
|
||||
self:init()
|
||||
-- do some initilization
|
||||
self.ypos = ypos
|
||||
self.h = height
|
||||
self.input_start_y = ypos + 35
|
||||
self.input_cur_x = self.input_start_x
|
||||
self.input_slot_w = fb.bb:getWidth() - 170
|
||||
|
||||
if d_text then -- if specified default text, draw it
|
||||
w = fb.bb:getWidth() - 40
|
||||
h = height - 45
|
||||
self:drawBox(ypos, w, h, title)
|
||||
self:setDefaultInput(d_text)
|
||||
fb:refresh(1, 20, ypos, w, h)
|
||||
pagedirty = false
|
||||
else -- otherwise, leave the draw task to the main loop
|
||||
self.input_string = ""
|
||||
self.cursor = Cursor:new {
|
||||
x_pos = self.input_start_x - 3,
|
||||
y_pos = ypos + 13,
|
||||
h = 30,
|
||||
}
|
||||
|
||||
|
||||
-- draw box and content
|
||||
w = fb.bb:getWidth() - 40
|
||||
h = height - 45
|
||||
self:drawHelpMsg(ypos, w, h)
|
||||
self:drawBox(ypos, w, h, title)
|
||||
if d_text then
|
||||
self.input_string = d_text
|
||||
self.input_cur_x = self.input_cur_x + (self.fwidth * d_text:len())
|
||||
self.cursor.x_pos = self.cursor.x_pos + (self.fwidth * d_text:len())
|
||||
self:refreshText()
|
||||
end
|
||||
self.cursor:draw()
|
||||
fb:refresh(1, 20, ypos, w, h)
|
||||
|
||||
while true do
|
||||
if pagedirty then
|
||||
w = fb.bb:getWidth() - 40
|
||||
h = height - 45
|
||||
self:drawBox(ypos, w, h, title)
|
||||
fb:refresh(1, 20, ypos, w, h)
|
||||
pagedirty = false
|
||||
end
|
||||
|
||||
local ev = input.waitForEvent()
|
||||
local ev = input.saveWaitForEvent()
|
||||
ev.code = adjustKeyEvents(ev)
|
||||
if ev.type == EV_KEY and ev.value == EVENT_VALUE_KEY_PRESS then
|
||||
--local secs, usecs = util.gettime()
|
||||
if ev.code == KEY_FW_UP then
|
||||
elseif ev.code == KEY_FW_DOWN then
|
||||
elseif ev.code == KEY_A then
|
||||
self:addChar("a")
|
||||
elseif ev.code == KEY_B then
|
||||
self:addChar("b")
|
||||
elseif ev.code == KEY_C then
|
||||
self:addChar("c")
|
||||
elseif ev.code == KEY_D then
|
||||
self:addChar("d")
|
||||
elseif ev.code == KEY_E then
|
||||
self:addChar("e")
|
||||
elseif ev.code == KEY_F then
|
||||
self:addChar("f")
|
||||
elseif ev.code == KEY_G then
|
||||
self:addChar("g")
|
||||
elseif ev.code == KEY_H then
|
||||
self:addChar("h")
|
||||
elseif ev.code == KEY_I then
|
||||
self:addChar("i")
|
||||
elseif ev.code == KEY_J then
|
||||
self:addChar("j")
|
||||
elseif ev.code == KEY_K then
|
||||
self:addChar("k")
|
||||
elseif ev.code == KEY_L then
|
||||
self:addChar("l")
|
||||
elseif ev.code == KEY_M then
|
||||
self:addChar("m")
|
||||
elseif ev.code == KEY_N then
|
||||
self:addChar("n")
|
||||
elseif ev.code == KEY_O then
|
||||
self:addChar("o")
|
||||
elseif ev.code == KEY_P then
|
||||
self:addChar("p")
|
||||
elseif ev.code == KEY_Q then
|
||||
self:addChar("q")
|
||||
elseif ev.code == KEY_R then
|
||||
self:addChar("r")
|
||||
elseif ev.code == KEY_S then
|
||||
self:addChar("s")
|
||||
elseif ev.code == KEY_T then
|
||||
self:addChar("t")
|
||||
elseif ev.code == KEY_U then
|
||||
self:addChar("u")
|
||||
elseif ev.code == KEY_V then
|
||||
self:addChar("v")
|
||||
elseif ev.code == KEY_W then
|
||||
self:addChar("w")
|
||||
elseif ev.code == KEY_X then
|
||||
self:addChar("x")
|
||||
elseif ev.code == KEY_Y then
|
||||
self:addChar("y")
|
||||
elseif ev.code == KEY_Z then
|
||||
self:addChar("z")
|
||||
elseif ev.code == KEY_1 then
|
||||
self:addChar("1")
|
||||
elseif ev.code == KEY_2 then
|
||||
self:addChar("2")
|
||||
elseif ev.code == KEY_3 then
|
||||
self:addChar("3")
|
||||
elseif ev.code == KEY_4 then
|
||||
self:addChar("4")
|
||||
elseif ev.code == KEY_5 then
|
||||
self:addChar("5")
|
||||
elseif ev.code == KEY_6 then
|
||||
self:addChar("6")
|
||||
elseif ev.code == KEY_7 then
|
||||
self:addChar("7")
|
||||
elseif ev.code == KEY_8 then
|
||||
self:addChar("8")
|
||||
elseif ev.code == KEY_9 then
|
||||
self:addChar("9")
|
||||
elseif ev.code == KEY_0 then
|
||||
self:addChar("0")
|
||||
elseif ev.code == KEY_SPACE then
|
||||
self:addChar(" ")
|
||||
elseif ev.code == KEY_PGFWD then
|
||||
elseif ev.code == KEY_PGBCK then
|
||||
elseif ev.code == KEY_ENTER or ev.code == KEY_FW_PRESS then
|
||||
if self.input_string == "" then
|
||||
self.input_string = nil
|
||||
end
|
||||
break
|
||||
elseif ev.code == KEY_DEL then
|
||||
self:delChar()
|
||||
elseif ev.code == KEY_BACK then
|
||||
self.input_string = nil
|
||||
break
|
||||
keydef = Keydef:new(ev.code, getKeyModifier())
|
||||
print("key pressed: "..tostring(keydef))
|
||||
|
||||
command = self.commands:getByKeydef(keydef)
|
||||
if command ~= nil then
|
||||
print("command to execute: "..tostring(command))
|
||||
ret_code = command.func(self, keydef)
|
||||
else
|
||||
print("command not found: "..tostring(command))
|
||||
end
|
||||
|
||||
--local nsecs, nusecs = util.gettime()
|
||||
--local dur = (nsecs - secs) * 1000000 + nusecs - usecs
|
||||
--print("E: T="..ev.type.." V="..ev.value.." C="..ev.code.." DUR="..dur)
|
||||
if ret_code == "break" then
|
||||
ret_code = nil
|
||||
break
|
||||
end
|
||||
end -- if
|
||||
end -- while
|
||||
|
||||
return self.input_string
|
||||
local return_str = self.input_string
|
||||
self.input_string = ""
|
||||
return return_str
|
||||
end
|
||||
|
||||
function InputBox:addAllCommands()
|
||||
if self.commands then
|
||||
-- we only initialize once
|
||||
return
|
||||
end
|
||||
self.commands = Commands:new{}
|
||||
|
||||
INPUT_KEYS = {
|
||||
{KEY_Q, "q"}, {KEY_W, "w"}, {KEY_E, "e"}, {KEY_R, "r"}, {KEY_T, "t"},
|
||||
{KEY_Y, "y"}, {KEY_U, "u"}, {KEY_I, "i"}, {KEY_O, "o"}, {KEY_P, "p"},
|
||||
|
||||
{KEY_A, "a"}, {KEY_S, "s"}, {KEY_D, "d"}, {KEY_F, "f"}, {KEY_G, "g"},
|
||||
{KEY_H, "h"}, {KEY_J, "j"}, {KEY_K, "k"}, {KEY_L, "l"},
|
||||
|
||||
{KEY_Z, "z"}, {KEY_X, "x"}, {KEY_C, "c"}, {KEY_V, "v"}, {KEY_B, "b"},
|
||||
{KEY_N, "n"}, {KEY_M, "m"},
|
||||
|
||||
{KEY_1, "1"}, {KEY_2, "2"}, {KEY_3, "3"}, {KEY_4, "4"}, {KEY_5, "5"},
|
||||
{KEY_6, "6"}, {KEY_7, "7"}, {KEY_8, "8"}, {KEY_9, "9"}, {KEY_0, "0"},
|
||||
|
||||
{KEY_SPACE, " "},
|
||||
|
||||
-- DXG keys
|
||||
{KEY_DOT, "."}, {KEY_SLASH, "/"},
|
||||
}
|
||||
for k,v in ipairs(INPUT_KEYS) do
|
||||
self.commands:add(v[1], nil, "",
|
||||
"input "..v[2],
|
||||
function(self)
|
||||
self:addChar(v[2])
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
self.commands:add(KEY_FW_LEFT, nil, "",
|
||||
"move cursor left",
|
||||
function(self)
|
||||
if (self.cursor.x_pos + 3) > self.input_start_x then
|
||||
self.cursor:moveHorizontalAndDraw(-self.fwidth)
|
||||
fb:refresh(1, self.input_start_x-5, self.ypos,
|
||||
self.input_slot_w, self.h)
|
||||
end
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_FW_RIGHT, nil, "",
|
||||
"move cursor right",
|
||||
function(self)
|
||||
if (self.cursor.x_pos + 3) < self.input_cur_x then
|
||||
self.cursor:moveHorizontalAndDraw(self.fwidth)
|
||||
fb:refresh(1, self.input_start_x-5, self.ypos,
|
||||
self.input_slot_w, self.h)
|
||||
end
|
||||
end
|
||||
)
|
||||
self.commands:add({KEY_ENTER, KEY_FW_PRESS}, nil, "",
|
||||
"submit input content",
|
||||
function(self)
|
||||
if self.input_string == "" then
|
||||
self.input_string = nil
|
||||
end
|
||||
return "break"
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_DEL, nil, "",
|
||||
"delete one character",
|
||||
function(self)
|
||||
self:delChar()
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_DEL, MOD_SHIFT, "",
|
||||
"empty inputbox",
|
||||
function(self)
|
||||
self:clearText()
|
||||
end
|
||||
)
|
||||
self.commands:add({KEY_BACK, KEY_HOME}, nil, "",
|
||||
"cancel inputbox",
|
||||
function(self)
|
||||
self.input_string = nil
|
||||
return "break"
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
----------------------------------------------------
|
||||
-- Inputbox for numbers only
|
||||
-- Designed by eLiNK
|
||||
----------------------------------------------------
|
||||
|
||||
NumInputBox = InputBox:new{
|
||||
initialized = false,
|
||||
commands = Commands:new{},
|
||||
}
|
||||
|
||||
function NumInputBox:addAllCommands()
|
||||
self.commands = Commands:new{}
|
||||
|
||||
INPUT_NUM_KEYS = {
|
||||
{KEY_Q, "1"}, {KEY_W, "2"}, {KEY_E, "3"}, {KEY_R, "4"}, {KEY_T, "5"},
|
||||
{KEY_Y, "6"}, {KEY_U, "7"}, {KEY_I, "8"}, {KEY_O, "9"}, {KEY_P, "0"},
|
||||
|
||||
{KEY_1, "1"}, {KEY_2, "2"}, {KEY_3, "3"}, {KEY_4, "4"}, {KEY_5, "5"},
|
||||
{KEY_6, "6"}, {KEY_7, "7"}, {KEY_8, "8"}, {KEY_9, "9"}, {KEY_0, "0"},
|
||||
}
|
||||
for k,v in ipairs(INPUT_NUM_KEYS) do
|
||||
self.commands:add(v[1], nil, "",
|
||||
"input "..v[2],
|
||||
function(self)
|
||||
self:addChar(v[2])
|
||||
end
|
||||
)
|
||||
end -- for
|
||||
|
||||
self.commands:add(KEY_FW_LEFT, nil, "",
|
||||
"move cursor left",
|
||||
function(self)
|
||||
if (self.cursor.x_pos + 3) > self.input_start_x then
|
||||
self.cursor:moveHorizontalAndDraw(-self.fwidth)
|
||||
fb:refresh(1, self.input_start_x-5, self.ypos,
|
||||
self.input_slot_w, self.h)
|
||||
end
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_FW_RIGHT, nil, "",
|
||||
"move cursor right",
|
||||
function(self)
|
||||
if (self.cursor.x_pos + 3) < self.input_cur_x then
|
||||
self.cursor:moveHorizontalAndDraw(self.fwidth)
|
||||
fb:refresh(1, self.input_start_x-5, self.ypos,
|
||||
self.input_slot_w, self.h)
|
||||
end
|
||||
end
|
||||
)
|
||||
self.commands:add({KEY_ENTER, KEY_FW_PRESS}, nil, "",
|
||||
"submit input content",
|
||||
function(self)
|
||||
if self.input_string == "" then
|
||||
self.input_string = nil
|
||||
end
|
||||
return "break"
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_DEL, nil, "",
|
||||
"delete one character",
|
||||
function(self)
|
||||
self:delChar()
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_DEL, MOD_SHIFT, "",
|
||||
"empty inputbox",
|
||||
function(self)
|
||||
self:clearText()
|
||||
end
|
||||
)
|
||||
self.commands:add({KEY_BACK, KEY_HOME}, nil, "",
|
||||
"cancel inputbox",
|
||||
function(self)
|
||||
self.input_string = nil
|
||||
return "break"
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
function NumInputBox:drawHelpMsg(ypos, w, h)
|
||||
local w = 415
|
||||
local y = ypos - 60
|
||||
local x = (G_width - w) / 2
|
||||
local h = 50
|
||||
local bw = 2
|
||||
local face = Font:getFace("scfont", 22)
|
||||
|
||||
fb.bb:paintRect(x, y, w, h, 15)
|
||||
fb.bb:paintRect(x+bw, y+bw, w-2*bw, h-2*bw, 0)
|
||||
|
||||
local font_y = y + 22
|
||||
local font_x = x + 22
|
||||
INPUT_NUM_KEYS = {
|
||||
{"Q", "1"}, {"W", "2"}, {"E", "3"}, {"R", "4"}, {"T", "5"},
|
||||
{"Y", "6"}, {"U", "7"}, {"I", "8"}, {"O", "9"}, {"P", "0"},
|
||||
}
|
||||
for k,v in ipairs(INPUT_NUM_KEYS) do
|
||||
renderUtf8Text(fb.bb, font_x, font_y, face,
|
||||
v[1], true)
|
||||
renderUtf8Text(fb.bb, font_x, font_y + 22, face,
|
||||
v[2], true)
|
||||
font_x = font_x + 40
|
||||
end
|
||||
|
||||
fb:refresh(1, x, y, w, h)
|
||||
end
|
||||
|
||||
24
keys.lua
24
keys.lua
@@ -87,6 +87,9 @@ KEY_FW_UP = 122
|
||||
KEY_FW_DOWN = 123
|
||||
KEY_FW_PRESS = 92
|
||||
|
||||
KEY_INTO_SCREEN_SAVER = 10000
|
||||
KEY_OUTOF_SCREEN_SAVER = 10001
|
||||
|
||||
-- constants from <linux/input.h>
|
||||
EV_KEY = 1
|
||||
|
||||
@@ -126,6 +129,8 @@ end
|
||||
function setEmuKeycodes()
|
||||
KEY_PGFWD = 117
|
||||
KEY_PGBCK = 112
|
||||
KEY_LPGBCK = 69 -- F3
|
||||
KEY_LPGFWD = 70 -- F4
|
||||
KEY_HOME = 110 -- home
|
||||
KEY_BACK = 22 -- backspace
|
||||
KEY_DEL = 119 -- Delete
|
||||
@@ -252,3 +257,22 @@ function adjustKeyEvents(ev)
|
||||
print("# Unrecognizable rotation mode "..Screen.cur_rotation_mode.."!")
|
||||
return nil
|
||||
end
|
||||
|
||||
-- wrapper for input.waitForEvents that will retry for some cases
|
||||
function input.saveWaitForEvent(timeout)
|
||||
local retry = true
|
||||
while retry do
|
||||
local ok, ev = pcall(input.waitForEvent, timeout)
|
||||
if not ok then
|
||||
print("got error waiting for events:", ev)
|
||||
if ev == "Waiting for input failed: 4\n" then
|
||||
-- EINTR, we got interrupted. Try and restart
|
||||
retry = true
|
||||
else
|
||||
retry = false
|
||||
end
|
||||
else
|
||||
return ev
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -26,6 +26,9 @@
|
||||
#include "blitbuffer.h"
|
||||
#include "drawcontext.h"
|
||||
#include "pdf.h"
|
||||
#include "mupdfimg.h"
|
||||
#include "djvu.h"
|
||||
#include "cre.h"
|
||||
#include "einkfb.h"
|
||||
#include "input.h"
|
||||
#include "ft.h"
|
||||
@@ -53,9 +56,11 @@ int main(int argc, char **argv) {
|
||||
luaopen_einkfb(L);
|
||||
luaopen_pdf(L);
|
||||
luaopen_djvu(L);
|
||||
luaopen_cre(L);
|
||||
luaopen_input(L);
|
||||
luaopen_util(L);
|
||||
luaopen_ft(L);
|
||||
luaopen_mupdfimg(L);
|
||||
|
||||
luaopen_lfs(L);
|
||||
|
||||
|
||||
57
kpvcrlib/CMakeLists.txt
Normal file
57
kpvcrlib/CMakeLists.txt
Normal file
@@ -0,0 +1,57 @@
|
||||
|
||||
PROJECT(kpvcrlib)
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
|
||||
SET(MUPDF_DIR ../mupdf)
|
||||
SET(MUPDF_3RDPARTY_DIR ${MUPDF_DIR}/thirdparty)
|
||||
SET(CR_3RDPARTY_DIR crengine/thirdparty)
|
||||
|
||||
SET(CR3_PNG 1)
|
||||
#SET(CR3_JPEG 1)
|
||||
|
||||
SET(FREETYPE_INCLUDE_DIRS ${MUPDF_3RDPARTY_DIR}/freetype-2.4.8/include)
|
||||
#SET(FREETYPE_INCLUDE_DIRS ${CR_3RDPARTY_DIR}/freetype/include)
|
||||
SET(ANTIWORD_INCLUDE_DIR ${CR_3RDPARTY_DIR}/antiword)
|
||||
SET(CHM_INCLUDE_DIRS ${CR_3RDPARTY_DIR}/chmlib)
|
||||
SET(PNG_INCLUDE_DIR ${CR_3RDPARTY_DIR}/libpng)
|
||||
SET(ZLIB_INCLUDE_DIR ${MUPDF_3RDPARTY_DIR}/zlib-1.2.5)
|
||||
#SET(ZLIB_INCLUDE_DIR ${CR_3RDPARTY_DIR}/zlib)
|
||||
SET(JPEGLIB_INCLUDE_DIR ${MUPDF_3RDPARTY_DIR}/jpeg-8d)
|
||||
#SET(JPEGLIB_INCLUDE_DIR ${CR_3RDPARTY_DIR}/libjpeg)
|
||||
SET(JCONFIG_INCLUDE_DIR ${MUPDF_DIR}/scripts)
|
||||
|
||||
INCLUDE_DIRECTORIES(
|
||||
${FREETYPE_INCLUDE_DIRS}
|
||||
${ANTIWORD_INCLUDE_DIR}
|
||||
${CHM_INCLUDE_DIRS}
|
||||
${PNG_INCLUDE_DIR}
|
||||
${ZLIB_INCLUDE_DIR}
|
||||
${JPEGLIB_INCLUDE_DIR}
|
||||
${JCONFIG_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
ADD_DEFINITIONS(-DLINUX=1 -D_LINUX=1 -DUSE_FONTCONFIG=0 -DUSE_FREETYPE=1 -DCR3_PATCH=1 -DNDEBUG=1)
|
||||
|
||||
|
||||
message("Will build patched LIBCHM library")
|
||||
ADD_DEFINITIONS(-DCHM_SUPPORT_ENABLED=1)
|
||||
ADD_SUBDIRECTORY(${CR_3RDPARTY_DIR}/chmlib)
|
||||
|
||||
message("Will build patched LIBPNG library")
|
||||
ADD_SUBDIRECTORY(${CR_3RDPARTY_DIR}/libpng)
|
||||
|
||||
#message("Will build patched JPEGLIB library")
|
||||
#ADD_SUBDIRECTORY(${CR_3RDPARTY_DIR}/libjpeg)
|
||||
|
||||
message("Will not build patched ANTIWORD library, because we haven't supported dictionary lookup yet.")
|
||||
#message("Will build patched ANTIWORD library")
|
||||
ADD_DEFINITIONS(-DENABLE_ANTIWORD=0)
|
||||
#ADD_DEFINITIONS(-DCR3_ANTIWORD_PATCH=1)
|
||||
#ADD_SUBDIRECTORY(${CR_3RDPARTY_DIR}/antiword)
|
||||
|
||||
message("Will build crengine library")
|
||||
SET(GUI kpv)
|
||||
#ADD_DEFINITIONS(-DJCONFIG_INCLUDED=1)
|
||||
ADD_SUBDIRECTORY(crengine/crengine)
|
||||
|
||||
|
||||
1
kpvcrlib/crengine
Submodule
1
kpvcrlib/crengine
Submodule
Submodule kpvcrlib/crengine added at ba469d3347
15
kpvcrlib/jpeg_compress_struct_size.patch
Normal file
15
kpvcrlib/jpeg_compress_struct_size.patch
Normal file
@@ -0,0 +1,15 @@
|
||||
--- jcapimin.c 2012-04-04 00:02:30.000000000 +0800
|
||||
+++ jcapimin-patched.c 2012-04-04 00:02:26.000000000 +0800
|
||||
@@ -36,9 +36,9 @@
|
||||
cinfo->mem = NULL; /* so jpeg_destroy knows mem mgr not called */
|
||||
if (version != JPEG_LIB_VERSION)
|
||||
ERREXIT2(cinfo, JERR_BAD_LIB_VERSION, JPEG_LIB_VERSION, version);
|
||||
- if (structsize != SIZEOF(struct jpeg_compress_struct))
|
||||
- ERREXIT2(cinfo, JERR_BAD_STRUCT_SIZE,
|
||||
- (int) SIZEOF(struct jpeg_compress_struct), (int) structsize);
|
||||
+ /*if (structsize != SIZEOF(struct jpeg_compress_struct))*/
|
||||
+ /*ERREXIT2(cinfo, JERR_BAD_STRUCT_SIZE, */
|
||||
+ /*(int) SIZEOF(struct jpeg_compress_struct), (int) structsize);*/
|
||||
|
||||
/* For debugging purposes, we zero the whole master structure.
|
||||
* But the application has already set the err pointer, and may have set
|
||||
15
kpvcrlib/jpeg_decompress_struct_size.patch
Normal file
15
kpvcrlib/jpeg_decompress_struct_size.patch
Normal file
@@ -0,0 +1,15 @@
|
||||
--- jdapimin.c 2012-04-04 01:09:00.000000000 +0800
|
||||
+++ jdapimin-patched.c 2012-04-04 01:42:44.000000000 +0800
|
||||
@@ -36,9 +36,9 @@
|
||||
cinfo->mem = NULL; /* so jpeg_destroy knows mem mgr not called */
|
||||
if (version != JPEG_LIB_VERSION)
|
||||
ERREXIT2(cinfo, JERR_BAD_LIB_VERSION, JPEG_LIB_VERSION, version);
|
||||
- if (structsize != SIZEOF(struct jpeg_decompress_struct))
|
||||
- ERREXIT2(cinfo, JERR_BAD_STRUCT_SIZE,
|
||||
- (int) SIZEOF(struct jpeg_decompress_struct), (int) structsize);
|
||||
+ /*if (structsize != SIZEOF(struct jpeg_decompress_struct))*/
|
||||
+ /*ERREXIT2(cinfo, JERR_BAD_STRUCT_SIZE, */
|
||||
+ /*(int) SIZEOF(struct jpeg_decompress_struct), (int) structsize);*/
|
||||
|
||||
/* For debugging purposes, we zero the whole master structure.
|
||||
* But the application has already set the err pointer, and may have set
|
||||
@@ -1,3 +1,7 @@
|
||||
[Actions]
|
||||
P P = !/mnt/us/launchpad/kpdf.sh
|
||||
# start kindlepdfviewer with filebrowser in /mnt/us/documents
|
||||
P D = !/mnt/us/launchpad/kpdf.sh /mnt/us/documents
|
||||
# start kindlepdfviewer with last document
|
||||
P P = !/mnt/us/launchpad/kpdf.sh
|
||||
# restart amazon framework - when it got irritated
|
||||
P R = !/etc/init.d/framework restart
|
||||
|
||||
@@ -3,6 +3,14 @@ export LC_ALL="en_US.UTF-8"
|
||||
|
||||
echo unlock > /proc/keypad
|
||||
echo unlock > /proc/fiveway
|
||||
|
||||
cd /mnt/us/kindlepdfviewer/
|
||||
./reader.lua $1
|
||||
|
||||
grep /mnt/us/kindlepdfviewer/fonts/host /proc/mounts || mount -o bind /usr/java/lib/fonts /mnt/us/kindlepdfviewer/fonts/host
|
||||
|
||||
./reader.lua "$1" 2> /mnt/us/kindlepdfviewer/crash.log || cat /mnt/us/kindlepdfviewer/crash.log
|
||||
|
||||
grep /mnt/us/kindlepdfviewer/fonts/host /proc/mounts && umount /mnt/us/kindlepdfviewer/fonts/host
|
||||
|
||||
killall -cont cvm
|
||||
echo 1 > /proc/eink_fb/update_display
|
||||
|
||||
171
mupdf.patch
Normal file
171
mupdf.patch
Normal file
@@ -0,0 +1,171 @@
|
||||
diff --git a/pdf/pdf_font.c b/pdf/pdf_font.c
|
||||
index 5e54e0b..38bd1d8 100644
|
||||
--- a/pdf/pdf_font.c
|
||||
+++ b/pdf/pdf_font.c
|
||||
@@ -182,8 +182,13 @@ pdf_load_builtin_font(fz_context *ctx, pdf_font_desc *fontdesc, char *fontname)
|
||||
if (!data)
|
||||
fz_throw(ctx, "cannot find builtin font: '%s'", fontname);
|
||||
|
||||
+#ifndef NOBUILTINFONT
|
||||
fontdesc->font = fz_new_font_from_memory(ctx, data, len, 0, 1);
|
||||
/* RJW: "cannot load freetype font from memory" */
|
||||
+#else
|
||||
+ fontdesc->font = fz_new_font_from_file(ctx, data, 0, 1);
|
||||
+ free(data);
|
||||
+#endif
|
||||
|
||||
if (!strcmp(fontname, "Symbol") || !strcmp(fontname, "ZapfDingbats"))
|
||||
fontdesc->flags |= PDF_FD_SYMBOLIC;
|
||||
@@ -199,8 +204,13 @@ pdf_load_substitute_font(fz_context *ctx, pdf_font_desc *fontdesc, int mono, int
|
||||
if (!data)
|
||||
fz_throw(ctx, "cannot find substitute font");
|
||||
|
||||
+#ifndef NOBUILTINFONT
|
||||
fontdesc->font = fz_new_font_from_memory(ctx, data, len, 0, 1);
|
||||
/* RJW: "cannot load freetype font from memory" */
|
||||
+#else
|
||||
+ fontdesc->font = fz_new_font_from_file(ctx, data, 0, 1);
|
||||
+ free(data);
|
||||
+#endif
|
||||
|
||||
fontdesc->font->ft_substitute = 1;
|
||||
fontdesc->font->ft_bold = bold && !ft_is_bold(fontdesc->font->ft_face);
|
||||
@@ -218,7 +228,12 @@ pdf_load_substitute_cjk_font(fz_context *ctx, pdf_font_desc *fontdesc, int ros,
|
||||
fz_throw(ctx, "cannot find builtin CJK font");
|
||||
|
||||
/* a glyph bbox cache is too big for droid sans fallback (51k glyphs!) */
|
||||
+#ifndef NOBUILTINFONT
|
||||
fontdesc->font = fz_new_font_from_memory(ctx, data, len, 0, 0);
|
||||
+#else
|
||||
+ fontdesc->font = fz_new_font_from_file(ctx, data, 0, 0);
|
||||
+ free(data);
|
||||
+#endif
|
||||
/* RJW: "cannot load builtin CJK font" */
|
||||
|
||||
fontdesc->font->ft_substitute = 1;
|
||||
diff --git a/pdf/pdf_fontfile.c b/pdf/pdf_fontfile.c
|
||||
index 543ce76..a076033 100644
|
||||
--- a/pdf/pdf_fontfile.c
|
||||
+++ b/pdf/pdf_fontfile.c
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "fitz.h"
|
||||
#include "mupdf.h"
|
||||
|
||||
+#ifndef NOBUILTINFONT
|
||||
+
|
||||
#ifdef NOCJK
|
||||
#define NOCJKFONT
|
||||
#endif
|
||||
@@ -129,3 +131,112 @@ pdf_find_substitute_cjk_font(int ros, int serif, unsigned int *len)
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
+
|
||||
+#else // NOBUILTINFONT
|
||||
+
|
||||
+unsigned char *
|
||||
+get_font_file(char *name)
|
||||
+{
|
||||
+ char *fontdir;
|
||||
+ char *filename;
|
||||
+ int len;
|
||||
+ fontdir = getenv("FONTDIR");
|
||||
+ if(fontdir == NULL) {
|
||||
+ fontdir = "./fonts";
|
||||
+ }
|
||||
+ len = strlen(fontdir) + strlen(name) + 2;
|
||||
+ filename = malloc(len);
|
||||
+ if(filename == NULL) {
|
||||
+ return NULL;
|
||||
+ }
|
||||
+ snprintf(filename, len, "%s/%s", fontdir, name);
|
||||
+ return filename;
|
||||
+}
|
||||
+
|
||||
+unsigned char *
|
||||
+pdf_find_builtin_font(char *name, unsigned int *len)
|
||||
+{
|
||||
+ *len = 0;
|
||||
+ if (!strcmp("Courier", name)) {
|
||||
+ return get_font_file("NimbusMonL-Regu.cff");
|
||||
+ }
|
||||
+ if (!strcmp("Courier-Bold", name)) {
|
||||
+ return get_font_file("NimbusMonL-Bold.cff");
|
||||
+ }
|
||||
+ if (!strcmp("Courier-Oblique", name)) {
|
||||
+ return get_font_file("NimbusMonL-ReguObli.cff");
|
||||
+ }
|
||||
+ if (!strcmp("Courier-BoldOblique", name)) {
|
||||
+ return get_font_file("NimbusMonL-BoldObli.cff");
|
||||
+ }
|
||||
+ if (!strcmp("Helvetica", name)) {
|
||||
+ return get_font_file("NimbusSanL-Regu.cff");
|
||||
+ }
|
||||
+ if (!strcmp("Helvetica-Bold", name)) {
|
||||
+ return get_font_file("NimbusSanL-Bold.cff");
|
||||
+ }
|
||||
+ if (!strcmp("Helvetica-Oblique", name)) {
|
||||
+ return get_font_file("NimbusSanL-ReguItal.cff");
|
||||
+ }
|
||||
+ if (!strcmp("Helvetica-BoldOblique", name)) {
|
||||
+ return get_font_file("NimbusSanL-BoldItal.cff");
|
||||
+ }
|
||||
+ if (!strcmp("Times-Roman", name)) {
|
||||
+ return get_font_file("NimbusRomNo9L-Regu.cff");
|
||||
+ }
|
||||
+ if (!strcmp("Times-Bold", name)) {
|
||||
+ return get_font_file("NimbusRomNo9L-Medi.cff");
|
||||
+ }
|
||||
+ if (!strcmp("Times-Italic", name)) {
|
||||
+ return get_font_file("NimbusRomNo9L-ReguItal.cff");
|
||||
+ }
|
||||
+ if (!strcmp("Times-BoldItalic", name)) {
|
||||
+ return get_font_file("NimbusRomNo9L-MediItal.cff");
|
||||
+ }
|
||||
+ if (!strcmp("Symbol", name)) {
|
||||
+ return get_font_file("StandardSymL.cff");
|
||||
+ }
|
||||
+ if (!strcmp("ZapfDingbats", name)) {
|
||||
+ return get_font_file("Dingbats.cff");
|
||||
+ }
|
||||
+ return NULL;
|
||||
+}
|
||||
+
|
||||
+unsigned char *
|
||||
+pdf_find_substitute_font(int mono, int serif, int bold, int italic, unsigned int *len)
|
||||
+{
|
||||
+ if (mono) {
|
||||
+ if (bold) {
|
||||
+ if (italic) return pdf_find_builtin_font("Courier-BoldOblique", len);
|
||||
+ else return pdf_find_builtin_font("Courier-Bold", len);
|
||||
+ } else {
|
||||
+ if (italic) return pdf_find_builtin_font("Courier-Oblique", len);
|
||||
+ else return pdf_find_builtin_font("Courier", len);
|
||||
+ }
|
||||
+ } else if (serif) {
|
||||
+ if (bold) {
|
||||
+ if (italic) return pdf_find_builtin_font("Times-BoldItalic", len);
|
||||
+ else return pdf_find_builtin_font("Times-Bold", len);
|
||||
+ } else {
|
||||
+ if (italic) return pdf_find_builtin_font("Times-Italic", len);
|
||||
+ else return pdf_find_builtin_font("Times-Roman", len);
|
||||
+ }
|
||||
+ } else {
|
||||
+ if (bold) {
|
||||
+ if (italic) return pdf_find_builtin_font("Helvetica-BoldOblique", len);
|
||||
+ else return pdf_find_builtin_font("Helvetica-Bold", len);
|
||||
+ } else {
|
||||
+ if (italic) return pdf_find_builtin_font("Helvetica-Oblique", len);
|
||||
+ else return pdf_find_builtin_font("Helvetica", len);
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+unsigned char *
|
||||
+pdf_find_substitute_cjk_font(int ros, int serif, unsigned int *len)
|
||||
+{
|
||||
+ *len = 0;
|
||||
+ return get_font_file("droid/DroidSansFallback.ttf");
|
||||
+}
|
||||
+
|
||||
+#endif // NOBUILTINFONT
|
||||
154
mupdfimg.c
Normal file
154
mupdfimg.c
Normal file
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
KindlePDFViewer: MuPDF abstraction for Lua, only image part
|
||||
Copyright (C) 2012 Hans-Werner Hilse <hilse@web.de>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <fitz/fitz.h>
|
||||
|
||||
#include "blitbuffer.h"
|
||||
#include "mupdfimg.h"
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
|
||||
|
||||
typedef struct Image {
|
||||
fz_pixmap *pixmap;
|
||||
fz_context *context;
|
||||
} Image;
|
||||
|
||||
static int newImage(lua_State *L) {
|
||||
int cache_size = luaL_optint(L, 1, 8 << 20); // 8 MB limit default
|
||||
|
||||
Image *img = (Image*) lua_newuserdata(L, sizeof(Image));
|
||||
|
||||
img->pixmap = NULL;
|
||||
|
||||
luaL_getmetatable(L, "image");
|
||||
lua_setmetatable(L, -2);
|
||||
|
||||
img->context = fz_new_context(NULL, NULL, cache_size);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int loadPNGData(lua_State *L) {
|
||||
Image *img = (Image*) luaL_checkudata(L, 1, "image");
|
||||
size_t length;
|
||||
unsigned char *data = luaL_checklstring(L, 2, &length);
|
||||
fz_try(img->context) {
|
||||
img->pixmap = fz_load_png(img->context, data, length);
|
||||
}
|
||||
fz_catch(img->context) {
|
||||
return luaL_error(L, "cannot load PNG data");
|
||||
}
|
||||
}
|
||||
|
||||
static int loadJPEGData(lua_State *L) {
|
||||
Image *img = (Image*) luaL_checkudata(L, 1, "image");
|
||||
size_t length;
|
||||
unsigned char *data = luaL_checklstring(L, 2, &length);
|
||||
fz_try(img->context) {
|
||||
img->pixmap = fz_load_jpeg(img->context, data, length);
|
||||
}
|
||||
fz_catch(img->context) {
|
||||
return luaL_error(L, "cannot open JPEG data");
|
||||
}
|
||||
}
|
||||
|
||||
static int toBlitBuffer(lua_State *L) {
|
||||
Image *img = (Image*) luaL_checkudata(L, 1, "image");
|
||||
BlitBuffer *bb;
|
||||
int ret;
|
||||
int w, h;
|
||||
|
||||
fz_pixmap *pix;
|
||||
|
||||
if(img->pixmap == NULL) {
|
||||
return luaL_error(L, "no pixmap loaded that we could convert");
|
||||
}
|
||||
|
||||
if(img->pixmap->n == 2) {
|
||||
pix = img->pixmap;
|
||||
} else {
|
||||
fz_try(img->context) {
|
||||
pix = fz_new_pixmap(img->context, fz_device_gray, img->pixmap->w, img->pixmap->h);
|
||||
}
|
||||
fz_catch(img->context) {
|
||||
return luaL_error(L, "can't claim new grayscale fz_pixmap");
|
||||
}
|
||||
fz_convert_pixmap(img->context, img->pixmap, pix);
|
||||
}
|
||||
|
||||
ret = newBlitBufferNative(L, img->pixmap->w, img->pixmap->h, &bb);
|
||||
if(ret != 1) {
|
||||
// TODO (?): fail more gracefully, clean up mem?
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint8_t *bbptr = (uint8_t*)bb->data;
|
||||
uint16_t *pmptr = (uint16_t*)pix->samples;
|
||||
int x, y;
|
||||
|
||||
for(y = 0; y < bb->h; y++) {
|
||||
for(x = 0; x < (bb->w / 2); x++) {
|
||||
bbptr[x] = (((pmptr[x*2 + 1] & 0xF0) >> 4) | (pmptr[x*2] & 0xF0)) ^ 0xFF;
|
||||
}
|
||||
if(bb->w & 1) {
|
||||
bbptr[x] = (pmptr[x*2] & 0xF0) ^ 0xF0;
|
||||
}
|
||||
bbptr += bb->pitch;
|
||||
pmptr += bb->w;
|
||||
}
|
||||
|
||||
if(pix != img->pixmap) {
|
||||
fz_drop_pixmap(img->context, pix);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int freeImage(lua_State *L) {
|
||||
Image *img = (Image*) luaL_checkudata(L, 1, "image");
|
||||
if(img->pixmap) {
|
||||
fz_drop_pixmap(img->context, img->pixmap);
|
||||
}
|
||||
fz_free_context(img->context);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct luaL_Reg mupdfimg_func[] = {
|
||||
{"new", newImage},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
static const struct luaL_Reg image_meth[] = {
|
||||
{"loadPNGData", loadPNGData},
|
||||
{"loadJPEGData", loadJPEGData},
|
||||
{"toBlitBuffer", toBlitBuffer},
|
||||
{"free", freeImage},
|
||||
{"__gc", freeImage},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
int luaopen_mupdfimg(lua_State *L) {
|
||||
luaL_newmetatable(L, "image");
|
||||
lua_pushstring(L, "__index");
|
||||
lua_pushvalue(L, -2);
|
||||
lua_settable(L, -3);
|
||||
luaL_register(L, NULL, image_meth);
|
||||
lua_pop(L, 1);
|
||||
luaL_register(L, "mupdfimg", mupdfimg_func);
|
||||
return 1;
|
||||
}
|
||||
26
mupdfimg.h
Normal file
26
mupdfimg.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
KindlePDFViewer: MuPDF abstraction for Lua, only image part
|
||||
Copyright (C) 2012 Hans-Werner Hilse <hilse@web.de>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef _MUPDFIMG_H
|
||||
#define _MUPDFIMG_H
|
||||
|
||||
#include <lua.h>
|
||||
#include <lualib.h>
|
||||
#include <lauxlib.h>
|
||||
|
||||
int luaopen_mupdfimg(lua_State *L);
|
||||
#endif
|
||||
143
pdf.c
143
pdf.c
@@ -164,16 +164,16 @@ fz_alloc_context my_alloc_default =
|
||||
|
||||
static int openDocument(lua_State *L) {
|
||||
char *filename = strdup(luaL_checkstring(L, 1));
|
||||
int cachesize = luaL_optint(L, 2, 64 << 20); // 64 MB limit default
|
||||
int cache_size = luaL_optint(L, 2, 64 << 20); // 64 MB limit default
|
||||
char buf[15];
|
||||
printf("cachesize: %s\n",readable_fs(cachesize,buf));
|
||||
printf("## cache_size: %s\n",readable_fs(cache_size,buf));
|
||||
|
||||
PdfDocument *doc = (PdfDocument*) lua_newuserdata(L, sizeof(PdfDocument));
|
||||
|
||||
luaL_getmetatable(L, "pdfdocument");
|
||||
lua_setmetatable(L, -2);
|
||||
|
||||
doc->context = fz_new_context(&my_alloc_default, NULL, cachesize);
|
||||
doc->context = fz_new_context(&my_alloc_default, NULL, cache_size);
|
||||
|
||||
fz_try(doc->context) {
|
||||
doc->xref = fz_open_document(doc->context, filename);
|
||||
@@ -311,6 +311,125 @@ static int openPage(lua_State *L) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* get the text of the given page
|
||||
*
|
||||
* will return text in a Lua table that is modeled after
|
||||
* djvu.c creates this table.
|
||||
*
|
||||
* note that the definition of "line" is somewhat arbitrary
|
||||
* here (for now)
|
||||
*
|
||||
* MuPDFs API provides text as single char information
|
||||
* that is collected in "spans". we use a span as a "line"
|
||||
* in Lua output and segment spans into words by looking
|
||||
* for space characters.
|
||||
*
|
||||
* will return an empty table if we have no text
|
||||
*/
|
||||
static int getPageText(lua_State *L) {
|
||||
fz_text_span *page_text;
|
||||
fz_text_span *ptr;
|
||||
fz_device *tdev;
|
||||
fz_bbox bbox, linebbox;
|
||||
int i;
|
||||
int word, line;
|
||||
int len, c;
|
||||
int start;
|
||||
char chars[4]; // max length of UTF-8 encoded rune
|
||||
luaL_Buffer textbuf;
|
||||
|
||||
PdfPage *page = (PdfPage*) luaL_checkudata(L, 1, "pdfpage");
|
||||
|
||||
page_text = fz_new_text_span(page->doc->context);
|
||||
tdev = fz_new_text_device(page->doc->context, page_text);
|
||||
fz_run_page(page->doc->xref, page->page, tdev, fz_identity, NULL);
|
||||
fz_free_device(tdev);
|
||||
|
||||
/* table that contains all the lines */
|
||||
lua_newtable(L);
|
||||
line = 1;
|
||||
for(ptr = page_text; ptr != NULL; ptr = ptr->next) {
|
||||
if(ptr->text == NULL) continue;
|
||||
|
||||
/* table for the words */
|
||||
lua_newtable(L);
|
||||
word = 1;
|
||||
linebbox = ptr->text[0].bbox; // start with sensible default
|
||||
for(i = 0; i < ptr->len; ) {
|
||||
/* will hold information about a word: */
|
||||
lua_newtable(L);
|
||||
|
||||
luaL_buffinit(L, &textbuf);
|
||||
bbox = ptr->text[i].bbox; // start with sensible default
|
||||
for(; i < ptr->len; i++) {
|
||||
/* check for space characters */
|
||||
if(ptr->text[i].c == ' ' ||
|
||||
ptr->text[i].c == '\t' ||
|
||||
ptr->text[i].c == '\n' ||
|
||||
ptr->text[i].c == '\v' ||
|
||||
ptr->text[i].c == '\f' ||
|
||||
ptr->text[i].c == '\r' ||
|
||||
ptr->text[i].c == 0xA0 ||
|
||||
ptr->text[i].c == 0x1680 ||
|
||||
ptr->text[i].c == 0x180E ||
|
||||
(ptr->text[i].c >= 0x2000 && ptr->text[i].c <= 0x200A) ||
|
||||
ptr->text[i].c == 0x202F ||
|
||||
ptr->text[i].c == 0x205F ||
|
||||
ptr->text[i].c == 0x3000) {
|
||||
// ignore and end word
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
len = runetochar(chars, &ptr->text[i].c);
|
||||
for(c = 0; c < len; c++) {
|
||||
luaL_addchar(&textbuf, chars[c]);
|
||||
}
|
||||
bbox = fz_union_bbox(bbox, ptr->text[i].bbox);
|
||||
linebbox = fz_union_bbox(linebbox, ptr->text[i].bbox);
|
||||
}
|
||||
lua_pushstring(L, "word");
|
||||
luaL_pushresult(&textbuf);
|
||||
lua_settable(L, -3);
|
||||
|
||||
/* bbox for a word: */
|
||||
lua_pushstring(L, "x0");
|
||||
lua_pushinteger(L, bbox.x0);
|
||||
lua_settable(L, -3);
|
||||
lua_pushstring(L, "y0");
|
||||
lua_pushinteger(L, bbox.y0);
|
||||
lua_settable(L, -3);
|
||||
lua_pushstring(L, "x1");
|
||||
lua_pushinteger(L, bbox.x1);
|
||||
lua_settable(L, -3);
|
||||
lua_pushstring(L, "y1");
|
||||
lua_pushinteger(L, bbox.y1);
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_rawseti(L, -2, word++);
|
||||
}
|
||||
|
||||
/* bbox for a whole line (or in fact, a "span") */
|
||||
lua_pushstring(L, "x0");
|
||||
lua_pushinteger(L, linebbox.x0);
|
||||
lua_settable(L, -3);
|
||||
lua_pushstring(L, "y0");
|
||||
lua_pushinteger(L, linebbox.y0);
|
||||
lua_settable(L, -3);
|
||||
lua_pushstring(L, "x1");
|
||||
lua_pushinteger(L, linebbox.x1);
|
||||
lua_settable(L, -3);
|
||||
lua_pushstring(L, "y1");
|
||||
lua_pushinteger(L, linebbox.y1);
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_rawseti(L, -2, line++);
|
||||
}
|
||||
|
||||
fz_free_text_span(page->doc->context, page_text);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int getPageSize(lua_State *L) {
|
||||
fz_matrix ctm;
|
||||
fz_rect bounds;
|
||||
@@ -323,7 +442,7 @@ static int getPageSize(lua_State *L) {
|
||||
ctm = fz_concat(ctm, fz_rotate(dc->rotate));
|
||||
bbox = fz_transform_rect(ctm, bounds);
|
||||
|
||||
lua_pushnumber(L, bbox.x1-bbox.x0);
|
||||
lua_pushnumber(L, bbox.x1-bbox.x0);
|
||||
lua_pushnumber(L, bbox.y1-bbox.y0);
|
||||
|
||||
return 2;
|
||||
@@ -423,6 +542,17 @@ static int drawPage(lua_State *L) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int getCacheSize(lua_State *L) {
|
||||
printf("## mupdf getCacheSize = %d\n", msize);
|
||||
lua_pushnumber(L, msize);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int cleanCache(lua_State *L) {
|
||||
printf("## mupdf cleanCache NOP\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct luaL_Reg pdf_func[] = {
|
||||
{"openDocument", openDocument},
|
||||
{NULL, NULL}
|
||||
@@ -433,8 +563,10 @@ static const struct luaL_Reg pdfdocument_meth[] = {
|
||||
{"authenticatePassword", authenticatePassword},
|
||||
{"openPage", openPage},
|
||||
{"getPages", getNumberOfPages},
|
||||
{"getTOC", getTableOfContent},
|
||||
{"getToc", getTableOfContent},
|
||||
{"close", closeDocument},
|
||||
{"getCacheSize", getCacheSize},
|
||||
{"cleanCache", cleanCache},
|
||||
{"__gc", closeDocument},
|
||||
{NULL, NULL}
|
||||
};
|
||||
@@ -442,6 +574,7 @@ static const struct luaL_Reg pdfdocument_meth[] = {
|
||||
static const struct luaL_Reg pdfpage_meth[] = {
|
||||
{"getSize", getPageSize},
|
||||
{"getUsedBBox", getUsedBBox},
|
||||
{"getPageText", getPageText},
|
||||
{"close", closePage},
|
||||
{"__gc", closePage},
|
||||
{"draw", drawPage},
|
||||
|
||||
@@ -8,12 +8,12 @@ function PDFReader:open(filename)
|
||||
-- muPDF manages its own cache, set second parameter
|
||||
-- to the maximum size you want it to grow
|
||||
local ok
|
||||
ok, self.doc = pcall(pdf.openDocument, filename, 64*1024*1024)
|
||||
ok, self.doc = pcall(pdf.openDocument, filename, self.cache_document_size)
|
||||
if not ok then
|
||||
return false, self.doc -- will contain error message
|
||||
end
|
||||
if self.doc:needsPassword() then
|
||||
local password = InputBox:input(height-100, 100, "Pass:")
|
||||
local password = InputBox:input(G_height-100, 100, "Pass:")
|
||||
if not password or not self.doc:authenticatePassword(password) then
|
||||
self.doc:close()
|
||||
self.doc = nil
|
||||
@@ -30,3 +30,18 @@ function PDFReader:open(filename)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
----------------------------------------------------
|
||||
-- highlight support
|
||||
----------------------------------------------------
|
||||
function PDFReader:getText(pageno)
|
||||
local ok, page = pcall(self.doc.openPage, self.doc, pageno)
|
||||
if not ok then
|
||||
-- TODO: error handling
|
||||
return nil
|
||||
end
|
||||
local text = page:getPageText()
|
||||
--print("## page:getPageText "..dump(text)) -- performance impact on device
|
||||
page:close()
|
||||
return text
|
||||
end
|
||||
|
||||
47
reader.lua
47
reader.lua
@@ -20,11 +20,13 @@
|
||||
require "alt_getopt"
|
||||
require "pdfreader"
|
||||
require "djvureader"
|
||||
require "crereader"
|
||||
require "filechooser"
|
||||
require "settings"
|
||||
require "screen"
|
||||
require "keys"
|
||||
require "commands"
|
||||
require "dialog"
|
||||
|
||||
-- option parsing:
|
||||
longopts = {
|
||||
@@ -42,17 +44,23 @@ function openFile(filename)
|
||||
reader = DJVUReader
|
||||
elseif file_type == "pdf" or file_type == "xps" or file_type == "cbz" then
|
||||
reader = PDFReader
|
||||
elseif file_type == "epub" or file_type == "txt" or file_type == "rtf" or file_type == "htm" or file_type == "html" or file_type == "fb2" or file_type == "chm" then
|
||||
reader = CREReader
|
||||
end
|
||||
if reader then
|
||||
InfoMessage:show("Opening document, please wait... ")
|
||||
fb:refresh(0)
|
||||
local ok, err = reader:open(filename)
|
||||
if ok then
|
||||
reader:loadSettings(filename)
|
||||
page_num = reader.settings:readSetting("last_page") or 1
|
||||
page_num = reader:getLastPageOrPos()
|
||||
reader:goto(tonumber(page_num))
|
||||
reader_settings:savesetting("lastfile", filename)
|
||||
return reader:inputLoop()
|
||||
else
|
||||
-- TODO: error handling
|
||||
InfoMessage:show("Error opening document.")
|
||||
fb:refresh(0)
|
||||
util.sleep(2)
|
||||
end
|
||||
end
|
||||
return true -- on failed attempts, we signal to keep running
|
||||
@@ -66,9 +74,6 @@ function showusage()
|
||||
print("-g, --goto=page start reading on page")
|
||||
print("-G, --gamma=GAMMA set gamma correction")
|
||||
print(" (floating point notation, e.g. \"1.5\")")
|
||||
print("-d, --device=DEVICE set device specific configuration,")
|
||||
print(" currently one of \"kdxg\" (default), \"k3\"")
|
||||
print(" \"emu\" (DXG emulation)")
|
||||
print("-h, --help show this usage help")
|
||||
print("")
|
||||
print("If you give the name of a directory instead of a file path, a file")
|
||||
@@ -86,18 +91,12 @@ if optarg["h"] then
|
||||
return showusage()
|
||||
end
|
||||
|
||||
|
||||
if optarg["d"] == "k3" then
|
||||
-- for now, the only difference is the additional input device
|
||||
input.open("/dev/input/event0")
|
||||
input.open("/dev/input/event1")
|
||||
input.open("/dev/input/event2")
|
||||
setK3Keycodes()
|
||||
elseif optarg["d"] == "emu" then
|
||||
if util.isEmulated()==1 then
|
||||
input.open("")
|
||||
-- SDL key codes
|
||||
setEmuKeycodes()
|
||||
else
|
||||
input.open("slider")
|
||||
input.open("/dev/input/event0")
|
||||
input.open("/dev/input/event1")
|
||||
|
||||
@@ -116,16 +115,16 @@ if optarg["G"] ~= nil then
|
||||
end
|
||||
|
||||
fb = einkfb.open("/dev/fb0")
|
||||
width, height = fb:getSize()
|
||||
G_width, G_height = fb:getSize()
|
||||
-- read current rotation mode
|
||||
Screen:updateRotationMode()
|
||||
origin_rotation_mode = Screen.cur_rotation_mode
|
||||
Screen.native_rotation_mode = Screen.cur_rotation_mode
|
||||
|
||||
-- set up reader's setting: font
|
||||
reader_settings = DocSettings:open(".reader")
|
||||
r_cfont = reader_settings:readSetting("cfont")
|
||||
if r_cfont ~=nil then
|
||||
Font.cfont = r_cfont
|
||||
fontmap = reader_settings:readSetting("fontmap")
|
||||
if fontmap ~= nil then
|
||||
Font.fontmap = fontmap
|
||||
end
|
||||
|
||||
-- initialize global settings shared among all readers
|
||||
@@ -133,6 +132,7 @@ UniReader:initGlobalSettings(reader_settings)
|
||||
-- initialize specific readers
|
||||
PDFReader:init()
|
||||
DJVUReader:init()
|
||||
CREReader:init()
|
||||
|
||||
-- display directory or open file
|
||||
local patharg = reader_settings:readSetting("lastfile")
|
||||
@@ -140,12 +140,13 @@ if ARGV[optind] and lfs.attributes(ARGV[optind], "mode") == "directory" then
|
||||
local running = true
|
||||
FileChooser:setPath(ARGV[optind])
|
||||
while running do
|
||||
local file, callback = FileChooser:choose(0,height)
|
||||
local file, callback = FileChooser:choose(0, G_height)
|
||||
if callback then
|
||||
callback()
|
||||
else
|
||||
if file ~= nil then
|
||||
running = openFile(file)
|
||||
print(file)
|
||||
else
|
||||
running = false
|
||||
end
|
||||
@@ -161,15 +162,15 @@ end
|
||||
|
||||
|
||||
-- save reader settings
|
||||
reader_settings:savesetting("cfont", Font.cfont)
|
||||
reader_settings:savesetting("fontmap", Font.fontmap)
|
||||
reader_settings:close()
|
||||
|
||||
-- @TODO dirty workaround, find a way to force native system poll
|
||||
-- screen orientation and upside down mode 09.03 2012
|
||||
fb:setOrientation(origin_rotation_mode)
|
||||
fb:setOrientation(Screen.native_rotation_mode)
|
||||
|
||||
input.closeAll()
|
||||
--os.execute('test -e /proc/keypad && echo "send '..KEY_HOME..'" > /proc/keypad ')
|
||||
if optarg["d"] ~= "emu" then
|
||||
if util.isEmulated()==0 then
|
||||
--os.execute("killall -cont cvm")
|
||||
os.execute('echo "send '..KEY_MENU..'" > /proc/keypad;echo "send '..KEY_MENU..'" > /proc/keypad')
|
||||
end
|
||||
|
||||
@@ -13,6 +13,7 @@ function glyphCacheClaim(size)
|
||||
glyphcache[k].age = glyphcache[k].age - 1
|
||||
else
|
||||
glyphcache_current_memsize = glyphcache_current_memsize - glyphcache[k].size
|
||||
glyphcache[k].glyph.bb:free()
|
||||
glyphcache[k] = nil
|
||||
end
|
||||
end
|
||||
@@ -20,10 +21,10 @@ function glyphCacheClaim(size)
|
||||
glyphcache_current_memsize = glyphcache_current_memsize + size
|
||||
return true
|
||||
end
|
||||
function getGlyph(face, facehash, charcode)
|
||||
local hash = glyphCacheHash(facehash, charcode)
|
||||
function getGlyph(face, charcode)
|
||||
local hash = glyphCacheHash(face.hash, charcode)
|
||||
if glyphcache[hash] == nil then
|
||||
local glyph = face:renderGlyph(charcode)
|
||||
local glyph = face.ftface:renderGlyph(charcode)
|
||||
local size = glyph.bb:getWidth() * glyph.bb:getHeight() / 2 + 32
|
||||
glyphCacheClaim(size);
|
||||
glyphcache[hash] = {
|
||||
@@ -88,9 +89,9 @@ function renderUtf8Text(buffer, x, y, face, facehash, text, kerning, backgroundC
|
||||
for uchar in string.gfind(text, "([%z\1-\127\194-\244][\128-\191]*)") do
|
||||
if pen_x < buffer:getWidth() then
|
||||
local charcode = util.utf8charcode(uchar)
|
||||
local glyph = getGlyph(face, facehash, charcode)
|
||||
local glyph = getGlyph(face, charcode)
|
||||
if kerning and prevcharcode then
|
||||
local kern = face:getKerning(prevcharcode, charcode)
|
||||
local kern = face.ftface:getKerning(prevcharcode, charcode)
|
||||
pen_x = pen_x + kern
|
||||
--print("prev:"..string.char(prevcharcode).." curr:"..string.char(charcode).." pen_x:"..pen_x.." kern:"..kern)
|
||||
buffer:addblitFrom(glyph.bb, x + pen_x + glyph.l, y - glyph.t, 0, 0, glyph.bb:getWidth(), glyph.bb:getHeight())
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
#!./kpdfview
|
||||
require "rendertext"
|
||||
require "graphics"
|
||||
|
||||
fb = einkfb.open("/dev/fb0")
|
||||
width, height = fb:getSize()
|
||||
|
||||
print("open")
|
||||
size = 50
|
||||
--face = freetype.newBuiltinFace("sans", 64)
|
||||
face = freetype.newFace("/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf", size)
|
||||
print("got face")
|
||||
|
||||
if face:hasKerning() then
|
||||
print("has kerning")
|
||||
end
|
||||
|
||||
width, height = fb:getSize()
|
||||
fb.bb:paintRect(5,5,width-5,height-5,4);
|
||||
|
||||
faceHeight, faceAscender = face:getHeightAndAscender();
|
||||
print("face height:"..tostring(faceHeight).." - ascender:"..faceAscender)
|
||||
faceHeight = math.ceil(faceHeight)
|
||||
faceAscender = math.ceil(faceAscender)
|
||||
print("face height:"..tostring(faceHeight).." - ascender:"..faceAscender)
|
||||
|
||||
posY = 5 + faceAscender
|
||||
renderUtf8Text(fb.bb, 5, posY, face, "h", "AV T.T: gxyt!", true)
|
||||
posY = posY + faceHeight
|
||||
renderUtf8Text(fb.bb, 5, posY, face, "h2", "AV T.T: gxyt!", false)
|
||||
|
||||
fb:refresh()
|
||||
|
||||
while true do
|
||||
local ev = input.waitForEvent()
|
||||
end
|
||||
BIN
resources/info-i.png
Normal file
BIN
resources/info-i.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
30
screen.lua
30
screen.lua
@@ -41,6 +41,11 @@ Codes for rotation modes:
|
||||
|
||||
Screen = {
|
||||
cur_rotation_mode = 0,
|
||||
-- these two variabls are used to help switching from framework to reader
|
||||
native_rotation_mode = nil,
|
||||
kpv_rotation_mode = nil,
|
||||
|
||||
saved_bb = nil,
|
||||
}
|
||||
|
||||
-- @orien: 1 for clockwise rotate, -1 for anti-clockwise
|
||||
@@ -71,4 +76,29 @@ function Screen:updateRotationMode()
|
||||
end
|
||||
end
|
||||
|
||||
function Screen:saveCurrentBB()
|
||||
local width, height = G_width, G_height
|
||||
|
||||
if not self.saved_bb then
|
||||
self.saved_bb = Blitbuffer.new(width, height)
|
||||
end
|
||||
if self.saved_bb:getWidth() ~= width then
|
||||
self.saved_bb:free()
|
||||
self.saved_bb = Blitbuffer.new(width, height)
|
||||
end
|
||||
self.saved_bb:blitFullFrom(fb.bb)
|
||||
end
|
||||
|
||||
function Screen:restoreFromSavedBB()
|
||||
self:restoreFromBB(self.saved_bb)
|
||||
end
|
||||
|
||||
function Screen:getCurrentScreenBB()
|
||||
local bb = Blitbuffer.new(G_width, G_height)
|
||||
bb:blitFullFrom(fb.bb)
|
||||
return bb
|
||||
end
|
||||
|
||||
function Screen:restoreFromBB(bb)
|
||||
fb.bb:blitFullFrom(bb)
|
||||
end
|
||||
|
||||
332
selectmenu.lua
332
selectmenu.lua
@@ -2,6 +2,7 @@ require "rendertext"
|
||||
require "keys"
|
||||
require "graphics"
|
||||
require "font"
|
||||
require "commands"
|
||||
|
||||
SelectMenu = {
|
||||
-- font for displaying item names
|
||||
@@ -11,8 +12,7 @@ SelectMenu = {
|
||||
-- font for paging display
|
||||
ffsize = 16,
|
||||
-- font for item shortcut
|
||||
sface = freetype.newBuiltinFace("mono", 22),
|
||||
sfhash = "mono22",
|
||||
sface = Font:getFace("scfont", 22),
|
||||
|
||||
-- title height
|
||||
title_H = 40,
|
||||
@@ -21,7 +21,7 @@ SelectMenu = {
|
||||
-- foot height
|
||||
foot_H = 27,
|
||||
|
||||
menu_title = "None Titled",
|
||||
menu_title = "No Title",
|
||||
no_item_msg = "No items found.",
|
||||
item_array = {},
|
||||
items = 0,
|
||||
@@ -32,10 +32,14 @@ SelectMenu = {
|
||||
"Z", "X", "C", "V", "B", "N", "M", ".", "Sym", "Ent",
|
||||
},
|
||||
last_shortcut = 0,
|
||||
|
||||
-- state buffer
|
||||
page = 1,
|
||||
current = 1,
|
||||
oldcurrent = 0,
|
||||
selected_item = nil,
|
||||
|
||||
commands = nil,
|
||||
}
|
||||
|
||||
function SelectMenu:new(o)
|
||||
@@ -46,10 +50,12 @@ function SelectMenu:new(o)
|
||||
o.page = 1
|
||||
o.current = 1
|
||||
o.oldcurrent = 0
|
||||
o.selected_item = nil
|
||||
-- increase spacing for DXG so we don't have more than 30 shortcuts
|
||||
if fb.bb:getHeight() == 1200 then
|
||||
o.spacing = 37
|
||||
end
|
||||
o:addAllCommands()
|
||||
return o
|
||||
end
|
||||
|
||||
@@ -62,74 +68,197 @@ function SelectMenu:getItemIndexByShortCut(c, perpage)
|
||||
end
|
||||
end
|
||||
|
||||
--[
|
||||
function SelectMenu:addAllCommands()
|
||||
self.commands = Commands:new{}
|
||||
|
||||
self.commands:add(KEY_FW_UP, nil, "",
|
||||
"previous item",
|
||||
function(sm)
|
||||
if sm.current == 1 then
|
||||
if sm.page > 1 then
|
||||
sm.current = sm.perpage
|
||||
sm.page = sm.page - 1
|
||||
sm.pagedirty = true
|
||||
end
|
||||
else
|
||||
sm.current = sm.current - 1
|
||||
sm.markerdirty = true
|
||||
end
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_FW_DOWN, nil, "",
|
||||
"next item",
|
||||
function(sm)
|
||||
if sm.current == sm.perpage then
|
||||
if sm.page < (sm.items / sm.perpage) then
|
||||
sm.current = 1
|
||||
sm.page = sm.page + 1
|
||||
sm.pagedirty = true
|
||||
end
|
||||
else
|
||||
if sm.page ~= math.floor(sm.items / sm.perpage) + 1
|
||||
or sm.current + (sm.page - 1) * sm.perpage < sm.items then
|
||||
sm.current = sm.current + 1
|
||||
sm.markerdirty = true
|
||||
end
|
||||
end
|
||||
end
|
||||
)
|
||||
self.commands:add({KEY_PGFWD, KEY_LPGFWD}, nil, "",
|
||||
"next page",
|
||||
function(sm)
|
||||
if sm.page < (sm.items / sm.perpage) then
|
||||
if sm.current + sm.page * sm.perpage > sm.items then
|
||||
sm.current = sm.items - sm.page * sm.perpage
|
||||
end
|
||||
sm.page = sm.page + 1
|
||||
sm.pagedirty = true
|
||||
else
|
||||
sm.current = sm.items - (sm.page - 1) * sm.perpage
|
||||
sm.markerdirty = true
|
||||
end
|
||||
end
|
||||
)
|
||||
self.commands:add({KEY_PGBCK, KEY_LPGBCK}, nil, "",
|
||||
"previous page",
|
||||
function(sm)
|
||||
if sm.page > 1 then
|
||||
sm.page = sm.page - 1
|
||||
sm.pagedirty = true
|
||||
else
|
||||
sm.current = 1
|
||||
sm.markerdirty = true
|
||||
end
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_FW_PRESS, nil, "",
|
||||
"select menu item",
|
||||
function(sm)
|
||||
if sm.last_shortcut < 30 then
|
||||
if sm.items == 0 then
|
||||
return "break"
|
||||
else
|
||||
self.selected_item = (sm.perpage * (sm.page - 1)
|
||||
+ sm.current)
|
||||
end
|
||||
end
|
||||
end
|
||||
)
|
||||
local KEY_Q_to_P = {}
|
||||
for i = KEY_Q, KEY_P do
|
||||
table.insert(KEY_Q_to_P, Keydef:new(i, nil, ""))
|
||||
end
|
||||
self.commands:addGroup("Q to P", KEY_Q_to_P,
|
||||
"Select menu item with Q to E key as shortcut",
|
||||
function(sm, keydef)
|
||||
sm.selected_item = sm:getItemIndexByShortCut(
|
||||
sm.item_shortcuts[ keydef.keycode - KEY_Q + 1 ], sm.perpage)
|
||||
end
|
||||
)
|
||||
local KEY_A_to_L = {}
|
||||
for i = KEY_A, KEY_L do
|
||||
table.insert(KEY_A_to_L, Keydef:new(i, nil, ""))
|
||||
end
|
||||
self.commands:addGroup("A to L", KEY_A_to_L,
|
||||
"Select menu 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)
|
||||
end
|
||||
)
|
||||
local KEY_Z_to_M = {}
|
||||
for i = KEY_Z, KEY_M do
|
||||
table.insert(KEY_Z_to_M, Keydef:new(i, nil, ""))
|
||||
end
|
||||
self.commands:addGroup("Z to M", KEY_Z_to_M,
|
||||
"Select menu 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_DEL, nil, "",
|
||||
"Select menu item with del key as shortcut",
|
||||
function(sm)
|
||||
sm.selected_item = sm:getItemIndexByShortCut("Del", sm.perpage)
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_DOT, nil, "",
|
||||
"Select menu item with dot key as shortcut",
|
||||
function(sm)
|
||||
sm.selected_item = sm:getItemIndexByShortCut(".", sm.perpage)
|
||||
end
|
||||
)
|
||||
self.commands:add({KEY_SYM, KEY_SLASH}, nil, "",
|
||||
"Select menu item with sym/slash key as shortcut",
|
||||
function(sm)
|
||||
-- DXG has slash after dot
|
||||
sm.selected_item = sm:getItemIndexByShortCut("Sym", sm.perpage)
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_ENTER, nil, "",
|
||||
"Select menu item with enter key as shortcut",
|
||||
function(sm)
|
||||
sm.selected_item = sm:getItemIndexByShortCut("Ent", sm.perpage)
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_BACK, nil, "",
|
||||
"Exit menu",
|
||||
function(sm)
|
||||
return "break"
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
function SelectMenu:clearCommands()
|
||||
self.commands = Commands:new{}
|
||||
|
||||
self.commands:add(KEY_BACK, nil, "",
|
||||
"Exit menu",
|
||||
function(sm)
|
||||
return "break"
|
||||
end)
|
||||
end
|
||||
|
||||
------------------------------------------------
|
||||
-- return the index of selected item
|
||||
--]
|
||||
------------------------------------------------
|
||||
function SelectMenu:choose(ypos, height)
|
||||
local perpage = math.floor(height / self.spacing) - 2
|
||||
local pagedirty = true
|
||||
local markerdirty = false
|
||||
|
||||
local prevItem = function ()
|
||||
if self.current == 1 then
|
||||
if self.page > 1 then
|
||||
self.current = perpage
|
||||
self.page = self.page - 1
|
||||
pagedirty = true
|
||||
end
|
||||
else
|
||||
self.current = self.current - 1
|
||||
markerdirty = true
|
||||
end
|
||||
end
|
||||
|
||||
local nextItem = function ()
|
||||
if self.current == perpage then
|
||||
if self.page < (self.items / perpage) then
|
||||
self.current = 1
|
||||
self.page = self.page + 1
|
||||
pagedirty = true
|
||||
end
|
||||
else
|
||||
if self.page ~= math.floor(self.items / perpage) + 1
|
||||
or self.current + (self.page-1)*perpage < self.items then
|
||||
self.current = self.current + 1
|
||||
markerdirty = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self.perpage = math.floor(height / self.spacing) - 2
|
||||
self.pagedirty = true
|
||||
self.markerdirty = false
|
||||
self.last_shortcut = 0
|
||||
|
||||
while true do
|
||||
local cface, cfhash = Font:getFaceAndHash(22)
|
||||
local tface, tfhash = Font:getFaceAndHash(25, Font.tfont)
|
||||
local fface, ffhash = Font:getFaceAndHash(16, Font.ffont)
|
||||
local cface = Font:getFace("cfont", 22)
|
||||
local tface = Font:getFace("tfont", 25)
|
||||
local fface = Font:getFace("ffont", 16)
|
||||
|
||||
if pagedirty then
|
||||
markerdirty = true
|
||||
if self.pagedirty then
|
||||
self.markerdirty = true
|
||||
-- draw menu title
|
||||
fb.bb:paintRect(0, ypos, fb.bb:getWidth(), self.title_H + 10, 0)
|
||||
fb.bb:paintRect(10, ypos + 10, fb.bb:getWidth() - 20, self.title_H, 5)
|
||||
|
||||
local x = 20
|
||||
local y = ypos + self.title_H
|
||||
renderUtf8Text(fb.bb, x, y, tface, tfhash, self.menu_title, true)
|
||||
renderUtf8Text(fb.bb, x, y, tface, self.menu_title, true)
|
||||
|
||||
-- draw items
|
||||
fb.bb:paintRect(0, ypos + self.title_H + 10, fb.bb:getWidth(), height - self.title_H, 0)
|
||||
if self.items == 0 then
|
||||
y = ypos + self.title_H + (self.spacing * 2)
|
||||
renderUtf8Text(fb.bb, 30, y, cface, cfhash,
|
||||
renderUtf8Text(fb.bb, 30, y, cface,
|
||||
"Oops... Bad news for you:", true)
|
||||
y = y + self.spacing
|
||||
renderUtf8Text(fb.bb, 30, y, cface, cfhash,
|
||||
renderUtf8Text(fb.bb, 30, y, cface,
|
||||
self.no_item_msg, true)
|
||||
markerdirty = false
|
||||
self.markerdirty = false
|
||||
self:clearCommands()
|
||||
else
|
||||
local c
|
||||
for c = 1, perpage do
|
||||
local i = (self.page - 1) * perpage + c
|
||||
for c = 1, self.perpage do
|
||||
local i = (self.page - 1) * self.perpage + c
|
||||
if i <= self.items then
|
||||
y = ypos + self.title_H + (self.spacing * c)
|
||||
|
||||
@@ -142,30 +271,32 @@ function SelectMenu:choose(ypos, height)
|
||||
if self.item_shortcuts[c] ~= nil and
|
||||
string.len(self.item_shortcuts[c]) == 3 then
|
||||
-- print "Del", "Sym and "Ent"
|
||||
renderUtf8Text(fb.bb, 13, y, fface, ffhash,
|
||||
renderUtf8Text(fb.bb, 13, y, fface,
|
||||
self.item_shortcuts[c], true)
|
||||
else
|
||||
renderUtf8Text(fb.bb, 18, y, self.sface, self.sfhash,
|
||||
renderUtf8Text(fb.bb, 18, y, self.sface,
|
||||
self.item_shortcuts[c], true)
|
||||
end
|
||||
|
||||
self.last_shortcut = c
|
||||
|
||||
renderUtf8Text(fb.bb, 50, y, cface, cfhash,
|
||||
renderUtf8Text(fb.bb, 50, y, cface,
|
||||
self.item_array[i], true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end -- EOF if i <= self.items
|
||||
end -- EOF for
|
||||
end -- EOF if
|
||||
|
||||
-- draw footer
|
||||
y = ypos + self.title_H + (self.spacing * perpage) + self.foot_H + 5
|
||||
y = ypos + self.title_H + (self.spacing * self.perpage)
|
||||
+ self.foot_H + 5
|
||||
x = (fb.bb:getWidth() / 2) - 50
|
||||
renderUtf8Text(fb.bb, x, y, fface, ffhash,
|
||||
"Page "..self.page.." of "..(math.floor(self.items / perpage)+1), true)
|
||||
renderUtf8Text(fb.bb, x, y, fface,
|
||||
"Page "..self.page.." of "..
|
||||
(math.ceil(self.items / self.perpage)), true)
|
||||
end
|
||||
|
||||
if markerdirty then
|
||||
if not pagedirty then
|
||||
if self.markerdirty then
|
||||
if not self.pagedirty then
|
||||
if self.oldcurrent > 0 then
|
||||
y = ypos + self.title_H + (self.spacing * self.oldcurrent) + 8
|
||||
fb.bb:paintRect(45, y, fb.bb:getWidth() - 60, 3, 0)
|
||||
@@ -175,72 +306,41 @@ function SelectMenu:choose(ypos, height)
|
||||
-- draw new marker line
|
||||
y = ypos + self.title_H + (self.spacing * self.current) + 8
|
||||
fb.bb:paintRect(45, y, fb.bb:getWidth() - 60, 3, 15)
|
||||
if not pagedirty then
|
||||
if not self.pagedirty then
|
||||
fb:refresh(1, 45, y, fb.bb:getWidth() - 60, 3)
|
||||
end
|
||||
self.oldcurrent = self.current
|
||||
markerdirty = false
|
||||
self.markerdirty = false
|
||||
end
|
||||
|
||||
if pagedirty then
|
||||
if self.pagedirty then
|
||||
fb:refresh(0, 0, ypos, fb.bb:getWidth(), height)
|
||||
pagedirty = false
|
||||
self.pagedirty = false
|
||||
end
|
||||
|
||||
local ev = input.waitForEvent()
|
||||
local ev = input.saveWaitForEvent()
|
||||
ev.code = adjustKeyEvents(ev)
|
||||
if ev.type == EV_KEY and ev.value == EVENT_VALUE_KEY_PRESS then
|
||||
local selected = nil
|
||||
if ev.code == KEY_FW_UP then
|
||||
prevItem()
|
||||
elseif ev.code == KEY_FW_DOWN then
|
||||
nextItem()
|
||||
elseif ev.code == KEY_PGFWD or ev.code == KEY_LPGFWD then
|
||||
if self.page < (self.items / perpage) then
|
||||
if self.current + self.page*perpage > self.items then
|
||||
self.current = self.items - self.page*perpage
|
||||
end
|
||||
self.page = self.page + 1
|
||||
pagedirty = true
|
||||
else
|
||||
self.current = self.items - (self.page-1)*perpage
|
||||
markerdirty = true
|
||||
end
|
||||
elseif ev.code == KEY_PGBCK or ev.code == KEY_LPGBCK then
|
||||
if self.page > 1 then
|
||||
self.page = self.page - 1
|
||||
pagedirty = true
|
||||
else
|
||||
self.current = 1
|
||||
markerdirty = true
|
||||
end
|
||||
elseif ev.code == KEY_FW_PRESS or ev.code == KEY_ENTER and self.last_shortcut < 30 then
|
||||
if self.items == 0 then
|
||||
return nil
|
||||
else
|
||||
return (perpage*(self.page-1) + self.current)
|
||||
end
|
||||
elseif ev.code >= KEY_Q and ev.code <= KEY_P then
|
||||
selected = self:getItemIndexByShortCut(self.item_shortcuts[ ev.code - KEY_Q + 1 ], perpage)
|
||||
elseif ev.code >= KEY_A and ev.code <= KEY_L then
|
||||
selected = self:getItemIndexByShortCut(self.item_shortcuts[ ev.code - KEY_A + 11], perpage)
|
||||
elseif ev.code >= KEY_Z and ev.code <= KEY_M then
|
||||
selected = self:getItemIndexByShortCut(self.item_shortcuts[ ev.code - KEY_Z + 21], perpage)
|
||||
elseif ev.code == KEY_DEL then
|
||||
selected = self:getItemIndexByShortCut("Del", perpage)
|
||||
elseif ev.code == KEY_DOT then
|
||||
selected = self:getItemIndexByShortCut(".", perpage)
|
||||
elseif ev.code == KEY_SYM or ev.code == KEY_SLASH then -- DXG has slash after dot
|
||||
selected = self:getItemIndexByShortCut("Sym", perpage)
|
||||
elseif ev.code == KEY_ENTER then
|
||||
selected = self:getItemIndexByShortCut("Ent", perpage)
|
||||
elseif ev.code == KEY_BACK then
|
||||
return nil
|
||||
keydef = Keydef:new(ev.code, getKeyModifier())
|
||||
print("key pressed: "..tostring(keydef))
|
||||
|
||||
command = self.commands:getByKeydef(keydef)
|
||||
if command ~= nil then
|
||||
print("command to execute: "..tostring(command))
|
||||
ret_code = command.func(self, keydef)
|
||||
else
|
||||
print("command not found: "..tostring(command))
|
||||
end
|
||||
if selected ~= nil then
|
||||
print("# selected "..selected)
|
||||
return selected
|
||||
|
||||
if ret_code == "break" then
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if self.selected_item ~= nil then
|
||||
print("# selected "..self.selected_item)
|
||||
return self.selected_item, self.item_array[self.selected_item]
|
||||
end
|
||||
end -- EOF if
|
||||
end -- EOF while
|
||||
return nil
|
||||
end
|
||||
|
||||
87
slider_watcher.c
Normal file
87
slider_watcher.c
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
KindlePDFViewer: power slider key event watcher
|
||||
Copyright (C) 2012 Qingping Hou <dave2008713@gamil.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/input.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
|
||||
#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;
|
||||
FILE *fp;
|
||||
char std_out[OUTPUT_SIZE] = "";
|
||||
struct input_event ev;
|
||||
__u16 key_code = 10000;
|
||||
|
||||
/* create the npipe if not exists */
|
||||
/*if(access(argv[1], F_OK)){*/
|
||||
/*printf("npipe %s not found, try to create it...\n", argv[1]);*/
|
||||
/*if(mkfifo(argv[1], 0777)) {*/
|
||||
/*printf("Create npipe %s failed!\n", argv[1]);*/
|
||||
/*}*/
|
||||
/*}*/
|
||||
|
||||
/* 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));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* initialize event struct */
|
||||
ev.type = EV_KEY;
|
||||
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);
|
||||
|
||||
/* fill event struct */
|
||||
gettimeofday(&ev.time, NULL);
|
||||
if(std_out[0] == 'g') {
|
||||
ev.code = CODE_IN_SAVER;
|
||||
} else if(std_out[0] == 'o') {
|
||||
ev.code = CODE_OUT_SAVER;
|
||||
} else {
|
||||
printf("Unrecognized event.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* generate event */
|
||||
ret = write(fd, &ev, sizeof(struct input_event));
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
BIN
test/2col.pdf
Normal file
BIN
test/2col.pdf
Normal file
Binary file not shown.
BIN
test/tall.pdf
Normal file
BIN
test/tall.pdf
Normal file
Binary file not shown.
1204
unireader.lua
1204
unireader.lua
File diff suppressed because it is too large
Load Diff
26
util.c
26
util.c
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
@@ -28,6 +29,19 @@ static int gettime(lua_State *L) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
static int util_sleep(lua_State *L) {
|
||||
unsigned int seconds = luaL_optint(L, 1, 0);
|
||||
sleep(seconds);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int util_usleep(lua_State *L) {
|
||||
useconds_t useconds = luaL_optint(L, 1, 0);
|
||||
usleep(useconds);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Turn UTF-8 char code to Unicode */
|
||||
static int utf8charcode(lua_State *L) {
|
||||
size_t len;
|
||||
const char* utf8char = luaL_checklstring(L, 1, &len);
|
||||
@@ -46,9 +60,21 @@ static int utf8charcode(lua_State *L) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int isEmulated(lua_State *L) {
|
||||
#ifdef EMULATE_READER
|
||||
lua_pushinteger(L, 1);
|
||||
#else
|
||||
lua_pushinteger(L, 0);
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const struct luaL_Reg util_func[] = {
|
||||
{"gettime", gettime},
|
||||
{"sleep", util_sleep},
|
||||
{"usleep", util_usleep},
|
||||
{"utf8charcode", utf8charcode},
|
||||
{"isEmulated", isEmulated},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
263
widget.lua
Normal file
263
widget.lua
Normal file
@@ -0,0 +1,263 @@
|
||||
require "rendertext"
|
||||
require "graphics"
|
||||
require "image"
|
||||
|
||||
--[[
|
||||
This is a (useless) generic Widget interface
|
||||
|
||||
widgets can be queried about their size and can be paint.
|
||||
that's it for now. Probably we need something more elaborate
|
||||
later.
|
||||
]]
|
||||
Widget = {
|
||||
dimen = { w = 0, h = 0},
|
||||
}
|
||||
|
||||
function Widget:new(o)
|
||||
o = o or {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end
|
||||
|
||||
function Widget:getSize()
|
||||
return self.dimen
|
||||
end
|
||||
|
||||
function Widget:paintTo(bb, x, y)
|
||||
end
|
||||
|
||||
function Widget:free()
|
||||
end
|
||||
|
||||
--[[
|
||||
WidgetContainer is a container for another Widget
|
||||
]]
|
||||
WidgetContainer = Widget:new()
|
||||
|
||||
function WidgetContainer:free()
|
||||
for _, widget in ipairs(self) do
|
||||
widget:free()
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
CenterContainer centers its content (1 widget) within its own dimensions
|
||||
]]
|
||||
CenterContainer = WidgetContainer:new()
|
||||
|
||||
function CenterContainer:paintTo(bb, x, y)
|
||||
local contentSize = self[1]:getSize()
|
||||
if contentSize.w > self.dimen.w or contentSize.h > self.dimen.h then
|
||||
-- throw error?
|
||||
return
|
||||
end
|
||||
self[1]:paintTo(bb,
|
||||
x + (self.dimen.w - contentSize.w)/2,
|
||||
y + (self.dimen.h - contentSize.h)/2)
|
||||
end
|
||||
|
||||
--[[
|
||||
A FrameContainer is some graphics content (1 widget) that is surrounded by a frame
|
||||
]]
|
||||
FrameContainer = WidgetContainer:new({
|
||||
background = nil,
|
||||
color = 15,
|
||||
margin = 0,
|
||||
bordersize = 2,
|
||||
padding = 5,
|
||||
})
|
||||
|
||||
function FrameContainer:getSize()
|
||||
local content_size = self[1]:getSize()
|
||||
return {
|
||||
w = content_size.w + ( self.margin + self.bordersize + self.padding ) * 2,
|
||||
h = content_size.h + ( self.margin + self.bordersize + self.padding ) * 2
|
||||
}
|
||||
end
|
||||
|
||||
function FrameContainer:paintTo(bb, x, y)
|
||||
local my_size = self:getSize()
|
||||
|
||||
if self.background then
|
||||
bb:paintRect(x, y, my_size.w, my_size.h, self.background)
|
||||
end
|
||||
if self.bordersize > 0 then
|
||||
bb:paintBorder(x + self.margin, y + self.margin,
|
||||
my_size.w - self.margin * 2, my_size.h - self.margin * 2,
|
||||
self.bordersize, self.color)
|
||||
end
|
||||
self[1]:paintTo(bb,
|
||||
x + self.margin + self.bordersize + self.padding,
|
||||
y + self.margin + self.bordersize + self.padding)
|
||||
end
|
||||
|
||||
--[[
|
||||
A TextWidget puts a string on a single line
|
||||
]]
|
||||
TextWidget = Widget:new({
|
||||
text = nil,
|
||||
face = nil,
|
||||
color = 15,
|
||||
_bb = nil,
|
||||
_length = 0,
|
||||
_maxlength = 1200,
|
||||
})
|
||||
|
||||
function TextWidget:_render()
|
||||
local h = self.face.size * 1.5
|
||||
self._bb = Blitbuffer.new(self._maxlength, h)
|
||||
self._length = renderUtf8Text(self._bb, 0, h*.7, self.face, self.text, self.color)
|
||||
end
|
||||
|
||||
function TextWidget:getSize()
|
||||
if not self._bb then
|
||||
self:_render()
|
||||
end
|
||||
return { w = self._length, h = self._bb:getHeight() }
|
||||
end
|
||||
|
||||
function TextWidget:paintTo(bb, x, y)
|
||||
if not self._bb then
|
||||
self:_render()
|
||||
end
|
||||
bb:blitFrom(self._bb, x, y, 0, 0, self._length, self._bb:getHeight())
|
||||
end
|
||||
|
||||
function TextWidget:free()
|
||||
if self._bb then
|
||||
self._bb:free()
|
||||
self._bb = nil
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
ImageWidget shows an image from a file
|
||||
]]
|
||||
ImageWidget = Widget:new({
|
||||
file = nil,
|
||||
_bb = nil
|
||||
})
|
||||
|
||||
function ImageWidget:_render()
|
||||
local itype = string.lower(string.match(self.file, ".+%.([^.]+)") or "")
|
||||
if itype == "jpeg" or itype == "jpg" then
|
||||
self._bb = Image.fromJPEG(self.file)
|
||||
elseif itype == "png" then
|
||||
self._bb = Image.fromPNG(self.file)
|
||||
end
|
||||
end
|
||||
|
||||
function ImageWidget:getSize()
|
||||
if not self._bb then
|
||||
self:_render()
|
||||
end
|
||||
return { w = self._bb:getWidth(), h = self._bb:getHeight() }
|
||||
end
|
||||
|
||||
function ImageWidget:paintTo(bb, x, y)
|
||||
local size = self:getSize()
|
||||
bb:blitFrom(self._bb, x, y, 0, 0, size.w, size.h)
|
||||
end
|
||||
|
||||
function ImageWidget:free()
|
||||
if self._bb then
|
||||
self._bb:free()
|
||||
self._bb = nil
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
A Layout widget that puts objects besides each others
|
||||
]]
|
||||
HorizontalGroup = WidgetContainer:new({
|
||||
align = "center",
|
||||
_size = nil,
|
||||
})
|
||||
|
||||
function HorizontalGroup:getSize()
|
||||
if not self._size then
|
||||
self._size = { w = 0, h = 0 }
|
||||
self._offsets = { }
|
||||
for i, widget in ipairs(self) do
|
||||
local w_size = widget:getSize()
|
||||
self._offsets[i] = {
|
||||
x = self._size.w,
|
||||
y = w_size.h
|
||||
}
|
||||
self._size.w = self._size.w + w_size.w
|
||||
if w_size.h > self._size.h then
|
||||
self._size.h = w_size.h
|
||||
end
|
||||
end
|
||||
end
|
||||
return self._size
|
||||
end
|
||||
|
||||
function HorizontalGroup:paintTo(bb, x, y)
|
||||
local size = self:getSize()
|
||||
|
||||
for i, widget in ipairs(self) do
|
||||
if self.align == "center" then
|
||||
widget:paintTo(bb, x + self._offsets[i].x, y + (size.h - self._offsets[i].y) / 2)
|
||||
elseif self.align == "top" then
|
||||
widget:paintTo(bb, x + self._offsets[i].x, y)
|
||||
elseif self.align == "bottom" then
|
||||
widget:paintTo(bb, x + self._offsets[i].x, y + size.h - self._offsets[i].y)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function HorizontalGroup:free()
|
||||
self._size = nil
|
||||
self._offsets = {}
|
||||
WidgetContainer.free(self)
|
||||
end
|
||||
|
||||
--[[
|
||||
A Layout widget that puts objects under each other
|
||||
]]
|
||||
VerticalGroup = WidgetContainer:new({
|
||||
align = "center",
|
||||
_size = nil,
|
||||
_offsets = {}
|
||||
})
|
||||
|
||||
function VerticalGroup:getSize()
|
||||
if not self._size then
|
||||
self._size = { w = 0, h = 0 }
|
||||
self._offsets = { }
|
||||
for i, widget in ipairs(self) do
|
||||
local w_size = widget:getSize()
|
||||
self._offsets[i] = {
|
||||
x = w_size.w,
|
||||
y = self._size.h,
|
||||
}
|
||||
self._size.h = self._size.h + w_size.h
|
||||
if w_size.w > self._size.w then
|
||||
self._size.w = w_size.w
|
||||
end
|
||||
end
|
||||
end
|
||||
return self._size
|
||||
end
|
||||
|
||||
function VerticalGroup:paintTo(bb, x, y)
|
||||
local size = self:getSize()
|
||||
|
||||
for i, widget in ipairs(self) do
|
||||
if self.align == "center" then
|
||||
widget:paintTo(bb, x + (size.w - self._offsets[i].x) / 2, y + self._offsets[i].y)
|
||||
elseif self.align == "left" then
|
||||
widget:paintTo(bb, x, y + self._offsets[i].y)
|
||||
elseif self.align == "right" then
|
||||
widget:paintTo(bb, x + size.w - self._offsets[i].x, y + self._offsets[i].y)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function VerticalGroup:free()
|
||||
self._size = nil
|
||||
self._offsets = {}
|
||||
WidgetContainer.free(self)
|
||||
end
|
||||
Reference in New Issue
Block a user