mirror of
https://github.com/koreader/koreader.git
synced 2025-08-10 00:52:38 +00:00
This is a major overhaul of the hardware abstraction layer. A few notes: General platform distinction happens in frontend/device.lua which will delegate everything else to frontend/device/<platform_name>/device.lua which should extend frontend/device/generic/device.lua Screen handling is implemented in frontend/device/screen.lua which includes the *functionality* to support device specifics. Actually setting up the device specific functionality, however, is done in the device specific setup code in the relevant device.lua file. The same goes for input handling.
314 lines
9.8 KiB
Lua
314 lines
9.8 KiB
Lua
local FileManagerHistory = require("apps/filemanager/filemanagerhistory")
|
|
local FileManagerMenu = require("apps/filemanager/filemanagermenu")
|
|
local InputContainer = require("ui/widget/container/inputcontainer")
|
|
local FrameContainer = require("ui/widget/container/framecontainer")
|
|
local FileManager = require("apps/filemanager/filemanager")
|
|
local VerticalGroup = require("ui/widget/verticalgroup")
|
|
local VerticalSpan = require("ui/widget/verticalspan")
|
|
local ButtonDialog = require("ui/widget/buttondialog")
|
|
local InfoMessage = require("ui/widget/infomessage")
|
|
local TextWidget = require("ui/widget/textwidget")
|
|
local DocSettings = require("docsettings")
|
|
local UIManager = require("ui/uimanager")
|
|
local Screen = require("device").screen
|
|
local Event = require("ui/event")
|
|
local Font = require("ui/font")
|
|
local ltn12 = require("ltn12")
|
|
local DEBUG = require("dbg")
|
|
local _ = require("gettext")
|
|
local util = require("ffi/util")
|
|
-- lfs
|
|
|
|
local ffi = require("ffi")
|
|
ffi.cdef[[
|
|
int remove(const char *);
|
|
int rmdir(const char *);
|
|
]]
|
|
|
|
local dummy = require("ffi/zeromq_h")
|
|
local ZSync = InputContainer:new{
|
|
name = "zsync",
|
|
}
|
|
|
|
function ZSync:init()
|
|
self.ui.menu:registerToMainMenu(self)
|
|
self.outbox = self.path.."/outbox"
|
|
self.server_config = self.path.."/server.cfg"
|
|
self.client_config = self.path.."/client.cfg"
|
|
end
|
|
|
|
function ZSync:addToMainMenu(tab_item_table)
|
|
table.insert(tab_item_table.plugins, {
|
|
text = _("ZSync"),
|
|
sub_item_table = {
|
|
{
|
|
text_func = function()
|
|
return not self.filemq_server
|
|
and _("Publish this document")
|
|
or _("Stop publisher")
|
|
end,
|
|
enabled_func = function()
|
|
return self.filemq_client == nil
|
|
end,
|
|
callback = function()
|
|
if not self.filemq_server then
|
|
self:publish()
|
|
else
|
|
self:unpublish()
|
|
end
|
|
end
|
|
},
|
|
{
|
|
text_func = function()
|
|
return not self.filemq_client
|
|
and _("Subscribe documents")
|
|
or _("Stop subscriber")
|
|
end,
|
|
enabled_func = function()
|
|
return self.filemq_server == nil
|
|
end,
|
|
callback = function()
|
|
if not self.filemq_client then
|
|
self:subscribe()
|
|
else
|
|
self:unsubscribe()
|
|
end
|
|
end
|
|
}
|
|
}
|
|
})
|
|
end
|
|
|
|
function ZSync:initServerZyreMQ()
|
|
local ZyreMessageQueue = require("ui/message/zyremessagequeue")
|
|
if self.zyre_messagequeue == nil then
|
|
self.server_zyre = ZyreMessageQueue:new{
|
|
header = {["FILEMQ-SERVER"] = tostring(self.fmq_port)},
|
|
}
|
|
self.server_zyre:start()
|
|
self.zyre_messagequeue = UIManager:insertZMQ(self.server_zyre)
|
|
end
|
|
end
|
|
|
|
function ZSync:initClientZyreMQ()
|
|
local ZyreMessageQueue = require("ui/message/zyremessagequeue")
|
|
if self.zyre_messagequeue == nil then
|
|
self.client_zyre = ZyreMessageQueue:new{}
|
|
self.client_zyre:start()
|
|
self.zyre_messagequeue = UIManager:insertZMQ(self.client_zyre)
|
|
end
|
|
end
|
|
|
|
function ZSync:initServerFileMQ(outboxes)
|
|
local FileMessageQueue = require("ui/message/filemessagequeue")
|
|
local filemq = ffi.load("libs/libfmq.so.1")
|
|
if self.file_messagequeue == nil then
|
|
self.filemq_server = filemq.fmq_server_new()
|
|
self.file_messagequeue = UIManager:insertZMQ(FileMessageQueue:new{
|
|
server = self.filemq_server
|
|
})
|
|
self.fmq_port = filemq.fmq_server_bind(self.filemq_server, "tcp://*:*")
|
|
filemq.fmq_server_configure(self.filemq_server, self.server_config)
|
|
filemq.fmq_server_set_anonymous(self.filemq_server, true)
|
|
end
|
|
UIManager:scheduleIn(1, function()
|
|
for _, outbox in ipairs(outboxes) do
|
|
DEBUG("publish", outbox.path, outbox.alias)
|
|
filemq.fmq_server_publish(self.filemq_server, outbox.path, outbox.alias)
|
|
end
|
|
end)
|
|
end
|
|
|
|
function ZSync:initClientFileMQ(inbox)
|
|
local FileMessageQueue = require("ui/message/filemessagequeue")
|
|
local filemq = ffi.load("libs/libfmq.so.1")
|
|
if self.file_messagequeue == nil then
|
|
self.filemq_client = filemq.fmq_client_new()
|
|
self.file_messagequeue = UIManager:insertZMQ(FileMessageQueue:new{
|
|
client = self.filemq_client
|
|
})
|
|
filemq.fmq_client_configure(self.filemq_client, self.client_config)
|
|
end
|
|
UIManager:scheduleIn(1, function()
|
|
filemq.fmq_client_set_inbox(self.filemq_client, inbox)
|
|
end)
|
|
end
|
|
|
|
local function clearDirectory(dir, rmdir)
|
|
for f in lfs.dir(dir) do
|
|
local path = dir.."/"..f
|
|
local mode = lfs.attributes(path, "mode")
|
|
if mode == "file" then
|
|
ffi.C.remove(path)
|
|
elseif mode == "directory" and f ~= "." and f ~= ".." then
|
|
clearDirectory(path, true)
|
|
end
|
|
end
|
|
if rmdir then
|
|
ffi.C.rmdir(dir)
|
|
end
|
|
end
|
|
|
|
local function mklink(path, filename)
|
|
local basename = filename:match(".*/(.*)") or filename
|
|
local linkname = path .. "/" .. basename .. ".ln"
|
|
local linkfile = io.open(linkname, "w")
|
|
if linkfile then
|
|
linkfile:write(filename .. "\n")
|
|
linkfile:close()
|
|
end
|
|
end
|
|
|
|
-- add directory directly into outboxes
|
|
function ZSync:outboxesAddDirectory(outboxes, dir)
|
|
if lfs.attributes(dir, "mode") == "directory" then
|
|
local basename = dir:match(".*/(.*)") or dir
|
|
table.insert(outboxes, {
|
|
path = dir,
|
|
alias = "/"..basename,
|
|
})
|
|
end
|
|
end
|
|
|
|
-- link file in root outbox
|
|
function ZSync:outboxAddFileLink(filename)
|
|
local mode = lfs.attributes(filename, "mode")
|
|
if mode == "file" then
|
|
mklink(self.outbox, filename)
|
|
end
|
|
end
|
|
|
|
-- copy directory content into root outbox(no recursively)
|
|
function ZSync:outboxCopyDirectory(dir)
|
|
local basename = dir:match(".*/(.*)") or dir
|
|
local newdir = self.outbox.."/"..basename
|
|
lfs.mkdir(newdir)
|
|
if pcall(lfs.dir, dir) then
|
|
for f in lfs.dir(dir) do
|
|
local filename = dir.."/"..f
|
|
if lfs.attributes(filename, "mode") == "file" then
|
|
local newfile = newdir.."/"..f
|
|
ltn12.pump.all(
|
|
ltn12.source.file(assert(io.open(filename, "rb"))),
|
|
ltn12.sink.file(assert(io.open(newfile, "wb")))
|
|
)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function ZSync:publish()
|
|
DEBUG("publish document", self.view.document.file)
|
|
lfs.mkdir(self.outbox)
|
|
clearDirectory(self.outbox)
|
|
local file = self.view.document.file
|
|
local sidecar = file:match("(.*)%.")..".sdr"
|
|
self:outboxAddFileLink(file)
|
|
self:outboxCopyDirectory(sidecar)
|
|
local outboxes = {}
|
|
table.insert(outboxes, {
|
|
path = self.outbox,
|
|
alias = "/",
|
|
})
|
|
-- init filemq first to get filemq port
|
|
self:initServerFileMQ(outboxes)
|
|
self:initServerZyreMQ()
|
|
end
|
|
|
|
function ZSync:unpublish()
|
|
DEBUG("ZSync unpublish")
|
|
clearDirectory(self.outbox)
|
|
self:stopZyreMQ()
|
|
self:stopFileMQ()
|
|
end
|
|
|
|
function ZSync:onChooseInbox(inbox)
|
|
DEBUG("choose inbox", inbox)
|
|
self.inbox = inbox
|
|
-- init zyre first for filemq endpoint
|
|
self:initClientZyreMQ()
|
|
self:initClientFileMQ(inbox)
|
|
return true
|
|
end
|
|
|
|
function ZSync:subscribe()
|
|
DEBUG("subscribe documents")
|
|
self.received = {}
|
|
local zsync = self
|
|
require("ui/downloadmgr"):new{
|
|
title = _("Choose inbox"),
|
|
onConfirm = function(inbox)
|
|
G_reader_settings:saveSetting("inbox_dir", inbox)
|
|
zsync:onChooseInbox(inbox)
|
|
end,
|
|
}:chooseDir()
|
|
end
|
|
|
|
function ZSync:unsubscribe()
|
|
DEBUG("ZSync unsubscribe")
|
|
self.received = {}
|
|
self:stopFileMQ()
|
|
self:stopZyreMQ()
|
|
end
|
|
|
|
function ZSync:onZyreEnter(id, name, header, endpoint)
|
|
local filemq = ffi.load("libs/libfmq.so.1")
|
|
if header and endpoint and header["FILEMQ-SERVER"] then
|
|
self.server_zyre_endpoint = endpoint
|
|
local port = header["FILEMQ-SERVER"]
|
|
local host = endpoint:match("(.*:)") or "*:"
|
|
local fmq_server_endpoint = host..port
|
|
DEBUG("connect filemq server at", fmq_server_endpoint)
|
|
-- wait for filemq server setup befor connecting
|
|
UIManager:scheduleIn(2, function()
|
|
filemq.fmq_client_set_resync(self.filemq_client, true)
|
|
filemq.fmq_client_subscribe(self.filemq_client, "/")
|
|
filemq.fmq_client_connect(self.filemq_client, fmq_server_endpoint)
|
|
end)
|
|
end
|
|
return true
|
|
end
|
|
|
|
function ZSync:onFileDeliver(filename, fullname)
|
|
-- sometimes several FileDelever msgs are sent from filemq
|
|
if self.received[filename] then return end
|
|
UIManager:show(InfoMessage:new{
|
|
text = _("Received file:") .. "\n" .. filename,
|
|
timeout = 1,
|
|
})
|
|
self.received[filename] = true
|
|
end
|
|
|
|
--[[
|
|
-- We assume that ZSync is running in either server mode or client mode
|
|
-- but never both. The zyre_messagequeue may be a server_zyre or client_zyre.
|
|
-- And the file_messagequeue may be a filemq_server or filemq_client.
|
|
--]]
|
|
function ZSync:stopZyreMQ()
|
|
if self.zyre_messagequeue then
|
|
self.zyre_messagequeue:stop()
|
|
UIManager:removeZMQ(self.zyre_messagequeue)
|
|
self.zyre_messagequeue = nil
|
|
self.server_zyre = nil
|
|
self.client_zyre = nil
|
|
end
|
|
end
|
|
|
|
function ZSync:stopFileMQ()
|
|
if self.file_messagequeue then
|
|
self.file_messagequeue:stop()
|
|
UIManager:removeZMQ(self.file_messagequeue)
|
|
self.file_messagequeue = nil
|
|
self.filemq_server = nil
|
|
self.filemq_client = nil
|
|
end
|
|
end
|
|
|
|
function ZSync:onCloseReader()
|
|
self:stopZyreMQ()
|
|
self:stopFileMQ()
|
|
end
|
|
|
|
return ZSync
|
|
|