diff --git a/frontend/ui/uimanager.lua b/frontend/ui/uimanager.lua
index b2c0e1541..a0f63d0c8 100644
--- a/frontend/ui/uimanager.lua
+++ b/frontend/ui/uimanager.lua
@@ -21,6 +21,9 @@ local UIManager = {
G_reader_settings:readSetting("full_refresh_count") or DRCOUNTMAX,
refresh_count = 0,
+ -- How long to wait between ZMQ wakeups: 50ms.
+ ZMQ_TIMEOUT = 50 * 1000,
+
event_handlers = nil,
_running = true,
@@ -660,6 +663,27 @@ function UIManager:resetInputTimeout()
self.INPUT_TIMEOUT = nil
end
+function UIManager:handleInputEvent(input_event)
+ if input_event.handler ~= "onInputError" then
+ self.event_hook:execute("InputEvent", input_event)
+ end
+ local handler = self.event_handlers[input_event]
+ if handler then
+ handler(input_event)
+ else
+ self.event_handlers["__default__"](input_event)
+ end
+end
+
+-- Process all pending events on all registered ZMQs.
+function UIManager:processZMQs()
+ for _, zeromq in ipairs(self._zeromqs) do
+ for input_event in zeromq.waitEvent,zeromq do
+ self:handleInputEvent(input_event)
+ end
+ end
+end
+
function UIManager:handleInput()
local wait_until, now
-- run this in a loop, so that paints can trigger events
@@ -683,42 +707,32 @@ function UIManager:handleInput()
self:_repaint()
until not self._task_queue_dirty
- -- wait for next event
- -- note that we will skip that if we have tasks that are ready to run
- local input_event = nil
- if not wait_until then
- if #self._zeromqs > 0 then
- -- pending message queue, wait 100ms for input
- input_event = Input:waitEvent(1000*100)
- if not input_event or input_event.handler == "onInputError" then
- for _, zeromq in ipairs(self._zeromqs) do
- input_event = zeromq:waitEvent()
- if input_event then break end
- end
- end
- else
- -- no pending task, wait without timeout
- input_event = Input:waitEvent(self.INPUT_TIMEOUT)
- end
- elseif wait_until[1] > now[1]
- or wait_until[1] == now[1] and wait_until[2] > now[2] then
- -- wait until next task is pending
- local wait_us = (wait_until[1] - now[1]) * MILLION
- + (wait_until[2] - now[2])
- input_event = Input:waitEvent(wait_us)
+ -- run ZMQs if any
+ self:processZMQs()
+
+ -- Figure out how long to wait.
+ -- Default to INPUT_TIMEOUT (which may be nil, i.e. block until an event happens).
+ local wait_us = self.INPUT_TIMEOUT
+
+ -- If there's a timed event pending, that puts an upper bound on how long to wait.
+ if wait_until then
+ wait_us = math.min(
+ wait_us or math.huge,
+ (wait_until[1] - now[1]) * MILLION
+ + (wait_until[2] - now[2]))
+ end
+
+ -- If we have any ZMQs registered, ZMQ_TIMEOUT is another upper bound.
+ if #self._zeromqs > 0 then
+ wait_us = math.min(wait_us or math.huge, self.ZMQ_TIMEOUT)
end
+ -- wait for next event
+ local input_event = Input:waitEvent(wait_us)
+
-- delegate input_event to handler
if input_event then
- if input_event.handler ~= "onInputError" then
- self.event_hook:execute("InputEvent", input_event)
- end
- local handler = self.event_handlers[input_event]
- if handler then
- handler(input_event)
- else
- self.event_handlers["__default__"](input_event)
- end
+ self:handleInputEvent(input_event)
end
if self.looper then
diff --git a/frontend/util.lua b/frontend/util.lua
index 9737b17e3..c3fd55230 100644
--- a/frontend/util.lua
+++ b/frontend/util.lua
@@ -304,6 +304,32 @@ function util.isEmptyDir(path)
return true
end
+--- Checks if the given path exists. Doesn't care if it's a file or directory.
+---- @string path
+---- @treturn bool
+function util.pathExists(path)
+ local lfs = require("libs/libkoreader-lfs")
+ return lfs.attributes(path, "mode") ~= nil
+end
+
+--- As `mkdir -p`.
+--- Unlike lfs.mkdir(), does not error if the directory already exists, and
+--- creates intermediate directories as needed.
+---- @string path the directory to create
+---- @treturn bool true on success; nil, err_message on error
+function util.makePath(path)
+ path = path:gsub("/+$", "")
+ if util.pathExists(path) then return true end
+
+ local success, err = util.makePath((util.splitFilePathName(path)))
+ if not success then
+ return nil, err.." (creating "..path..")"
+ end
+
+ local lfs = require("libs/libkoreader-lfs")
+ return lfs.mkdir(path)
+end
+
--- Replaces characters that are invalid filenames.
--
-- Replaces the characters \/:*?"<>|
with an _
.
@@ -326,7 +352,9 @@ function util.replaceSlashChar(str)
end
end
---- Splits a file into its path and name
+--- Splits a file into its directory path and file name.
+--- If the given path has a trailing /, returns the entire path as the directory
+--- path and "" as the file name.
---- @string file
---- @treturn string path, filename
function util.splitFilePathName(file)
diff --git a/plugins/calibrecompanion.koplugin/main.lua b/plugins/calibrecompanion.koplugin/main.lua
index 6349f73f5..4598b0828 100644
--- a/plugins/calibrecompanion.koplugin/main.lua
+++ b/plugins/calibrecompanion.koplugin/main.lua
@@ -5,6 +5,7 @@ local JSON = require("json")
local DEBUG = require("dbg")
local _ = require("gettext")
local NetworkMgr = require("ui/network/manager")
+local util = require("frontend/util")
require("ffi/zeromq_h")
@@ -320,6 +321,7 @@ function CalibreCompanion:sendBook(arg)
local inbox_dir = G_reader_settings:readSetting("inbox_dir")
local filename = inbox_dir .. "/" .. arg.lpath
DEBUG("write to file", filename)
+ util.makePath((util.splitFilePathName(filename)))
local outfile = io.open(filename, "wb")
local to_write_bytes = arg.length
local calibre_device = self