diff --git a/frontend/apps/reader/modules/readerdictionary.lua b/frontend/apps/reader/modules/readerdictionary.lua index 599c07f19..5e9b86295 100644 --- a/frontend/apps/reader/modules/readerdictionary.lua +++ b/frontend/apps/reader/modules/readerdictionary.lua @@ -673,41 +673,29 @@ function ReaderDictionary:startSdcv(word, dict_names, fuzzy_search) end end - local results_str = nil - if Device:isAndroid() then - local A = require("android") - results_str = A.stdout(unpack(args)) - else - local cmd = util.shell_escape(args) - -- cmd = "sleep 7 ; " .. cmd -- uncomment to simulate long lookup time - - if self.lookup_progress_msg then - -- Some sdcv lookups, when using fuzzy search with many dictionaries - -- and a really bad selected text, can take up to 10 seconds. - -- It is nice to be able to cancel it when noticing wrong text was selected. - -- As we have a lookup_progress_msg (that can be used to catch a tap - -- and trigger cancellation), and because sdcv starts outputing its - -- output only at the end when it has done its work, we can - -- use Trapper:dismissablePopen() to cancel it as long as we are waiting - -- for output. - -- We must ensure we will have some output to be readable (if no - -- definition found, sdcv will output some message on stderr, and - -- let stdout empty) by appending an "echo": - cmd = cmd .. "; echo" - local completed - completed, results_str = Trapper:dismissablePopen(cmd, self.lookup_progress_msg) - lookup_cancelled = not completed - else - -- Fuzzy search disabled, usual option for people who don't want - -- a "Looking up..." InfoMessage and usually fast: do a classic - -- blocking io.popen() - local std_out = io.popen(cmd, "r") - if std_out then - results_str = std_out:read("*all") - std_out:close() - end - end - end + local cmd = util.shell_escape(args) + -- cmd = "sleep 7 ; " .. cmd -- uncomment to simulate long lookup time + + -- Some sdcv lookups, when using fuzzy search with many dictionaries + -- and a really bad selected text, can take up to 10 seconds. + -- It is nice to be able to cancel it when noticing wrong text was + -- selected. + -- Because sdcv starts outputing its output only at the end when it has + -- done its work, we can use Trapper:dismissablePopen() to cancel it as + -- long as we are waiting for output. + -- When fuzzy search is enabled, we have a lookup_progress_msg that can + -- be used to catch a tap and trigger cancellation. + -- When fuzzy search is disabled, we provide false instead so an + -- invisible non-event-forwarding TrapWidget is used to catch a tap + -- and trigger cancellation (invisible so there's no need for repaint + -- and refresh with the usually fast non-fuzzy search lookups). + -- We must ensure we will have some output to be readable (if no + -- definition found, sdcv will output some message on stderr, and + -- let stdout empty) by appending an "echo": + cmd = cmd .. "; echo" + local completed, results_str = Trapper:dismissablePopen(cmd, self.lookup_progress_msg or false) + lookup_cancelled = not completed + if results_str and results_str ~= "\n" then -- \n is when lookup was cancelled local ok, results = pcall(JSON.decode, results_str) if ok and results then diff --git a/frontend/ui/trapper.lua b/frontend/ui/trapper.lua index e859b4d3f..d627bf613 100644 --- a/frontend/ui/trapper.lua +++ b/frontend/ui/trapper.lua @@ -314,8 +314,9 @@ Notes and limitations: 3) We need a @{ui.widget.trapwidget|TrapWidget} or @{ui.widget.infomessage|InfoMessage}, that, as a modal, will catch any @{ui.event|Tap event} happening during `cmd` execution. This can be an existing already displayed widget, or - provided as a string (a new TrapWidget will be created). If nil, an invisible - TrapWidget will be used instead. + provided as a string (a new TrapWidget will be created). If nil, true or false, + an invisible TrapWidget will be used instead (if nil or true, the event will be + resent; if false, the event will not be resent). If we really need to have more control, we would need to use `select()` via `ffi` or do low level non-blocking reading on the file descriptor. @@ -324,7 +325,7 @@ collect indefinitely, the best option would be to compile any `timeout.c` and use it as a wrapper. @string cmd shell `cmd` to execute and get output from -@param trap_widget_or_string already shown widget, string or nil +@param trap_widget_or_string already shown widget, string, or nil, true or false @treturn boolean completed (`true` if not interrupted, `false` if dismissed) @treturn string output of command ]] @@ -358,10 +359,15 @@ function Trapper:dismissablePopen(cmd, trap_widget_or_string) UIManager:show(trap_widget) UIManager:forceRePaint() else - -- Use an invisible TrapWidget that resend event + -- Use an invisible TrapWidget that resend event, but not if + -- trap_widget_or_string is false (rather than nil or true) + local resend_event = true + if trap_widget_or_string == false then + resend_event = false + end trap_widget = TrapWidget:new{ text = nil, - resend_event = true, + resend_event = resend_event, } UIManager:show(trap_widget) own_trap_widget_invisible = true @@ -472,11 +478,12 @@ Notes and limitations: 4) We need a @{ui.widget.trapwidget|TrapWidget} or @{ui.widget.infomessage|InfoMessage}, that, as a modal, will catch any @{ui.event|Tap event} happening during `cmd` execution. This can be an existing already displayed widget, or - provided as a string (a new TrapWidget will be created). If nil, an invisible - TrapWidget will be used instead. + provided as a string (a new TrapWidget will be created). If nil, true or false, + an invisible TrapWidget will be used instead (if nil or true, the event will be + resent; if false, the event will not be resent). @function task lua function to execute and get return values from -@param trap_widget_or_string already shown widget, string or nil +@param trap_widget_or_string already shown widget, string, or nil, true or false @boolean task_returns_simple_string[opt=false] true if task returns a single string @treturn boolean completed (`true` if not interrupted, `false` if dismissed) @return ... return values of task @@ -504,10 +511,15 @@ function Trapper:dismissableRunInSubprocess(task, trap_widget_or_string, task_re UIManager:show(trap_widget) UIManager:forceRePaint() else - -- Use an invisible TrapWidget that resend event + -- Use an invisible TrapWidget that resend event, but not if + -- trap_widget_or_string is false (rather than nil or true) + local resend_event = true + if trap_widget_or_string == false then + resend_event = false + end trap_widget = TrapWidget:new{ text = nil, - resend_event = true, + resend_event = resend_event, } UIManager:show(trap_widget) own_trap_widget_invisible = true diff --git a/frontend/ui/widget/trapwidget.lua b/frontend/ui/widget/trapwidget.lua index a00162e9d..74391159f 100644 --- a/frontend/ui/widget/trapwidget.lua +++ b/frontend/ui/widget/trapwidget.lua @@ -115,16 +115,13 @@ function TrapWidget:_dismissAndResent(evtype, ev) self.dismiss_callback() UIManager:close(self) if self.resend_event and evtype and ev then - -- XXX There may be timing problems that could cause crashes, as we + -- There may be some timing issues that could cause crashes, as we -- use nextTick, if the dismiss_callback uses UIManager:scheduleIn() -- or has set up some widget that may catch that event while not being -- yet fully initialiazed. - -- It happened mostly when I had some bug somewhere, and it was a quite + -- (It happened mostly when I had some bug somewhere, and it was a quite -- reliable sign of a bug somewhere, but the stacktrace was unrelated - -- to the bug location. - -- Fix to avoid crashes: in GestureRange:match(), check that self.range() - -- does not return nil before using it: - -- if not range or not range:contains(gs.pos) then return false + -- to the bug location.) UIManager:nextTick(function() UIManager:handleInputEvent(Event:new(evtype, ev)) end) end return true