diff --git a/.gitignore b/.gitignore index 7dbe43999..62312b73f 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ crash.log data fonts kpdfview +slider_watcher *.o kindlepdfviewer-*.zip @@ -24,3 +25,17 @@ kpvcrlib/CMakeCache.txt kpvcrlib/CMakeFiles/ kpvcrlib/cmake_install.cmake kpvcrlib/Makefile + +popen-noshell/libpopen_noshell.a +popen-noshell/*.o +popen-noshell/.svn/ +popen-noshell/CREDITS +popen-noshell/Makefile +popen-noshell/README +popen-noshell/performance_tests/ +popen-noshell/popen_noshell.c +popen-noshell/popen_noshell.h +popen-noshell/popen_noshell_examples.c +popen-noshell/popen_noshell_tests.c +popen-noshell/popen_noshell_tests.cpp + diff --git a/Makefile b/Makefile index 9b206e19c..17a987b01 100644 --- a/Makefile +++ b/Makefile @@ -11,15 +11,18 @@ CRENGINEDIR=$(KPVCRLIBDIR)/crengine FREETYPEDIR=$(MUPDFDIR)/thirdparty/freetype-2.4.10 LFSDIR=luafilesystem +POPENNSDIR=popen-noshell + # must point to directory with *.ttf fonts for crengine TTF_FONTS_DIR=$(MUPDFDIR)/fonts # set this to your ARM cross compiler: -HOST:=arm-none-linux-gnueabi -CC:=$(HOST)-gcc -CXX:=$(HOST)-g++ -STRIP:=$(HOST)-strip +CHOST?=arm-none-linux-gnueabi +CC:=$(CHOST)-gcc +CXX:=$(CHOST)-g++ +STRIP:=$(CHOST)-strip +AR:=$(CHOST)-ar ifdef SBOX_UNAME_MACHINE CC:=gcc CXX:=g++ @@ -27,12 +30,16 @@ endif HOSTCC:=gcc HOSTCXX:=g++ -CFLAGS:=-O3 $(SYSROOT) -CXXFLAGS:=-O3 $(SYSROOT) +# Base CFLAGS, without arch. We'll need it for luajit, because its Makefiles do some tricky stuff to differentiate HOST/TARGET +BASE_CFLAGS:=-O2 -ffast-math -pipe -fomit-frame-pointer -fno-stack-protector -U_FORTIFY_SOURCE +# Use this for debugging: +#BASE_CFLAGS:=-O0 -g +ARM_ARCH:=-march=armv6j -mtune=arm1136jf-s -mfpu=vfp +HOST_ARCH:=-march=native +HOSTCFLAGS:=$(HOST_ARCH) $(BASE_CFLAGS) +CFLAGS:=$(BASE_CFLAGS) +CXXFLAGS:=$(BASE_CFLAGS) -fno-use-cxa-atexit LDFLAGS:=-Wl,-O1 -Wl,--as-needed -ARM_CFLAGS:=-march=armv6j -mtune=arm1136jf-s -mfpu=vfp -# use this for debugging: -#CFLAGS:=-O0 -g DYNAMICLIBSTDCPP:=-lstdc++ ifdef STATICLIBSTDCPP @@ -56,9 +63,11 @@ ifdef EMULATE_READER ifeq "$(shell uname -s -m)" "Darwin x86_64" EMU_LDFLAGS += -pagezero_size 10000 -image_base 100000000 endif + CFLAGS+= $(HOST_ARCH) + CXXFLAGS+= $(HOST_ARCH) else - CFLAGS+= $(ARM_CFLAGS) - CXXFLAGS+= $(ARM_CFLAGS) + CFLAGS+= $(ARM_ARCH) + CXXFLAGS+= $(ARM_ARCH) endif # standard includes @@ -88,16 +97,20 @@ THIRDPARTYLIBS := $(MUPDFLIBDIR)/libfreetype.a \ LUALIB := $(LUADIR)/src/libluajit.a -all:kpdfview +POPENNSLIB := $(POPENNSDIR)/libpopen_noshell.a + +all: kpdfview -kpdfview: kpdfview.o einkfb.o pdf.o blitbuffer.o drawcontext.o input.o util.o ft.o lfs.o mupdfimg.o $(MUPDFLIBS) $(THIRDPARTYLIBS) $(LUALIB) djvu.o $(DJVULIBS) cre.o $(CRENGINELIBS) +kpdfview: kpdfview.o einkfb.o pdf.o blitbuffer.o drawcontext.o input.o $(POPENNSLIB) util.o ft.o lfs.o mupdfimg.o $(MUPDFLIBS) $(THIRDPARTYLIBS) $(LUALIB) djvu.o $(DJVULIBS) cre.o $(CRENGINELIBS) $(CC) \ + $(CFLAGS) \ kpdfview.o \ einkfb.o \ pdf.o \ blitbuffer.o \ drawcontext.o \ input.o \ + $(POPENNSLIB) \ util.o \ ft.o \ lfs.o \ @@ -110,10 +123,14 @@ kpdfview: kpdfview.o einkfb.o pdf.o blitbuffer.o drawcontext.o input.o util.o ft cre.o \ $(CRENGINELIBS) \ $(STATICLIBSTDCPP) \ - -o kpdfview -lm -ldl -lpthread $(EMU_LDFLAGS) $(DYNAMICLIBSTDCPP) + $(LDFLAGS) \ + -o $@ -lm -ldl -lpthread $(EMU_LDFLAGS) $(DYNAMICLIBSTDCPP) -slider_watcher: slider_watcher.c - $(CC) $(CFLAGS) $< -o $@ +slider_watcher.o: %.o: %.c + $(CC) -c $(CFLAGS) $< -o $@ + +slider_watcher: slider_watcher.o $(POPENNSLIB) + $(CC) $(CFLAGS) slider_watcher.o $(POPENNSLIB) -o $@ ft.o: %.o: %.c $(THIRDPARTYLIBS) $(CC) -c $(KPDFREADER_CFLAGS) -I$(FREETYPEDIR)/include -I$(MUPDFDIR)/fitz $< -o $@ @@ -131,7 +148,7 @@ lfs.o: $(LFSDIR)/src/lfs.c $(CC) -c $(CFLAGS) -I$(LUADIR)/src -I$(LFSDIR)/src $(LFSDIR)/src/lfs.c -o $@ fetchthirdparty: - -rm -Rf mupdf/thirdparty + rm -rf mupdf/thirdparty test -d mupdf && (cd mupdf; git checkout .) || echo warn: mupdf folder not found test -d $(LUADIR) && (cd $(LUADIR); git checkout .) || echo warn: $(LUADIR) folder not found git submodule init @@ -153,58 +170,66 @@ fetchthirdparty: patch -N -p0 < ../../../kpvcrlib/jpeg_decompress_struct_size.patch # MuPDF patch: use external fonts cd mupdf && patch -N -p1 < ../mupdf.patch + test -f popen-noshell/popen_noshell.c || svn co http://popen-noshell.googlecode.com/svn/trunk/ popen-noshell + # popen_noshell patch: Make it build on recent TCs, and implement a simple Makefile for building it as a static lib + cd popen-noshell && test -f Makefile || patch -N -p0 < popen_noshell-buildfix.patch clean: - -rm -f *.o kpdfview slider_watcher + rm -f *.o kpdfview slider_watcher cleanthirdparty: - -make -C $(LUADIR) clean - -make -C $(MUPDFDIR) build="release" clean - -make -C $(CRENGINEDIR)/thirdparty/antiword clean - test -d $(CRENGINEDIR)/thirdparty/chmlib && make -C $(CRENGINEDIR)/thirdparty/chmlib clean || echo warn: chmlib folder not found - test -d $(CRENGINEDIR)/thirdparty/libpng && (make -C $(CRENGINEDIR)/thirdparty/libpng clean) || echo warn: chmlib folder not found - test -d $(CRENGINEDIR)/crengine && (make -C $(CRENGINEDIR)/crengine clean) || echo warn: chmlib folder not found - test -d $(KPVCRLIBDIR) && (make -C $(KPVCRLIBDIR) clean) || echo warn: chmlib folder not found - -rm -rf $(DJVUDIR)/build - -rm -f $(MUPDFDIR)/fontdump.host - -rm -f $(MUPDFDIR)/cmapdump.host + $(MAKE) -C $(LUADIR) CC="$(HOSTCC)" HOST_CC="$(HOSTCC) -m32" CROSS="$(CHOST)-" clean + $(MAKE) -C $(MUPDFDIR) build="release" clean + $(MAKE) -C $(CRENGINEDIR)/thirdparty/antiword clean + test -d $(CRENGINEDIR)/thirdparty/chmlib && $(MAKE) -C $(CRENGINEDIR)/thirdparty/chmlib clean || echo warn: chmlib folder not found + test -d $(CRENGINEDIR)/thirdparty/libpng && ($(MAKE) -C $(CRENGINEDIR)/thirdparty/libpng clean) || echo warn: chmlib folder not found + test -d $(CRENGINEDIR)/crengine && ($(MAKE) -C $(CRENGINEDIR)/crengine clean) || echo warn: chmlib folder not found + test -d $(KPVCRLIBDIR) && ($(MAKE) -C $(KPVCRLIBDIR) clean) || echo warn: chmlib folder not found + rm -rf $(DJVUDIR)/build + rm -f $(MUPDFDIR)/fontdump.host + rm -f $(MUPDFDIR)/cmapdump.host + $(MAKE) -C $(POPENNSDIR) clean $(MUPDFDIR)/fontdump.host: - make -C mupdf build="release" CC="$(HOSTCC)" $(MUPDFTARGET)/fontdump + $(MAKE) -C mupdf build="release" CC="$(HOSTCC)" CFLAGS="$(HOSTCFLAGS) -I../mupdf/fitz -I../mupdf/pdf" $(MUPDFTARGET)/fontdump cp -a $(MUPDFLIBDIR)/fontdump $(MUPDFDIR)/fontdump.host - make -C mupdf clean + $(MAKE) -C mupdf clean $(MUPDFDIR)/cmapdump.host: - make -C mupdf build="release" CC="$(HOSTCC)" $(MUPDFTARGET)/cmapdump + $(MAKE) -C mupdf build="release" CC="$(HOSTCC)" CFLAGS="$(HOSTCFLAGS) -I../mupdf/fitz -I../mupdf/pdf" $(MUPDFTARGET)/cmapdump cp -a $(MUPDFLIBDIR)/cmapdump $(MUPDFDIR)/cmapdump.host - make -C mupdf clean + $(MAKE) -C mupdf clean $(MUPDFLIBS) $(THIRDPARTYLIBS): $(MUPDFDIR)/cmapdump.host $(MUPDFDIR)/fontdump.host # build only thirdparty libs, libfitz and pdf utils, which will care for libmupdf.a being built - CFLAGS="$(CFLAGS) -DNOBUILTINFONT" make -C mupdf build="release" CC="$(CC)" CMAPDUMP=cmapdump.host FONTDUMP=fontdump.host MUPDF= MU_APPS= BUSY_APP= XPS_APPS= verbose=1 + CFLAGS="$(CFLAGS) -DNOBUILTINFONT" $(MAKE) -C mupdf build="release" CC="$(CC)" CMAPDUMP=cmapdump.host FONTDUMP=fontdump.host MUPDF= MU_APPS= BUSY_APP= XPS_APPS= verbose=1 $(DJVULIBS): - -mkdir $(DJVUDIR)/build + mkdir -p $(DJVUDIR)/build ifdef EMULATE_READER cd $(DJVUDIR)/build && ../configure --disable-desktopfiles --disable-shared --enable-static --disable-xmltools --disable-largefile else - cd $(DJVUDIR)/build && ../configure --disable-desktopfiles --disable-shared --enable-static --host=$(HOST) --disable-xmltools --disable-largefile + cd $(DJVUDIR)/build && ../configure --disable-desktopfiles --disable-shared --enable-static --host=$(CHOST) --disable-xmltools --disable-largefile endif - make -C $(DJVUDIR)/build + $(MAKE) -C $(DJVUDIR)/build $(CRENGINELIBS): cd $(KPVCRLIBDIR) && rm -rf CMakeCache.txt CMakeFiles && \ CFLAGS="$(CFLAGS)" CXXFLAGS="$(CXXFLAGS)" CC="$(CC)" CXX="$(CXX)" LDFLAGS="$(LDFLAGS)" cmake . && \ - make + $(MAKE) $(LUALIB): ifdef EMULATE_READER - make -C $(LUADIR) + $(MAKE) -C $(LUADIR) else - make -C $(LUADIR) CC="$(HOSTCC)" HOST_CC="$(HOSTCC) -m32" CROSS="$(HOST)-" TARGET_FLAGS="$(SYSROOT) -DLUAJIT_NO_LOG2 -DLUAJIT_NO_EXP2" + # To recap: build its TARGET_CC from CROSS+CC, so we need HOSTCC in CC. Build its HOST/TARGET_CFLAGS based on CFLAGS, so we need a neutral CFLAGS without arch + $(MAKE) -C $(LUADIR) CC="$(HOSTCC)" HOST_CC="$(HOSTCC) -m32" CFLAGS="$(BASE_CFLAGS)" HOST_CFLAGS="$(HOSTCFLAGS)" TARGET_CFLAGS="$(CFLAGS)" CROSS="$(CHOST)-" TARGET_FLAGS="-DLUAJIT_NO_LOG2 -DLUAJIT_NO_EXP2" endif -thirdparty: $(MUPDFLIBS) $(THIRDPARTYLIBS) $(LUALIB) $(DJVULIBS) $(CRENGINELIBS) +$(POPENNSLIB): + $(MAKE) -C $(POPENNSDIR) CC="$(CC)" AR="$(AR)" + +thirdparty: $(MUPDFLIBS) $(THIRDPARTYLIBS) $(LUALIB) $(DJVULIBS) $(CRENGINELIBS) $(POPENNSLIB) INSTALL_DIR=kindlepdfviewer @@ -215,8 +240,8 @@ customupdate: all # ensure that build binary is for ARM file kpdfview | grep ARM || exit 1 $(STRIP) --strip-unneeded kpdfview - -rm kindlepdfviewer-$(VERSION).zip - rm -Rf $(INSTALL_DIR) + rm -f kindlepdfviewer-$(VERSION).zip + rm -rf $(INSTALL_DIR) mkdir -p $(INSTALL_DIR)/{history,screenshots} echo $(VERSION) > $(INSTALL_DIR)/git-rev cp -p README.md COPYING kpdfview $(LUA_FILES) $(INSTALL_DIR) @@ -226,5 +251,5 @@ customupdate: all cp -r resources $(INSTALL_DIR) mkdir $(INSTALL_DIR)/fonts/host zip -9 -r kindlepdfviewer-$(VERSION).zip $(INSTALL_DIR) launchpad/ kite/ - rm -Rf $(INSTALL_DIR) + rm -rf $(INSTALL_DIR) @echo "copy kindlepdfviewer-$(VERSION).zip to /mnt/us/customupdates and install with shift+shift+I" diff --git a/commands.lua b/commands.lua index 4957552be..bbce7bc91 100644 --- a/commands.lua +++ b/commands.lua @@ -168,7 +168,7 @@ function Commands:new(obj) --os.execute("echo 'screensaver in' >> /mnt/us/event_test.txt") if G_charging_mode == false and G_screen_saver_mode == false then Screen:saveCurrentBB() - InfoMessage:show("Going into screensaver... ", 0) + InfoMessage:inform("Going into screensaver... ", nil, 0, MSG_AUX) Screen.kpv_rotation_mode = Screen.cur_rotation_mode fb:setOrientation(Screen.native_rotation_mode) util.sleep(1) @@ -199,7 +199,7 @@ function Commands:new(obj) Screen:saveCurrentBB() Screen.kpv_rotation_mode = Screen.cur_rotation_mode fb:setOrientation(Screen.native_rotation_mode) - InfoMessage:show("Going into USB mode... ", 0) + InfoMessage:inform("Going into USB mode... ", nil, 0, MSG_AUX) util.sleep(1) os.execute("killall -cont cvm") end diff --git a/dialog.lua b/dialog.lua index 632b9b146..b46303d90 100644 --- a/dialog.lua +++ b/dialog.lua @@ -1,7 +1,43 @@ require "widget" require "font" +-- some definitions for developers +MSG_AUX = 1 +MSG_WARN = 2 +MSG_ERROR = 3 +MSG_CONFIRM = 4 +MSG_BUG = 5 + InfoMessage = { + InfoMethod = { --[[ + The items define how to inform user about various types of events, the values should be 0..3: + the lowest bit is responcible for showing popup windows, + while the 2nd bit allows using TTS-voice messages. + The current default values {1,1,1,1,1} correspond to previous kpdfviewer-settings + when every message is shown in popup window. ]] + 1, -- auxiliary messages = 0 or 2 (nothing or TTS) + 1, -- warnings = 1 or 2 (popup or TTS) + 1, -- errors = 1 or 3 (popup or popup & TTS) + 1, -- confirmations (must not be silent!) = 1 or 3 (popup or popup & TTS) + 1, -- bugs (must not be silent!) = 1 or 3 (popup or popup & TTS) + }, + -- images for various message types + Images = { + "resources/info-aux.png", + "resources/info-warn.png", + "resources/info-error.png", + "resources/info-confirm.png", + "resources/info-bug.png", + }, + ImageFile = "resources/info-warn.png", + -- TTS-related parameters + TTSspeed = nil, + SoundVolume = nil, + + --[[ as Kindle3-volume has nonlinear scale, one need to define the 'VolumeLevels'-values + TODO: test whether VolumeLevels are different for other sound-equipped Kindle models (K2, KDX) + by running self.VolumeLevels = self:getVolumeLevels() ]] + VolumeLevels = { 0, 1, 2, 4, 6, 7, 14, 21, 28, 35, 42, 49, 56, 63, 70, 77} } function InfoMessage:show(text,refresh_mode) @@ -14,7 +50,7 @@ function InfoMessage:show(text,refresh_mode) HorizontalGroup:new({ align = "center", ImageWidget:new({ - file = "resources/info-i.png" + file = self.ImageFile }), Widget:new({ dimen = { w = 10, h = 0 } @@ -54,3 +90,191 @@ function showInfoMsgWithDelay(text, msec, refresh_mode) Screen:restoreFromSavedBB() fb:refresh(refresh_mode) end + +--[[ Unified function to inform user about smth. It is generally intended to replace showInfoMsgWithDelay() & InfoMessage:show(). +Since the former function is used in Lua-code more often than the latter, I kept here the same sequence of parameters as used in +showInfoMsgWithDelay() + added two not obligatory parameters (see description below). + +Thus, this trick allows multiple text replaces thoughout the whole Lua-code: 'showInfoMsgWithDelay(' to 'InfoMessage:inform(' +Yet, it would be much better to accompany such replaces by adding the 'message_importance' and, if needed, by +'alternative_voice_message' that is not so restricted in length as 'text' + +But replacing InfoMessage:show(...) by InfoMessage:inform(...) MUST be accompanied by adding the 2nd parameter -- either msec=nil or msec=0. +Adding new parameters -- 'message_importance' & 'alternative_voice_message' -- is also appreciated. + +Brief description of the function parameters +-- text : is the text message for visual and (if 'alternative_voice_message' isn't defined) voice notification +-- msec : parameter to define visual notification method + msec=0: one calls InfoMessage:show(), otherwise one calls showInfoMsgWithDelay() + msec>0: with either predefines msec-value or + msec<0: autocalculated from the text length: one may eventually add user-configurable + parameter 'reading_speed' that would define the popup exposition for this regime +-- message_importance : parameter separating various messages on + 1 - not obligatory messages that might be readily avoided + 2 - warnings (important messages) + 3 - errors + 4 - confirmations + 5 - bugs +-- alternative_voice_message: not obligatory parameter that allows to send longer messages to TTS-engine + if not defined, the default 'text' will be TTS-voiced +]] + +function InfoMessage:inform(text, msec, refresh_mode, message_importance, alternative_voice_message) + -- temporary test for 'message_importance'; it might be further removed as soon + -- as every message will be properly marked by 'importance' + if not message_importance then message_importance = 5 end + local popup, voice = InfoMessage:getMethodForEvent(message_importance) + if voice then + alternative_voice_message = alternative_voice_message or text + say(alternative_voice_message) + -- here one may set pause -- it might be useful only if one sound message + -- is directly followed by another, otherwise it's just wasting of time. + --[[ if msec and msec ~=0 then + -- pause = 0.5sec + 40ms/character * string.len() / normalized_voice_speed + util.usleep(500000 + 40*alternative_voice_message:len*10000/self.TTSspeed) + end ]] + end + if not popup then return end -- to avoid drawing popup window + self.ImageFile = self.Images[message_importance] -- select proper image for window + if not msec or msec == 0 then + InfoMessage:show(text, refresh_mode) + else + if msec < 0 then msec = 500 + string.len(text) * 50 end + showInfoMsgWithDelay(text, msec, refresh_mode) + end +end + +function InfoMessage:getMethodForEvent(event) + local popup, voice = true, true + if self.InfoMethod[event] %2 == 0 then popup = false end + if self.InfoMethod[event] < 2 then voice = false end + return popup, voice +end + +-- GUI-methods for user to choose the way to inform about various events -- + +function InfoMessage:chooseMethodForEvent(event) + local popup, voice = self:getMethodForEvent(event) + local items_menu = SelectMenu:new{ + menu_title = "Choose the way how to inform you", + item_array = {"Avoid any notifications", + "Show popup window", + "Use TTS-voice", + "Popup window and TTS-voice", + }, + current_entry = (popup and 1 or 0) + (voice and 1 or 0) * 2, + } + local item_no = items_menu:choose(0, fb.bb:getHeight()) + if item_no then + self.InfoMethod[event] = item_no - 1 + -- just to illustrate the way how the selected method works; might be removed + self:inform("Event = "..event..", Method = "..self.InfoMethod[event], -1500, 1, event, + "You have chosen the method number "..self.InfoMethod[event].." for the event item number "..event) + end +end + +function InfoMessage:chooseEventForMethod(event) + event = event or 0 + local item_no = 0 + local event_list = { + "Messages (e.g. 'Scanning folder...')", + "Warnings (e.g. 'Already first jump!')", + "Errors (e.g. 'Zip contains improper content!')", + "Confirmations (e.g. 'Press Y to confirm deleting')", + "Bugs", + } + while item_no ~= event and item_no < #event_list do + item_no = item_no + 1 + end + local event_menu = SelectMenu:new{ + menu_title = "Select the event type to tune", + item_array = event_list, + current_entry = item_no - 1, + } + item_no = event_menu:choose(0, G_height) + return item_no +end + +function InfoMessage:chooseNotificatonMethods() + local event = 1 -- default: auxiliary messages + while event do + event = self:chooseEventForMethod(event) + if event then self:chooseMethodForEvent(event) end + end +end + +---------------- audio-related functions ---------------- + +function InfoMessage:incrTTSspeed(direction) -- either +1 or -1 + -- make sure new TTS-speed is within reasonable range + self.TTSspeed = math.max(self.TTSspeed + direction * 20, 40) -- min = 40% + self.TTSspeed = math.min(self.TTSspeed, 200) -- max = 200% + -- set new value & give an example for more convenient tuning + os.execute('lipc-set-prop com.lab126.tts TtsISpeed '..self.TTSspeed) + say("The current voice speed is "..self.TTSspeed.." percent.") +end + +function InfoMessage:getTTSspeed() + local tmp = io.popen('lipc-get-prop com.lab126.tts TtsISpeed', "r") + local speed = tmp:read("*number") + tmp:close() + return speed or 100 -- nominal TTS-speed +end + +function InfoMessage:incrSoundVolume(direction) -- either +1 or -1 + -- make sure that new volume is within reasonable range + self.SoundVolume = math.max(self.SoundVolume + direction, 1) + self.SoundVolume = math.min(self.SoundVolume, #self.VolumeLevels) + -- set new value & give an example for more convenient tuning + os.execute('lipc-set-prop com.lab126.audio Volume '..(self.SoundVolume-1)) + -- that is not exactly the volume percents, but more conventional values, + -- than abstract units returned by 'lipc-get-prop com.lab126.audio Volume' + local percents = math.floor(100*self.VolumeLevels[self.SoundVolume]/self.VolumeLevels[#self.VolumeLevels]) + say("The current sound volume is "..percents.." percent.") +end + +function InfoMessage:getSoundVolume() + local tmp = io.popen('lipc-get-prop com.lab126.audio Volume', "r") + local volume = tmp:read("*number") + tmp:close() + local i = 1 + while self.VolumeLevels[i] < volume and i < #self.VolumeLevels do + i = i + 1 + end + return i or 16 -- maximum volume +end + +--[[ -- to determine self.VolumeLevels in various Kindle-models +function InfoMessage:getVolumeLevels() + local levels, v, i = {}, 0, 0 + while i < 16 do -- proper K3-range 0..15 might be different for other models + os.execute('lipc-set-prop com.lab126.audio Volume '..i) + v = self:getSoundVolume() + table.insert(levels, v) + i = i + 1 + end + return levels +end ]] + +function say(text) + os.execute("say \""..text.."\"") +end + +-- The read/write global InfoMessage settings. When properly tested, the +-- condition 'if FileChooser.filemanager_expert_mode == ...' might be deleted + +function InfoMessage:initInfoMessageSettings() + if FileChooser.filemanager_expert_mode == FileChooser.ROOT_MODE then + InfoMessage.InfoMethod = G_reader_settings:readSetting("info_message_methods") or InfoMessage.InfoMethod + InfoMessage.TTSspeed = G_reader_settings:readSetting("tts_speed") or InfoMessage:getTTSspeed() + InfoMessage.SoundVolume = G_reader_settings:readSetting("sound_volume") or InfoMessage:getSoundVolume() + end +end + +function InfoMessage:saveInfoMessageSettings() + if FileChooser.filemanager_expert_mode == FileChooser.ROOT_MODE then + G_reader_settings:saveSetting("info_message_methods", InfoMessage.InfoMethod) + G_reader_settings:saveSetting("sound_volume", InfoMessage.SoundVolume-1) + G_reader_settings:saveSetting("tts_speed", InfoMessage.TTSspeed) + end +end diff --git a/filechooser.lua b/filechooser.lua index fadec7e33..7cde6e144 100644 --- a/filechooser.lua +++ b/filechooser.lua @@ -29,33 +29,27 @@ FileChooser = { pagedirty = true, markerdirty = false, perpage, + clipboard = lfs.currentdir() .. "/clipboard", -- NO finishing slash + before_clipboard, -- NuPogodi, 30.09.12: to store the path where jump to clipboard was made from - -- NuPogodi, 04.09.2012: introduced modes that configures the filechoser - -- for users with various purposes & skills + -- modes that configures the filechoser for users with various purposes & skills filemanager_expert_mode, -- default value is defined in reader.lua -- the definitions BEGINNERS_MODE = 1, -- the filemanager content is restricted by files with reader-related extensions; safe renaming (no extension) ADVANCED_MODE = 2, -- no extension-based filtering; renaming with extensions; appreciable danger to crash crengine by improper docs ROOT_MODE = 3, -- TODO: all functions (including non-stable and dangerous) - } -function getProperTitleLength(txt,font_face,max_width) - local tw = TextWidget:new({ text = txt, face = font_face}) - -- 1st approximation for a point where to start title - local n = math.floor(string.len(txt) * (1 - max_width / tw:getSize().w)) - 2 - n = math.max(n, 1) - while tw:getSize().w >= max_width do - tw:free() - tw = TextWidget:new({ text = string.sub(txt,n,-1), face = font_face}) - n = n + 1 +-- NuPogodi, 29.09.12: simplified the code +function getProperTitleLength(txt, font_face, max_width) + while sizeUtf8Text(0, G_width, font_face, txt, true).x > max_width do + txt = txt:sub(2, -1) end - return string.sub(txt,n-1,-1) + return txt end function BatteryLevel() - -- NuPogodi, 18.05.12: This command seems to work even without Amazon Kindle framework local p = io.popen("gasgauge-info -s 2> /dev/null", "r") -- io.popen() _never_ fails! local battery = p:read("*a") or "?" if battery == "" then battery = "?" end @@ -63,36 +57,30 @@ function BatteryLevel() return string.gsub(battery, "[\n\r]+", "") end +-- NuPogodi, 29.09.12: avoid using widgets function DrawTitle(text,lmargin,y,height,color,font_face) local r = 6 -- radius for round corners color = 3 -- redefine to ignore the input for background color - - fb.bb:paintRect(1, 1, fb.bb:getWidth() - 2, height - r, color) - blitbuffer.paintBorder(fb.bb, 1, height/2, fb.bb:getWidth() - 2, height/2, height/2, color, r) - -- to have a horisontal gap between text & background rectangle - t = BatteryLevel() .. os.date(" %H:%M") - local tw = TextWidget:new({ text = t, face = font_face}) - twidth = tw:getSize().w - renderUtf8Text(fb.bb, fb.bb:getWidth()-twidth-lmargin, height-10, font_face, t, true) - tw:free() - - tw = TextWidget:new({ text = text, face = font_face}) - local max_width = fb.bb:getWidth() - 2*lmargin - twidth - if tw:getSize().w < max_width then + fb.bb:paintRect(1, 1, G_width-2, height - r, color) + blitbuffer.paintBorder(fb.bb, 1, height/2, G_width-2, height/2, height/2, color, r) + local t = BatteryLevel() .. os.date(" %H:%M") + r = sizeUtf8Text(0, G_width, font_face, t, true).x + renderUtf8Text(fb.bb, G_width-r-lmargin, height-10, font_face, t, true) + r = G_width - r - 2 * lmargin - 10 -- let's leave small gap + if sizeUtf8Text(0, G_width, font_face, text, true).x <= r then renderUtf8Text(fb.bb, lmargin, height-10, font_face, text, true) else - local w = renderUtf8Text(fb.bb, lmargin, height-10, font_face, "...", true) - local txt = getProperTitleLength(text, font_face, max_width-w) - renderUtf8Text(fb.bb, w+lmargin, height-10, font_face, txt, true) + t = renderUtf8Text(fb.bb, lmargin, height-10, font_face, "...", true) + text = getProperTitleLength(text, font_face, r-t) + renderUtf8Text(fb.bb, lmargin+t, height-10, font_face, text, true) end - tw:free() end function DrawFooter(text,font_face,h) local y = G_height - 7 - -- NuPogodi, 29.09.12: just dirty fix to have the same footer everywhere + -- just dirty fix to have the same footer everywhere local x = FileChooser.margin_H --(G_width / 2) - 50 - renderUtf8Text(fb.bb, x, y, font_face, text.." - Press H for help", true) + renderUtf8Text(fb.bb, x, y, font_face, text.." - Press H for help", true) end function DrawFileItem(name,x,y,image) @@ -106,11 +94,11 @@ function DrawFileItem(name,x,y,image) -- then drawing filenames local cface = Font:getFace("cfont", 22) local xleft = x + iw:getSize().w + 9 -- the gap between icon & filename - local width = fb.bb:getWidth() - xleft - x + local width = G_width - xleft - x -- now printing the name - if sizeUtf8Text(xleft, fb.bb:getWidth() - x, cface, name, true).x < width then + if sizeUtf8Text(xleft, G_width - x, cface, name, true).x < width then renderUtf8Text(fb.bb, xleft, y, cface, name, true) - else + else local lgap = sizeUtf8Text(0, width, cface, " ...", true).x local handle = renderUtf8TextWidth(fb.bb, xleft, y, cface, name, true, width - lgap - x) renderUtf8Text(fb.bb, handle.x + lgap + x, y, cface, " ...", true) @@ -162,6 +150,10 @@ end function FileChooser:setPath(newPath) local curr_path = self.path + if self.before_clipboard then -- back from clipboard + newPath = self.before_clipboard + self.before_clipboard = nil + end self.path = getAbsolutePath(newPath) local readdir_ok, exc = pcall(self.readDir,self) if(not readdir_ok) then @@ -173,8 +165,20 @@ function FileChooser:setPath(newPath) if self.items == 0 then return nil end - self.page = 1 - self.current = 1 + -- NuPogodi, 02.10.12: 1) changing the current item position ONLY IF the path was changed + -- 2) trying to set marker under the folder that was just left by ".." + if self.path ~= curr_path then + local i = 2 + while i <= #self.dirs and not string.find(curr_path, self.dirs[i]) do + i = i + 1 + end + if i <= #self.dirs then -- found + self.current, self.page = gotoTargetItem(i, self.items, self.current, self.page, self.perpage) + else -- set defaults + self.page = 1 + self.current = 1 + end + end return true end end @@ -210,7 +214,7 @@ function FileChooser:choose(ypos, height) local msg = self.exception_message and self.exception_message:match("[^%:]+:%d+: (.*)") or self.path self.exception_message = nil -- draw header - DrawTitle(msg,self.margin_H,ypos,self.title_H,4,tface) + DrawTitle(msg,self.margin_H,ypos,self.title_H,3,tface) self.markerdirty = true end @@ -260,7 +264,7 @@ function FileChooser:choose(ypos, height) end -- while end --- NuPogodi, 20.05.12: add available commands +-- add available commands function FileChooser:addAllCommands() self.commands = Commands:new{} @@ -270,35 +274,8 @@ function FileChooser:addAllCommands() self.pagedirty = true end ) - self.commands:add({KEY_PGFWD, KEY_LPGFWD}, nil, ">", - "goto next page", - function(self) - if self.page < (self.items / self.perpage) then - if self.current + self.page*self.perpage > self.items then - self.current = self.items - self.page*self.perpage - end - self.page = self.page + 1 - self.pagedirty = true - else - self.current = self.items - (self.page-1)*self.perpage - self.markerdirty = true - end - end - ) - self.commands:add({KEY_PGBCK, KEY_LPGBCK}, nil, "<", - "goto previous page", - function(self) - if self.page > 1 then - self.page = self.page - 1 - self.pagedirty = true - else - self.current = 1 - self.markerdirty = true - end - end - ) self.commands:add(KEY_FW_DOWN, nil, "joypad down", - "goto next item", + "next item", function(self) if self.current == self.perpage then if self.page < (self.items / self.perpage) then @@ -316,7 +293,7 @@ function FileChooser:addAllCommands() end ) self.commands:add(KEY_FW_UP, nil, "joypad up", - "goto previous item", + "previous item", function(self) if self.current == 1 then if self.page > 1 then @@ -330,16 +307,69 @@ function FileChooser:addAllCommands() end end ) - self.commands:add({KEY_FW_RIGHT, KEY_I}, nil, "joypad right", + -- NuPogodi, 01.10.12: fast jumps to items at positions 10, 20, .. 90, 0% within the list + local numeric_keydefs, i = {} + for i=1, 10 do numeric_keydefs[i]=Keydef:new(KEY_1+i-1, nil, tostring(i%10)) end + self.commands:addGroup("[1, 2 .. 9, 0]", numeric_keydefs, + "item at position 0%, 10% .. 90%, 100%", + function(self) + local target_item = math.ceil(self.items * (keydef.keycode-KEY_1) / 9) + self.current, self.page, self.markerdirty, self.pagedirty = + gotoTargetItem(target_item, self.items, self.current, self.page, self.perpage) + end + ) + self.commands:add({KEY_PGFWD, KEY_LPGFWD}, nil, ">", + "next page", + function(self) + if self.page < (self.items / self.perpage) then + if self.current + self.page*self.perpage > self.items then + self.current = self.items - self.page*self.perpage + end + self.page = self.page + 1 + self.pagedirty = true + else + self.current = self.items - (self.page-1)*self.perpage + self.markerdirty = true + end + end + ) + self.commands:add({KEY_PGBCK, KEY_LPGBCK}, nil, "<", + "previous page", + function(self) + if self.page > 1 then + self.page = self.page - 1 + self.pagedirty = true + else + self.current = 1 + self.markerdirty = true + end + end + ) + self.commands:add(KEY_G, nil, "G", -- NuPogodi, 01.10.12: goto page No. + "goto page", + function(self) + local n = math.ceil(self.items / self.perpage) + local page = NumInputBox:input(G_height-100, 100, "Page:", "current page "..self.page.." of "..n, true) + if pcall(function () page = math.floor(page) end) -- convert string to number + and page ~= self.page and page > 0 and page <= n then + self.page = page + if self.current + (page-1)*self.perpage > self.items then + self.current = self.items - (page-1)*self.perpage + end + end + self.pagedirty = true + end + ) + self.commands:add(KEY_FW_RIGHT, nil, "joypad right", "show document information", function(self) local folder = self.dirs[self.perpage*(self.page-1)+self.current] if folder then if folder == ".." then - showInfoMsgWithDelay(" ", 1000, 1) + warningUnsupportedFunction() else folder = self.path.."/"..folder - if FileInfo:show(folder) == "goto" then + if FileInfo:show(folder) == "goto" then self:setPath(folder) end end @@ -365,39 +395,29 @@ function FileChooser:addAllCommands() self.pagedirty = true end ) --- NuPogodi, 23.05.12: modified to delete both files and empty folders + -- modified to delete both files and empty folders self.commands:add(KEY_DEL, nil, "Del", "delete selected item", function(self) local pos = self.perpage*(self.page-1)+self.current - local folder = self.dirs[pos] - if folder == ".." then - showInfoMsgWithDelay(".. cannot be deleted! ",1500,1) - elseif folder then - InfoMessage:show("Press \'Y\' to confirm",0) - if self:ReturnKey() == KEY_Y then - if lfs.rmdir(self.path.."/"..folder) then - table.remove(self.dirs, offset) - self:setPath(self.path) - else - showInfoMsgWithDelay("Cannot be deleted!",1500,1) - end + local confirm = "Please, press key Y to confirm deleting" + if pos > #self.dirs then -- file + if InfoMessage.InfoMethod[MSG_CONFIRM] == 0 then -- silent regime + self:deleteFileAtPosition(pos) + else + InfoMessage:inform("Press 'Y' to confirm ", nil, 0, MSG_CONFIRM, confirm) + if self:ReturnKey() == KEY_Y then self:deleteFileAtPosition(pos) end end - else - InfoMessage:show("Press \'Y\' to confirm",0) - if self:ReturnKey() == KEY_Y then - pos = pos - #self.dirs - local fullpath = self.path.."/"..self.files[pos] - -- delete the file itself - os.remove(fullpath) - -- and its history file, if any - os.remove(DocToHistory(fullpath)) - -- to avoid showing just deleted file - table.remove(self.files, pos) - self.items = self.items - 1 - self.current = self.current - 1 + elseif self.dirs[pos] == ".." then + warningUnsupportedFunction() + else -- other folders + if InfoMessage.InfoMethod[MSG_CONFIRM] == 0 then -- silent regime + self:deleteFolderAtPosition(pos) + else + InfoMessage:inform("Press 'Y' to confirm ", nil, 0, MSG_CONFIRM, confirm) + if self:ReturnKey() == KEY_Y then self:deleteFolderAtPosition(pos) end end - end -- if folder == ".." + end self.pagedirty = true end -- function ) @@ -427,14 +447,48 @@ function FileChooser:addAllCommands() end end ) - -- NuPogodi, 04.09.12: menu to switch the filechooser mode self.commands:add(KEY_M, MOD_ALT, "M", "set mode for filemanager", function(self) self:changeFileChooserMode() end ) - self.commands:add({KEY_F, KEY_AA}, nil, "F", +-- NuPogodi, 25.09.12: new functions to tune the way how to inform user about the reader events + local popup_text = "Unstable... For experts only! " + local voice_text = "This function is still under development and available only for experts and beta testers." + self.commands:add(KEY_I, nil, "I", + "change the way to inform about events", + function(self) + if self.filemanager_expert_mode == self.ROOT_MODE then + InfoMessage:chooseNotificatonMethods() + self.pagedirty = true + else + InfoMessage:inform(popup_text, -1, 1, MSG_WARN, voice_text) + end + end + ) + self.commands:addGroup("Vol-/+", {Keydef:new(KEY_VPLUS,nil), Keydef:new(KEY_VMINUS,nil)}, + "decrease/increase sound volume", + function(self) + if self.filemanager_expert_mode == self.ROOT_MODE then + InfoMessage:incrSoundVolume(keydef.keycode == KEY_VPLUS and 1 or -1) + else + InfoMessage:inform(popup_text, -1, 1, MSG_WARN, voice_text) + end + end + ) + self.commands:addGroup(MOD_SHIFT.."Vol-/+", {Keydef:new(KEY_VPLUS,MOD_SHIFT), Keydef:new(KEY_VMINUS,MOD_SHIFT)}, + "decrease/increase TTS-engine speed", + function(self) + if self.filemanager_expert_mode == self.ROOT_MODE then + InfoMessage:incrTTSspeed(keydef.keycode == KEY_VPLUS and 1 or -1) + else + InfoMessage:inform(popup_text, -1, 1, MSG_WARN, voice_text) + end + end + ) +------------ end of changes (NuPogodi, 25.09.12) ------------ + self.commands:add({KEY_F, KEY_AA}, nil, "F, Aa", "change font faces", function(self) Font:chooseFonts() @@ -447,7 +501,7 @@ function FileChooser:addAllCommands() HelpPage:show(0, G_height, self.commands) self.pagedirty = true end - ) + ) self.commands:add(KEY_L, nil, "L", "show last documents", function(self) @@ -462,16 +516,17 @@ function FileChooser:addAllCommands() function(self) local keywords = InputBox:input(0, 0, "Search:") if keywords then - InfoMessage:show("Searching... ",0) + InfoMessage:inform("Searching... ", nil, 1, MSG_AUX) FileSearcher:init( self.path ) FileSearcher:choose(keywords) end self.pagedirty = true - end -- function + end ) self.commands:add(KEY_C, MOD_SHIFT, "C", - "copy file to \'clipboard\'", + "copy file to 'clipboard'", function(self) + -- TODO (NuPogodi, 27.09.12): overwrite? local file = self:FullFileName() if file then lfs.mkdir(self.clipboard) @@ -479,29 +534,34 @@ function FileChooser:addAllCommands() local fn = self.files[self.perpage*(self.page-1)+self.current - #self.dirs] os.execute("cp "..self:InQuotes(DocToHistory(file)).." " ..self:InQuotes(DocToHistory(self.clipboard.."/"..fn)) ) - showInfoMsgWithDelay("File copied to clipboard ", 1000, 1) + InfoMessage:inform("File copied to clipboard ", 1000, 1, MSG_WARN, + "The file has been copied to clipboard.") end end - ) + ) self.commands:add(KEY_X, MOD_SHIFT, "X", - "move file to \'clipboard\'", + "move file to 'clipboard'", function(self) + -- TODO (NuPogodi, 27.09.12): overwrite? local file = self:FullFileName() if file then lfs.mkdir(self.clipboard) local fn = self.files[self.perpage*(self.page-1)+self.current - #self.dirs] os.rename(file, self.clipboard.."/"..fn) os.rename(DocToHistory(file), DocToHistory(self.clipboard.."/"..fn)) - InfoMessage:show("File moved to clipboard ", 0) + InfoMessage:inform("File moved to clipboard ", nil, 0, MSG_WARN, + "The file has been moved to clipboard.") self:setPath(self.path) self.pagedirty = true end end ) self.commands:add(KEY_V, MOD_SHIFT, "V", - "paste file(s) from \'clipboard\'", + "paste file(s) from 'clipboard'", function(self) - InfoMessage:show("moving file(s) from clipboard ", 0) + -- TODO (NuPogodi, 27.09.12): first test whether the clipboard is empty & answer respectively + -- TODO (NuPogodi, 27.09.12): overwrite? + InfoMessage:inform("Moving files from clipboard...", nil, 0, MSG_AUX) for f in lfs.dir(self.clipboard) do if lfs.attributes(self.clipboard.."/"..f, "mode") == "file" then os.rename(self.clipboard.."/"..f, self.path.."/"..f) @@ -513,11 +573,15 @@ function FileChooser:addAllCommands() end ) self.commands:add(KEY_B, MOD_SHIFT, "B", - "show content of \'clipboard\'", + "show content of 'clipboard'", function(self) + -- NuPogodi, 30.09.12: exit back from clipboard to last folder by '..' + local current_path = self.path lfs.mkdir(self.clipboard) - self:setPath(self.clipboard) - -- TODO: exit back from clipboard to last folder - Redefine Exit on FW_Right? + if self.clipboard ~= self.path then + self:setPath(self.clipboard) + self.before_clipboard = current_path + end self.pagedirty = true end ) @@ -536,13 +600,12 @@ function FileChooser:addAllCommands() self.commands:add(KEY_K, MOD_SHIFT, "K", "run calculator", function(self) - local CalcBox = InputBox:new{ calcmode = true } + local CalcBox = InputBox:new{ inputmode = MODE_CALC } CalcBox:input(0, 0, "Calc ") self.pagedirty = true end ) - self.commands:add(KEY_HOME, nil, "Home", - "exit", + self.commands:addGroup("Home, Alt + Back", { Keydef:new(KEY_HOME, nil),Keydef:new(KEY_BACK, MOD_ALT)}, "exit", function(self) return "break" end @@ -551,19 +614,14 @@ end -- returns full filename or nil (if folder) function FileChooser:FullFileName() - local file - local folder = self.dirs[self.perpage*(self.page-1)+self.current] - if folder == ".." then - showInfoMsgWithDelay(" ",1000,1) - elseif folder then - showInfoMsgWithDelay(" ",1000,1) - else - file=self.path.."/"..self.files[self.perpage*(self.page-1)+self.current - #self.dirs] + if self.current > #self.dirs then + return self.path.."/"..self.files[self.perpage*(self.page-1)+self.current - #self.dirs] end - return file + warningUnsupportedFunction() + return nil end --- returns the keycode of released key and (if debug) shows the keycode on screen -function FileChooser:ReturnKey(debug) +-- returns the keycode of released key +function FileChooser:ReturnKey() while true do ev = input.saveWaitForEvent() ev.code = adjustKeyEvents(ev) @@ -571,7 +629,6 @@ function FileChooser:ReturnKey(debug) break end end - if debug then showInfoMsgWithDelay("Keycode = "..ev.code,1000,1) end return ev.code end @@ -582,11 +639,11 @@ end --[[ NuPogodi, 04.09.2012: to make it more easy for users with various purposes and skills. ATM, one may leave only silent toggling between BEGINNERS_MODE <> ADVANCED_MODE -- But, in future, one more (the so called ROOT_MODE) might also be rather useful. -Shitch this mode on should allow developers & beta-testers to use some unstable +Switching this mode on should allow developers & beta-testers to use some unstable and/or dangerous functions able to crash the reader. ]] function FileChooser:changeFileChooserMode() - local face_list = { "safe mode for beginners", "advanced mode for experienced users", "expert mode for beta-testers & developers" } + local face_list = { "Safe mode for beginners", "Advanced mode for experienced users", "Expert mode for beta-testers & developers" } local modes_menu = SelectMenu:new{ menu_title = "Select proper mode to manage files", item_array = face_list, @@ -594,30 +651,58 @@ function FileChooser:changeFileChooserMode() } local m = modes_menu:choose(0, G_height) if m and m ~= self.filemanager_expert_mode then - --[[ TODO: to allow multiline-rendering for info messages & to include detailed description of the selected mode - local msg = "Press 'Y' to accept new mode..." - if m==self.BEGINNERS_MODE then - msg = "You have selected safe mode for beginners: the filemanager shows only files with the reader-related extensions (*.pdf, *.djvu, etc.); ".. - "safe renaming (no extensions); unstable or dangerous functions are NOT included. "..msg - elseif m==self.ADVANCED_MODE then - msg = "You have selected advanced mode for experienced users: the filemanager shows all files; ".. - "you may rename not only their names, but also the extensions; the files with unknown extensions would be sent to CREReader ".. - "and could crash the reader. Please, use it in your own risk. "..msg - else -- ROOT_MODE - msg = "You have selected the most advanced and dangerous mode. I hope You know what you are doing. God bless You. "..msg - end - InfoMessage:show(msg, 1) - if self:ReturnKey() == KEY_Y then ]] if (self.filemanager_expert_mode == self.BEGINNERS_MODE and m > self.BEGINNERS_MODE) or (m == self.BEGINNERS_MODE and self.filemanager_expert_mode > self.BEGINNERS_MODE) then self.filemanager_expert_mode = m -- make sure that new mode is set before... - self:setPath(self.path) -- refreshing the folder content + self:setPath(self.path) -- refreshing the folder content else self.filemanager_expert_mode = m end G_reader_settings:saveSetting("filemanager_expert_mode", self.filemanager_expert_mode) - -- end end + -- NuPogodi, 26.09.2012: temporary place; when properly tested, might be commented / deleted the following line + InfoMessage:initInfoMessageSettings() self.pagedirty = true end +-- NuPogodi, 28.09.12: two following functions are extracted just to make the code more compact +function FileChooser:deleteFolderAtPosition(pos) + if lfs.rmdir(self.path.."/"..self.dirs[pos]) then + table.remove(self.dirs, pos) -- to avoid showing just deleted file + self.items = #self.dirs + #self.files + self.current, self.page = gotoTargetItem(pos, self.items, pos, self.page, self.perpage) + else + InfoMessage:inform("Folder can't be deleted! ", 1500, 1, MSG_ERROR, + "This folder can not be deleted! Please, make sure that it is empty.") + end +end + +function FileChooser:deleteFileAtPosition(pos) + local fullpath = self.path.."/"..self.files[pos-#self.dirs] + os.remove(fullpath) -- delete the file itself + os.remove(DocToHistory(fullpath)) -- and its history file, if any + table.remove(self.files, pos-#self.dirs) -- to avoid showing just deleted file + self.items = self.items - 1 + self.current, self.page = gotoTargetItem(pos, self.items, pos, self.page, self.perpage) +end + +-- NuPogodi, 01.10.12: jump to defined item in the itemlist +function gotoTargetItem(target_item, all_items, current_item, current_page, perpage) + target_item = math.max(math.min(target_item, all_items), 1) + local target_page = math.ceil(target_item/perpage) + local target_curr = (target_item -1) % perpage + 1 + local pagedirty, markerdirty = false, false + if target_page ~= current_page then + current_page = target_page + pagedirty = true + markerdirty = true + elseif target_curr ~= current_item then + markerdirty = true + end + return target_curr, current_page, markerdirty, pagedirty +end + +function warningUnsupportedFunction() + InfoMessage:inform("Unsupported function! ", 2000, 1, MSG_WARN, + "The requested function is not supported.") +end diff --git a/filehistory.lua b/filehistory.lua index ca71b3837..995bb136b 100644 --- a/filehistory.lua +++ b/filehistory.lua @@ -6,16 +6,13 @@ require "inputbox" require "dialog" require "filesearcher" require "settings" +require "dialog" FileHistory = { - -- title height - title_H = 40, - -- spacing between lines - spacing = 36, - -- foot height - foot_H = 28, - -- horisontal margin - margin_H = 10, + title_H = 40, -- title height + spacing = 36, -- spacing between lines + foot_H = 28, -- foot height + margin_H = 10, -- horisontal margin -- state buffer history_files = {}, @@ -25,6 +22,7 @@ FileHistory = { page = 0, current = 1, oldcurrent = 1, + commands = nil, } function FileHistory:init(history_path) @@ -33,14 +31,16 @@ function FileHistory:init(history_path) else self:setPath("./history") end - self:addAllCommands() + + -- to initialize only once + if not self.commands then self:addAllCommands() end end function FileHistory:setPath(newPath) self.path = newPath self:readDir("-c ") self.items = #self.files - if self.items == 0 then + if self.items == 1 then return nil end self.page = 1 @@ -49,24 +49,22 @@ function FileHistory:setPath(newPath) end function FileHistory:readDir(order_criteria) - self.history_files = {} - self.files = {} + self.history_files = { {dir=self.path, name=".."} } + self.files = { {dir=self.path, name=".."} } local p = io.popen("ls "..order_criteria.."-1 "..self.path) for f in p:lines() do -- insert history files - file_entry = {dir=self.path, name=f} - table.insert(self.history_files, file_entry) + table.insert(self.history_files, {dir=self.path, name=f}) -- and corresponding path & file items - file_entry = {dir=HistoryToPath(f), name=HistoryToName(f)} - table.insert(self.files, file_entry) + table.insert(self.files, {dir=HistoryToPath(f), name=HistoryToName(f)}) end p:close() end function FileHistory:setSearchResult(keywords) - self.result = {} + self.result = { {dir=self.path, name=".."} } if keywords == "" or keywords == " " then - -- show all history + -- show all history self.result = self.files else -- select history files with keywords in the filename @@ -80,6 +78,7 @@ function FileHistory:setSearchResult(keywords) self.items = #self.result self.page = 1 self.current = 1 + return self.items end function FileHistory:prevItem() @@ -113,28 +112,6 @@ end function FileHistory:addAllCommands() self.commands = Commands:new{} - -- search among last documents - self.commands:add(KEY_S, nil, "S", - "search among files", - function(self) - old_keywords = self.keywords - self.keywords = InputBox:input(G_height - 100, 100, - "Search:", old_keywords) - if self.keywords then - self:setSearchResult(self.keywords) - else - self.keywords = old_keywords - end - self.pagedirty = true - end - ) - self.commands:add(KEY_L, nil, "L", - "last documents", - function(self) - self:setSearchResult("") - self.pagedirty = true - end - ) self.commands:add(KEY_H, nil, "H", "show help page", function(self) @@ -142,26 +119,29 @@ function FileHistory:addAllCommands() self.pagedirty = true end ) - self.commands:add({KEY_FW_RIGHT, KEY_I}, nil, "joypad right", - "document details", - function(self) - file_entry = self.result[self.perpage*(self.page-1)+self.current] - FileInfo:show(file_entry.dir,file_entry.name) - self.pagedirty = true - end - ) self.commands:add(KEY_FW_UP, nil, "joypad up", - "goto previous item", + "previous item", function(self) self:prevItem() end ) self.commands:add(KEY_FW_DOWN, nil, "joypad down", - "goto next item", + "next item", function(self) self:nextItem() end ) + -- NuPogodi, 01.10.12: fast jumps to items at positions 10, 20, .. 90, 0% within the list + local numeric_keydefs, i = {} + for i=1, 10 do numeric_keydefs[i]=Keydef:new(KEY_1+i-1, nil, tostring(i%10)) end + self.commands:addGroup("[1, 2 .. 9, 0]", numeric_keydefs, + "item at position 0%, 10% .. 90%, 100%", + function(self) + local target_item = math.ceil(self.items * (keydef.keycode-KEY_1) / 9) + self.current, self.page, self.markerdirty, self.pagedirty = + gotoTargetItem(target_item, self.items, self.current, self.page, self.perpage) + end + ) self.commands:add({KEY_PGFWD, KEY_LPGFWD}, nil, ">", "next page", function(self) @@ -189,7 +169,58 @@ function FileHistory:addAllCommands() end end ) - self.commands:add({KEY_F, KEY_AA}, nil, "F", + self.commands:add(KEY_G, nil, "G", -- NuPogodi, 01.10.12: goto page No. + "goto page", + function(self) + local n = math.ceil(self.items / self.perpage) + local page = NumInputBox:input(G_height-100, 100, "Page:", "current page "..self.page.." of "..n, true) + if pcall(function () page = math.floor(page) end) -- convert string to number + and page ~= self.page and page > 0 and page <= n then + self.page = page + if self.current + (page-1)*self.perpage > self.items then + self.current = self.items - (page-1)*self.perpage + end + end + self.pagedirty = true + end + ) + self.commands:add(KEY_FW_RIGHT, nil, "joypad right", + "document details", + function(self) + local file_entry = self.result[self.perpage*(self.page-1)+self.current] + if file_entry.name == ".." then + warningUnsupportedFunction() + return + end -- do not show details + FileInfo:show(file_entry.dir,file_entry.name) + self.pagedirty = true + end + ) + self.commands:add(KEY_S, nil, "S", + "invoke search inputbox", + function(self) + -- NuPogodi, 30.09.12: be sure that something is found + local old_keywords = self.keywords + local old_data = self.result + local old_page, old_current = self.page, self.current + self.keywords = InputBox:input(G_height - 100, 100, "Search:", old_keywords) + if self.keywords then + self:setSearchResult(self.keywords) + end + if #self.result < 2 then + InfoMessage:inform("No hits! Try another keyword. ", 2000, 1, MSG_WARN, + "The search has given no results! Please, try another keyword.") + -- restoring the original data + self.result = old_data + self.items = #self.result + self.keywords = old_keywords + self.page = old_page + self.current = old_current + end + self.pagedirty = true + end + ) + self.commands:add({KEY_F, KEY_AA}, nil, "F, Aa", "change font faces", function(self) Font:chooseFonts() @@ -197,35 +228,42 @@ function FileHistory:addAllCommands() end ) self.commands:add({KEY_ENTER, KEY_FW_PRESS}, nil, "Enter", - "open selected item", + "open selected document", function(self) - if #self.result == 0 then - showInfoMsgWithDelay("No files to open", 1500, 1) - return - end - file_entry = self.result[self.perpage*(self.page-1)+self.current] + local file_entry = self.result[self.perpage*(self.page-1)+self.current] + if file_entry.name == ".." then return "break" end -- quit file_full_path = file_entry.dir .. "/" .. file_entry.name - openFile(file_full_path) --reset height and item index if screen has been rotated local item_no = self.perpage * (self.page - 1) + self.current self.perpage = math.floor(G_height / self.spacing) - 2 self.current = item_no % self.perpage self.page = math.floor(item_no / self.perpage) + 1 - self.pagedirty = true end ) self.commands:add({KEY_DEL}, nil, "Del", "delete history entry", function(self) - file_entry = self.result[self.perpage*(self.page-1)+self.current] - if not file_entry then return end + local file_entry = self.result[self.perpage*(self.page-1)+self.current] + if file_entry.name == ".." then + warningUnsupportedFunction() + return + end -- do not delete local file_to_del = file_entry.dir .. "/" .. file_entry.name - os.remove(DocToHistory(file_to_del)) - -- to avoid showing just deleted file - self:init() - self:setSearchResult(self.keywords) + if InfoMessage.InfoMethod[MSG_CONFIRM] == 0 then -- silent regime + os.remove(DocToHistory(file_to_del)) + self:init() + self:setSearchResult(self.keywords) + else + InfoMessage:inform("Press 'Y' to confirm ", nil, 0, MSG_CONFIRM, + "Please, press key Y to delete the book history") + if FileChooser:ReturnKey() == KEY_Y then + os.remove(DocToHistory(file_to_del)) + self:init() + self:setSearchResult(self.keywords) + end + end self.pagedirty = true end ) @@ -235,7 +273,7 @@ function FileHistory:addAllCommands() self.pagedirty = true end ) - self.commands:add({KEY_BACK, KEY_HOME}, nil, "Back", + self.commands:add({KEY_BACK, KEY_HOME}, nil, "Back, '..', Home", "back", function(self) return "break" @@ -248,7 +286,11 @@ function FileHistory:choose(keywords) self.pagedirty = true self.markerdirty = false - self:setSearchResult(keywords) + -- NuPogodi, 30.09.12: immediate quit (no redraw), if empty + if self:setSearchResult(keywords) < 2 then -- only ".." + InfoMessage:inform("No reading history! ", 2000, 1, MSG_WARN, "The reading history is empty!") + return nil + end while true do local cface = Font:getFace("cfont", 22) @@ -258,37 +300,25 @@ function FileHistory:choose(keywords) if self.pagedirty then self.markerdirty = true fb.bb:paintRect(0, 0, G_width, G_height, 0) - -- draw header local header = "Last Documents" if self.keywords ~= "" and self.keywords ~= " " then --header = header .. " (filter: \'" .. string.upper(self.keywords) .. "\')" header = "Search Results for \'"..string.upper(self.keywords).."\'" end - DrawTitle(header,self.margin_H,0,self.title_H,4,tface) - + DrawTitle(header,self.margin_H,0,self.title_H,3,tface) -- draw found results - local c - if self.items == 0 then -- nothing found - y = self.title_H + self.spacing * 2 - renderUtf8Text(fb.bb, self.margin_H, y, cface, - "Sorry, no files found.", true) - self.markerdirty = false - else -- found something, draw it - for c = 1, self.perpage do - local i = (self.page - 1) * self.perpage + c - if i <= self.items then - y = self.title_H + (self.spacing * c) + 4 - local ftype = string.lower(string.match(self.result[i].name, ".+%.([^.]+)") or "") - DrawFileItem(self.result[i].name,self.margin_H,y,ftype) - end + for c = 1, self.perpage do + local i = (self.page - 1) * self.perpage + c + if i <= self.items then + y = self.title_H + (self.spacing * c) + 4 + local ftype = string.lower(string.match(self.result[i].name, ".+%.([^.]+)") or "") + DrawFileItem(self.result[i].name,self.margin_H,y,ftype) end end - -- draw footer all_page = math.ceil(self.items/self.perpage) DrawFooter("Page "..self.page.." of "..all_page,fface,self.foot_H) - end if self.markerdirty then diff --git a/font.lua b/font.lua index 44666ffd3..006e33d8b 100644 --- a/font.lua +++ b/font.lua @@ -9,8 +9,6 @@ Font = { hpkfont = "droid/DroidSansMono.ttf", -- help page: font for displaying keys hfont = "droid/DroidSans.ttf", -- help page: font for displaying help messages infont = "droid/DroidSansMono.ttf", -- inputbox: use mono for better distance controlling - -- pgfont = "droid/DroidSans.ttf", -- was in use in heppage to render footer - -- to be repalced by ffont }, fontdir = os.getenv("FONTDIR") or "./fonts", -- face table diff --git a/input.c b/input.c index fffc35d61..d781c5b10 100644 --- a/input.c +++ b/input.c @@ -15,7 +15,11 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ + +#include "popen-noshell/popen_noshell.h" +#include #include +#include #include #include #include @@ -29,9 +33,7 @@ #include "input.h" #include #include -#include -#define OUTPUT_SIZE 21 #define CODE_IN_SAVER 10000 #define CODE_OUT_SAVER 10001 #define CODE_USB_PLUG_IN 10010 @@ -41,7 +43,17 @@ #define NUM_FDS 4 int inputfds[4] = { -1, -1, -1, -1 }; -int slider_pid = -1; +pid_t slider_pid = -1; +struct popen_noshell_pass_to_pclose pclose_arg; + +void slider_handler(int sig) +{ + /* Kill lipc-wait-event properly on exit */ + if(pclose_arg.pid != 0) { + // Be a little more gracious, lipc seems to handle SIGINT properly + kill(pclose_arg.pid, SIGINT); + } +} int findFreeFdSlot() { int i; @@ -73,10 +85,13 @@ static int openInputDevice(lua_State *L) { return luaL_error(L, "cannot fork() slider event listener"); } if(childpid == 0) { + // We send a SIGTERM to this child on exit, trap it to kill lipc properly. + signal(SIGTERM, slider_handler); + FILE *fp; - char std_out[OUTPUT_SIZE] = ""; + char std_out[256]; + int status; struct input_event ev; - int ret; __u16 key_code = 10000; close(pipefd[0]); @@ -85,17 +100,22 @@ static int openInputDevice(lua_State *L) { ev.code = key_code; ev.value = 1; - /* listen power slider events */ - while(1) { - fp = popen("exec lipc-wait-event com.lab126.powerd goingToScreenSaver,outOfScreenSaver,charging,notCharging", "r"); - /* @TODO 07.06 2012 (houqp) - * plugin and out event can only be watched by: - lipc-wait-event com.lab126.hal usbPlugOut,usbPlugIn - */ - if(fgets(std_out, OUTPUT_SIZE, fp) == NULL) { - break; - } - pclose(fp); + /* listen power slider events (listen for ever for multiple events) */ + char *argv[] = {"lipc-wait-event", "-m", "-s", "0", "com.lab126.powerd", "goingToScreenSaver,outOfScreenSaver,charging,notCharging", (char *) NULL}; + /* @TODO 07.06 2012 (houqp) + * plugin and out event can only be watched by: + lipc-wait-event com.lab126.hal usbPlugOut,usbPlugIn + */ + + fp = popen_noshell("lipc-wait-event", (const char * const *)argv, "r", &pclose_arg, 0); + if (!fp) { + err(EXIT_FAILURE, "popen_noshell()"); + } + + /* Flush to get rid of buffering issues? */ + fflush(fp); + + while(fgets(std_out, sizeof(std_out)-1, fp)) { if(std_out[0] == 'g') { ev.code = CODE_IN_SAVER; } else if(std_out[0] == 'o') { @@ -116,10 +136,26 @@ static int openInputDevice(lua_State *L) { /* generate event */ if(write(pipefd[1], &ev, sizeof(struct input_event)) == -1) { - break; + printf("Failed to generate event.\n"); } } - exit(0); /* cannot be reached?! */ + + status = pclose_noshell(&pclose_arg); + if (status == -1) { + err(EXIT_FAILURE, "pclose_noshell()"); + } else { + printf("lipc-wait-event exited with status %d.\n", status); + + if WIFEXITED(status) { + printf("lipc-wait-event exited normally with status: %d.\n", WEXITSTATUS(status)); + } + if WIFSIGNALED(status) { + printf("lipc-wait-event terminated by signal: %d.\n", WTERMSIG(status)); + } + } + + // We're done, go away :). + _exit(EXIT_SUCCESS); } else { close(pipefd[1]); inputfds[fd] = pipefd[0]; @@ -149,7 +185,7 @@ static int closeInputDevices(lua_State *L) { for(i=0; i usecs/1000) diff --git a/inputbox.lua b/inputbox.lua index 091c9937c..043c7956a 100644 --- a/inputbox.lua +++ b/inputbox.lua @@ -3,6 +3,9 @@ require "rendertext" require "keys" require "graphics" +MODE_CALC = 1 +MODE_TERM = 2 + ---------------------------------------------------- -- General inputbox ---------------------------------------------------- @@ -37,7 +40,7 @@ InputBox = { shiftmode = true, -- toggle chars <-> capitals, lowest bit in (layout-2) symbolmode = false, -- toggle chars <-> symbols, middle bit in (layout-2) utf8mode = false, -- toggle english <-> national, highest bit in (layout-2) - calcmode = false, -- toggle calculator mode + inputmode, -- define mode: input <> calculator <> terminal calcfunctions = nil, -- math functions for calculator helppage } @@ -128,7 +131,7 @@ function InputBox:input(ypos, height, title, d_text, is_hint) -- my own position, at the bottom screen edge ypos = fb.bb:getHeight() - 165 -- some corrections for calculator mode - if self.calcmode then + if self.inputmode == MODE_CALC then self:setCalcMode() end @@ -366,7 +369,7 @@ function InputBox:CharlistToString() end function InputBox:addAllCommands() - -- if already initialized, we (re)define only calcmode-dependent commands + -- if already initialized, we (re)define only inputmode-dependent commands if self.commands then self:ModeDependentCommands() self:DrawVirtualKeyboard() @@ -378,7 +381,7 @@ function InputBox:addAllCommands() self:addCharCommands(self.layout) -- adding the rest commands (independent of the selected layout) self.commands:add(KEY_H, MOD_ALT, "H", - "show helppage", + "show help page", function(self) self:showHelpPage(self.commands) end @@ -436,7 +439,7 @@ function InputBox:addAllCommands() end ) self.commands:addGroup("up/down", { Keydef:new(KEY_FW_DOWN, nil), Keydef:new(KEY_FW_UP, nil) }, - "goto previous/next VK-layout", + "previous/next VK-layout", function(self) if keydef.keycode == KEY_FW_DOWN then if self.layout == self.max_layout then self:addCharCommands(self.min_layout) @@ -469,10 +472,10 @@ function InputBox:addAllCommands() self:addCharCommands() end ) - -- NuPogodi, 02.06.12: calcmode-dependent commands are collected + -- NuPogodi, 02.06.12: inputmode-dependent commands are collected self:ModeDependentCommands() -- here - self.commands:add({KEY_BACK, KEY_HOME}, nil, "Back", + self.commands:add({KEY_BACK, KEY_HOME}, nil, "Back, Home", "back", function(self) self.input_string = nil @@ -552,9 +555,8 @@ function InputBox:showHelpPage(list, title) self.cursor:clear() -- hide cursor fb.bb:dimRect(self.input_start_x-5, self.input_start_y-19, self.input_slot_w, self.fheight, self.input_bg) fb:refresh(1, self.input_start_x-5, self.ypos, self.input_slot_w, self.h) - -- now start the helppage with own list of commands and own title HelpPage:show(0, fb.bb:getHeight()-165, list, title) - -- on the helppage-exit, making inactive helpage + -- on the help page-exit, making inactive helpage fb.bb:dimRect(0, 40, fb.bb:getWidth(), fb.bb:getHeight()-205, self.input_bg) -- and active input slot self:refreshText() @@ -595,13 +597,13 @@ end -- define whether we need to calculate the result or to return 'self.input_string' function InputBox:ModeDependentCommands() - if self.calcmode then + if self.inputmode == MODE_CALC then -- define what to do with the input_string - self.commands:add({KEY_FW_PRESS, KEY_ENTER}, nil, "joypad center", + self.commands:add({KEY_FW_PRESS, KEY_ENTER}, nil, "Enter", "calculate the result", function(self) if #self.input_string == 0 then - showInfoMsgWithDelay("No input ", 1000, 1) + InfoMessage:inform("No input! ", 1000, 1, MSG_WARN, "There is nothing to calculate") else local s = self:PrepareStringToCalc() if pcall(function () f = assert(loadstring("r = tostring("..s..")")) end) and pcall(f) then @@ -618,7 +620,7 @@ function InputBox:ModeDependentCommands() self.cursor:draw() fb:refresh(1, self.input_start_x-5, self.input_start_y-25, self.input_slot_w, self.h-25) else - showInfoMsgWithDelay("Invalid input ", 1000, 1) + InfoMessage:inform("Invalid input! ", 1000, 1, MSG_WARN) end -- if pcall end end -- function @@ -633,7 +635,7 @@ function InputBox:ModeDependentCommands() end ) else -- return input_string & close input box - self.commands:add({KEY_FW_PRESS, KEY_ENTER}, nil, "joypad center", + self.commands:add({KEY_FW_PRESS, KEY_ENTER}, nil, "Enter", "submit input content", function(self) if self.input_string == "" then @@ -644,7 +646,7 @@ function InputBox:ModeDependentCommands() ) -- delete calculator-specific help self.commands:del(KEY_M, MOD_ALT, "M") - end -- if self.calcmode + end -- if self.inputmode end ---------------------------------------------------- diff --git a/kite/KPDFviewer (last file) b/kite/KPDFviewer (last file) index 5d9acab14..de0c410d9 100755 --- a/kite/KPDFviewer (last file) +++ b/kite/KPDFviewer (last file) @@ -1,3 +1,3 @@ #!/bin/sh -/mnt/us/launchpad/kpdf.sh +/mnt/us/kindlepdfviewer/kpdf.sh diff --git a/kite/KPDFviewer File Manager b/kite/KPDFviewer File Manager index 3374ef3ae..f5f935234 100755 --- a/kite/KPDFviewer File Manager +++ b/kite/KPDFviewer File Manager @@ -1,3 +1,3 @@ #!/bin/sh -/mnt/us/launchpad/kpdf.sh /mnt/us/documents +/mnt/us/kindlepdfviewer/kpdf.sh /mnt/us/documents diff --git a/kpdf.sh b/kpdf.sh new file mode 100644 index 000000000..c1f81c700 --- /dev/null +++ b/kpdf.sh @@ -0,0 +1,37 @@ +#!/bin/sh +export LC_ALL="en_US.UTF-8" + +echo unlock > /proc/keypad +echo unlock > /proc/fiveway + +# we're always starting from our working directory +cd /mnt/us/kindlepdfviewer/ + +# bind-mount system fonts +if ! grep /mnt/us/kindlepdfviewer/fonts/host /proc/mounts; then + mount -o bind /usr/java/lib/fonts /mnt/us/kindlepdfviewer/fonts/host +fi + +# check if we are supposed to shut down the Amazon framework +if test "$1" == "--framework_stop"; then + shift 1 + /etc/init.d/framework stop +fi + +# stop cvm +killall -stop cvm + +# finally call reader +./reader.lua "$1" 2> /mnt/us/kindlepdfviewer/crash.log || cat /mnt/us/kindlepdfviewer/crash.log + +# unmount system fonts +if grep /mnt/us/kindlepdfviewer/fonts/host /proc/mounts; then + umount /mnt/us/kindlepdfviewer/fonts/host +fi + +# always try to continue cvm +killall -cont cvm || /etc/init.d/framework start + +# cleanup hanging process +killall lipc-wait-event + diff --git a/kpdfview.c b/kpdfview.c index d0003ae7f..0f8a674f2 100644 --- a/kpdfview.c +++ b/kpdfview.c @@ -39,7 +39,7 @@ lua_State *L; int main(int argc, char **argv) { - int i, err; + int i; if(argc < 2) { fprintf(stderr, "needs config file as first argument.\n"); diff --git a/launchpad/kpdf.ini b/launchpad/kpdf.ini index ab2a1b0d9..53be1fc3f 100755 --- a/launchpad/kpdf.ini +++ b/launchpad/kpdf.ini @@ -1,11 +1,11 @@ [Actions] # start kindlepdfviewer with filebrowser in /mnt/us/documents -P D = !/mnt/us/launchpad/kpdf.sh /mnt/us/documents +P D = !/mnt/us/kindlepdfviewer/kpdf.sh /mnt/us/documents # start kindlepdfviewer with last document -P P = !/mnt/us/launchpad/kpdf.sh +P P = !/mnt/us/kindlepdfviewer/kpdf.sh # start kindlepdfviewer without framework in /mnt/us/documents -P K = !/mnt/us/launchpad/kpdf.sh --framework_stop /mnt/us/documents +P K = !/mnt/us/kindlepdfviewer/kpdf.sh --framework_stop /mnt/us/documents # start kindlepdfviewer without framework on last read document -P L = !/mnt/us/launchpad/kpdf.sh --framework_stop +P L = !/mnt/us/kindlepdfviewer/kpdf.sh --framework_stop # restart amazon framework - when it got irritated P R = !/etc/init.d/framework restart diff --git a/launchpad/kpdf.sh b/launchpad/kpdf.sh index c1f81c700..3f5cffc43 100755 --- a/launchpad/kpdf.sh +++ b/launchpad/kpdf.sh @@ -31,7 +31,3 @@ fi # always try to continue cvm killall -cont cvm || /etc/init.d/framework start - -# cleanup hanging process -killall lipc-wait-event - diff --git a/popen-noshell/popen_noshell-buildfix.patch b/popen-noshell/popen_noshell-buildfix.patch new file mode 100644 index 000000000..97c09b881 --- /dev/null +++ b/popen-noshell/popen_noshell-buildfix.patch @@ -0,0 +1,86 @@ +Index: CREDITS +=================================================================== +--- CREDITS (revision 0) ++++ CREDITS (working copy) +@@ -0,0 +1 @@ ++Taken from http://code.google.com/p/popen-noshell/ + +Property changes on: CREDITS +___________________________________________________________________ +Added: svn:keywords +## -0,0 +1 ## ++Id +\ No newline at end of property +Index: Makefile +=================================================================== +--- Makefile (revision 0) ++++ Makefile (working copy) +@@ -0,0 +1,17 @@ ++SRCS=popen_noshell.c ++ ++OBJS:=$(SRCS:%.c=%.o) ++ ++%.o: %.c ++ $(CC) $(CPPFLAGS) $(CFLAGS) -o $@ -c $< ++ ++all: libpopen_noshell.a ++ ++libpopen_noshell.a: $(OBJS) ++ $(AR) rcs $@ $(OBJS) ++ ++clean: ++ rm -rf *.o ++ rm -rf libpopen_noshell.a ++ ++.PHONY: clean + +Property changes on: Makefile +___________________________________________________________________ +Added: svn:keywords +## -0,0 +1 ## ++Id +\ No newline at end of property +Index: popen_noshell.c +=================================================================== +--- popen_noshell.c (revision 8) ++++ popen_noshell.c (working copy) +@@ -16,6 +16,10 @@ + * along with this program. If not, see . + */ + ++#ifndef _GNU_SOURCE ++#define _GNU_SOURCE ++#endif ++ + #include "popen_noshell.h" + #include + #include +@@ -28,10 +32,6 @@ + #include + #include + #include +- +-#ifndef _GNU_SOURCE +-#define _GNU_SOURCE +-#endif + #include + + /* +@@ -249,7 +249,7 @@ + * The above malloc() + align implementation is taken from: + * http://stackoverflow.com/questions/227897/solve-the-memory-alignment-in-c-interview-question-that-stumped-me + */ +- ++ + #ifndef POPEN_NOSHELL_VALGRIND_DEBUG + pid = clone(fn, stack_aligned, CLONE_VM | SIGCHLD, arg); + #else +@@ -358,7 +358,7 @@ + + pclose_arg->fp = fp; + pclose_arg->pid = pid; +- ++ + return fp; // we should never end up here + } + diff --git a/reader.lua b/reader.lua index 320434a48..afddbc65f 100755 --- a/reader.lua +++ b/reader.lua @@ -42,7 +42,7 @@ function openFile(filename) reader = ext:getReader(file_type) if reader then - InfoMessage:show("Opening document... ", 0) + InfoMessage:inform("Opening document... ", nil, 0, MSG_AUX) reader:preLoadSettings(filename) local ok, err = reader:open(filename) if ok then @@ -54,9 +54,9 @@ function openFile(filename) else if err then Debug("openFile(): "..err) - showInfoMsgWithDelay(err:sub(1,30), 2000, 1) + InfoMessage:inform(err:sub(1,30), 2000, 1, MSG_ERROR) else - showInfoMsgWithDelay("Error opening document ", 2000, 1) + InfoMessage:inform("Error opening document! ", 2000, 1, MSG_ERROR) end end end @@ -140,6 +140,8 @@ end -- set up the mode to manage files FileChooser.filemanager_expert_mode = G_reader_settings:readSetting("filemanager_expert_mode") or 1 +InfoMessage:initInfoMessageSettings() + -- initialize global settings shared among all readers UniReader:initGlobalSettings(G_reader_settings) -- initialize specific readers @@ -171,6 +173,7 @@ end -- save reader settings G_reader_settings:saveSetting("fontmap", Font.fontmap) +InfoMessage:saveInfoMessageSettings() G_reader_settings:close() -- @TODO dirty workaround, find a way to force native system poll diff --git a/resources/info-aux.png b/resources/info-aux.png new file mode 100644 index 000000000..afee09ee5 Binary files /dev/null and b/resources/info-aux.png differ diff --git a/resources/info-bug.png b/resources/info-bug.png new file mode 100644 index 000000000..990e0466f Binary files /dev/null and b/resources/info-bug.png differ diff --git a/resources/info-confirm.png b/resources/info-confirm.png new file mode 100644 index 000000000..85a6585ed Binary files /dev/null and b/resources/info-confirm.png differ diff --git a/resources/info-error.png b/resources/info-error.png new file mode 100644 index 000000000..97ecdfc37 Binary files /dev/null and b/resources/info-error.png differ diff --git a/resources/info-i.png b/resources/info-i.png deleted file mode 100644 index 69192d4ed..000000000 Binary files a/resources/info-i.png and /dev/null differ diff --git a/resources/info-warn.png b/resources/info-warn.png new file mode 100644 index 000000000..f653634db Binary files /dev/null and b/resources/info-warn.png differ diff --git a/screen.lua b/screen.lua index 9f34322cd..6ac6cbd76 100644 --- a/screen.lua +++ b/screen.lua @@ -38,7 +38,6 @@ Codes for rotation modes: 0 --]] - Screen = { cur_rotation_mode = 0, -- these two variabls are used to help switching from framework to reader @@ -118,7 +117,8 @@ function Screen:screenshot() local diff = nsecs - secs + (nusecs - usecs)/1000000 --self:fb2bmp("/dev/fb0", lfs.currentdir().."/screenshots/"..os.date("%Y%m%d%H%M%S")..".bmp", true, "bzip2 ") --self:fb2pgm("/dev/fb0", lfs.currentdir().."/screenshots/"..os.date("%Y%m%d%H%M%S")..".pgm", "bzip2 ", 4) - showInfoMsgWithDelay(string.format("Screenshot is ready in %.2fs ", diff), 2000, 1) + local msg = "Screenshot is ready in " + InfoMessage:inform(msg..string.format("%.2fs ", diff), 2000, 1, MSG_WARN, msg..math.ceil(diff*1000).." milliseconds") end -- NuPogodi (02.07.2012): added the functions to save the fb-content in common graphic files - bmp & pgm. diff --git a/selectmenu.lua b/selectmenu.lua index a7e1e5cea..158a61c97 100644 --- a/selectmenu.lua +++ b/selectmenu.lua @@ -5,22 +5,14 @@ require "font" require "commands" SelectMenu = { - -- font for displaying item names - fsize = 22, - -- font for page title - tfsize = 25, - -- font for paging display - ffsize = 16, - -- font for item shortcut + fsize = 22, -- font for displaying item names + tfsize = 25, -- font for page title + ffsize = 16,-- font for paging display - -- title height - title_H = 40, - -- spacing between lines - spacing = 36, - -- foot height - foot_H = 27, - -- horisontal margin - margin_H = 10, + title_H = 40, -- title height + spacing = 36, -- spacing between lines + foot_H = 27, -- foot height + margin_H = 10, -- horisontal margin current_entry = 0, menu_title = "No Title", @@ -114,7 +106,7 @@ function SelectMenu:addAllCommands() end end ) - self.commands:add({KEY_PGFWD, KEY_LPGFWD}, nil, "next", + self.commands:add({KEY_PGFWD, KEY_LPGFWD}, nil, ">", "next page", function(sm) if sm.page < (sm.items / sm.perpage) then @@ -129,7 +121,7 @@ function SelectMenu:addAllCommands() end end ) - self.commands:add({KEY_PGBCK, KEY_LPGBCK}, nil, "prev", + self.commands:add({KEY_PGBCK, KEY_LPGBCK}, nil, "<", "previous page", function(sm) if sm.page > 1 then @@ -188,7 +180,7 @@ function SelectMenu:addAllCommands() table.insert(KEY_Q_to_P, Keydef:new(i, nil, "")) end self.commands:addGroup("Q to P", KEY_Q_to_P, - "Select item with Q to P key as shortcut", + "select item with Q to P key as shortcut", function(sm, keydef) sm.selected_item = sm:getItemIndexByShortCut( sm.item_shortcuts[ keydef.keycode - KEY_Q + 1 ], sm.perpage) @@ -199,7 +191,7 @@ function SelectMenu:addAllCommands() table.insert(KEY_A_to_L, Keydef:new(i, nil, "")) end self.commands:addGroup("A to L", KEY_A_to_L, - "Select item with A to L key as shortcut", + "select item with A to L key as shortcut", function(sm, keydef) sm.selected_item = sm:getItemIndexByShortCut( sm.item_shortcuts[ keydef.keycode - KEY_A + 11 ], sm.perpage) @@ -210,55 +202,55 @@ function SelectMenu:addAllCommands() table.insert(KEY_Z_to_M, Keydef:new(i, nil, "")) end self.commands:addGroup("Z to M", KEY_Z_to_M, - "Select item with Z to M key as shortcut", + "select item with Z to M key as shortcut", function(sm, keydef) sm.selected_item = sm:getItemIndexByShortCut( sm.item_shortcuts[ keydef.keycode - KEY_Z + 21 ], sm.perpage) end ) self.commands:add(KEY_SLASH, nil, "/", - "Select item with / key as shortcut", + "select item with / key as shortcut", function(sm) sm.selected_item = sm:getItemIndexByShortCut("/", sm.perpage) end ) self.commands:add(KEY_DOT, nil, ".", - "Select item with dot key as shortcut", + "select item with dot key as shortcut", function(sm) sm.selected_item = sm:getItemIndexByShortCut(".", sm.perpage) end ) self.commands:add(KEY_SYM, nil, "Sym", - "Select item with Sym key as shortcut", + "select item with Sym key as shortcut", function(sm) sm.selected_item = sm:getItemIndexByShortCut("Sym", sm.perpage) end ) self.commands:add(KEY_ENTER, nil, "Enter", - "Select item with Enter key as shortcut", + "select item with Enter key as shortcut", function(sm) sm.selected_item = sm:getItemIndexByShortCut("Ent", sm.perpage) end ) - self.commands:add(KEY_BACK, nil, "Back", - "Exit menu", - function(sm) - return "break" - end - ) self.commands:add(KEY_H,MOD_ALT,"H", "show help page", function(sm) HelpPage:show(0, G_height, sm.commands) sm.pagedirty = true end) + self.commands:add({KEY_BACK,KEY_HOME}, nil, "Back, Home", + "exit menu", + function(sm) + return "break" + end + ) end function SelectMenu:clearCommands() self.commands = Commands:new{} - self.commands:add(KEY_BACK, nil, "", - "Exit menu", + self.commands:add({KEY_BACK,KEY_HOME}, nil, "Back, Home", + "exit menu", function(sm) return "break" end) @@ -352,7 +344,7 @@ function SelectMenu:choose(ypos, height) end -- for c=1, self.perpage end -- if self.items == 0 - local footer = "Page "..self.page.." of "..(math.ceil(self.items / self.perpage)).." Press Alt-H for help" + local footer = "Page "..self.page.." of "..(math.ceil(self.items / self.perpage)).." - Press Alt-H for help" renderUtf8Text(fb.bb, self.margin_H, height-7, fface, footer, true) end diff --git a/settings.lua b/settings.lua index 286803d8b..3b38c3b25 100644 --- a/settings.lua +++ b/settings.lua @@ -100,6 +100,7 @@ function Debug(...) end end print("#"..line) + return true -- debug enabled end -- simple serialization function, won't do uservalues, functions, loops diff --git a/slider_watcher.c b/slider_watcher.c index 6a4e26e15..3d16c06ad 100644 --- a/slider_watcher.c +++ b/slider_watcher.c @@ -16,8 +16,11 @@ along with this program. If not, see . */ -#include +#include "popen-noshell/popen_noshell.h" +#include #include +#include +#include #include #include #include @@ -25,18 +28,19 @@ #include #include #include +#include -#define OUTPUT_SIZE 21 -#define EVENT_PIPE "/tmp/event_slider" #define CODE_IN_SAVER 10000 #define CODE_OUT_SAVER 10001 int main ( int argc, char *argv[] ) { - int fd, ret; + int fd; FILE *fp; - char std_out[OUTPUT_SIZE] = ""; + char std_out[256]; + int status; + struct popen_noshell_pass_to_pclose pclose_arg; struct input_event ev; __u16 key_code = 10000; @@ -51,7 +55,7 @@ main ( int argc, char *argv[] ) /* open npipe for writing */ fd = open(argv[1], O_RDWR | O_NONBLOCK); if(fd < 0) { - printf("Open %s falied: %s\n", argv[1], strerror(errno)); + printf("Open %s failed: %s\n", argv[1], strerror(errno)); exit(EXIT_FAILURE); } @@ -60,15 +64,18 @@ main ( int argc, char *argv[] ) ev.code = key_code; ev.value = 1; - while(1) { - /* listen power slider events */ - memset(std_out, 0, OUTPUT_SIZE); - fp = popen("lipc-wait-event -s 0 com.lab126.powerd goingToScreenSaver,outOfScreenSaver", "r"); - ret = fread(std_out, OUTPUT_SIZE, 1, fp); - pclose(fp); + /* listen power slider events */ + char *argv[] = {"lipc-wait-event", "-m", "-s", "0", "com.lab126.powerd", "goingToScreenSaver,outOfScreenSaver", (char *) NULL}; + + fp = popen_noshell("lipc-wait-event", (const char * const *)chargv, "r", &pclose_arg, 0); + if (!fp) { + err(EXIT_FAILURE, "popen_noshell()"); + } + + while(fgets(std_out, sizeof(std_out)-1, fp)) { + + /* printf("Got line: %s", std_out); */ - /* fill event struct */ - gettimeofday(&ev.time, NULL); if(std_out[0] == 'g') { ev.code = CODE_IN_SAVER; } else if(std_out[0] == 'o') { @@ -77,9 +84,29 @@ main ( int argc, char *argv[] ) printf("Unrecognized event.\n"); exit(EXIT_FAILURE); } + /* fill event struct */ + gettimeofday(&ev.time, NULL); + + /* printf("Send event %d\n", ev.code); */ /* generate event */ - ret = write(fd, &ev, sizeof(struct input_event)); + if(write(fd, &ev, sizeof(struct input_event)) == -1) { + printf("Failed to generate event.\n"); + } + } + + status = pclose_noshell(&pclose_arg); + if (status == -1) { + err(EXIT_FAILURE, "pclose_noshell()"); + } else { + printf("Power slider event listener child exited with status %d.\n", status); + + if WIFEXITED(status) { + printf("Child exited normally with status: %d.\n", WEXITSTATUS(status)); + } + if WIFSIGNALED(status) { + printf("Child terminated by signal: %d.\n", WTERMSIG(status)); + } } close(fd); diff --git a/unireader.lua b/unireader.lua index 102245536..5f1a749ef 100644 --- a/unireader.lua +++ b/unireader.lua @@ -1049,10 +1049,11 @@ function UniReader:drawOrCache(no, preCache) -- #4 goal: we render next page, too. (TODO) local pg_w = G_width / ( self.doc:getPages() ) - local page_indicator = function() - fb.bb:invertRect( pg_w*(no-1),0, pg_w,10) - fb:refresh(1, pg_w*(no-1),0, pg_w,10) - Debug('page_indicator',no) + local page_indicator = function() + if Debug('page_indicator',no) then + fb.bb:invertRect( pg_w*(no-1),0, pg_w,10) + fb:refresh(1, pg_w*(no-1),0, pg_w,10) + end end page_indicator()