2014-10-03 08:11:53 +00:00
#!./luajit
2016-12-29 08:10:38 +00:00
io.stdout : write ( [ [
2016-10-15 03:36:00 +00:00
---------------------------------------------
launching ...
_ _____ ____ _
| |/ / _ \ | _ \ ___ __ _ __ | | ___ _ __
| ' / | | | |_) / _ \ / _` |/ _` |/ _ \ ' __ |
| . \ | _ | | _ < __ / ( _ | | ( _ | | __ / |
| _ | \ _ \ ___ /| _ | \ _ \ ___ | \ __ , _ | \ __ , _ | \ ___ | _ |
2018-03-05 22:27:03 +00:00
It ' s a scroll... It ' s a codex ... It ' s KOReader!
[ * ] Current time : ] ] , os.date ( " %x-%X " ) , " \n " )
2016-10-22 07:53:02 +00:00
io.stdout : flush ( )
2016-10-15 03:36:00 +00:00
2019-12-06 21:55:30 +00:00
-- Load default settings
2016-11-13 07:44:06 +00:00
require ( " defaults " )
2015-06-15 08:46:43 +00:00
local DataStorage = require ( " datastorage " )
pcall ( dofile , DataStorage : getDataDir ( ) .. " /defaults.persistent.lua " )
2014-09-25 14:22:30 +00:00
2019-12-06 21:55:30 +00:00
-- Set up Lua and ffi search paths
2016-11-13 07:44:06 +00:00
require ( " setupkoenv " )
2015-02-18 17:37:36 +00:00
2018-03-05 22:27:03 +00:00
io.stdout : write ( " [*] Version: " , require ( " version " ) : getCurrentRevision ( ) , " \n \n " )
io.stdout : flush ( )
2019-12-06 21:55:30 +00:00
-- Read settings and check for language override
-- Has to be done before requiring other files because
2014-01-18 22:04:14 +00:00
-- they might call gettext on load
2016-06-12 18:28:44 +00:00
G_reader_settings = require ( " luasettings " ) : open (
DataStorage : getDataDir ( ) .. " /settings.reader.lua " )
2020-12-23 16:06:08 +00:00
-- Apply the JIT opt tweaks ASAP when the C BB is disabled,
-- because we want to avoid the jit.flush() from bb:enableCBB,
-- which only makes the mcode allocation issues worse on Android...
local is_cbb_enabled = G_reader_settings : nilOrFalse ( " dev_no_c_blitter " )
if not is_cbb_enabled then
jit.opt . start ( " loopunroll=45 " )
end
2014-01-19 17:59:27 +00:00
local lang_locale = G_reader_settings : readSetting ( " language " )
2019-12-06 21:55:33 +00:00
-- Allow quick switching to Arabic for testing RTL/UI mirroring
if os.getenv ( " KO_RTL " ) then lang_locale = " ar_AA " end
2016-11-13 07:44:06 +00:00
local _ = require ( " gettext " )
2014-01-19 17:59:27 +00:00
if lang_locale then
2014-03-13 13:52:43 +00:00
_.changeLang ( lang_locale )
2014-01-18 22:04:14 +00:00
end
2020-09-22 21:26:05 +00:00
-- Try to turn the C blitter on/off, and synchronize setting so that UI config reflects real state
local bb = require ( " ffi/blitbuffer " )
2020-12-23 16:06:08 +00:00
bb : setUseCBB ( is_cbb_enabled )
is_cbb_enabled = bb : enableCBB ( G_reader_settings : nilOrFalse ( " dev_no_c_blitter " ) )
2020-09-22 21:26:05 +00:00
G_reader_settings : saveSetting ( " dev_no_c_blitter " , not is_cbb_enabled )
2019-03-29 19:12:09 +00:00
2019-12-06 21:55:30 +00:00
-- Should check DEBUG option in arg and turn on DEBUG before loading other
2019-09-11 18:00:44 +00:00
-- modules, otherwise DEBUG in some modules may not be printed.
local dbg = require ( " dbg " )
if G_reader_settings : isTrue ( " debug " ) then dbg : turnOn ( ) end
if G_reader_settings : isTrue ( " debug " ) and G_reader_settings : isTrue ( " debug_verbose " ) then dbg : setVerbose ( true ) end
2019-02-18 16:01:00 +00:00
2019-12-06 21:55:30 +00:00
-- Option parsing:
2013-10-18 20:38:07 +00:00
local longopts = {
2014-03-13 13:52:43 +00:00
debug = " d " ,
2020-12-12 01:27:18 +00:00
verbose = " d " ,
2014-03-13 13:52:43 +00:00
profile = " p " ,
help = " h " ,
2012-06-11 16:37:58 +00:00
}
2014-09-03 04:09:25 +00:00
local function showusage ( )
2014-06-05 06:58:53 +00:00
print ( " usage: ./reader.lua [OPTION] ... path " )
print ( " Read all the books on your E-Ink reader " )
2014-03-13 13:52:43 +00:00
print ( " " )
2014-06-05 06:58:53 +00:00
print ( " -d start in debug mode " )
2016-08-12 06:05:18 +00:00
print ( " -v debug in verbose mode " )
2014-06-05 11:06:35 +00:00
print ( " -p enable Lua code profiling " )
2014-06-05 06:58:53 +00:00
print ( " -h show this usage help " )
2014-03-13 13:52:43 +00:00
print ( " " )
2014-06-05 06:58:53 +00:00
print ( " If you give the name of a directory instead of a file path, a file " )
print ( " chooser will show up and let you select a file " )
2014-03-13 13:52:43 +00:00
print ( " " )
2021-01-07 19:38:10 +00:00
print ( " If you don't pass any path, the File Manager will be opened " )
2014-03-13 13:52:43 +00:00
print ( " " )
2014-06-05 11:23:54 +00:00
print ( " This software is licensed under the AGPLv3. " )
2014-06-05 06:58:53 +00:00
print ( " See http://github.com/koreader/koreader for more info. " )
2012-06-11 16:37:58 +00:00
end
2021-01-07 19:38:10 +00:00
local function getPathFromURI ( str )
local hexToChar = function ( x )
return string.char ( tonumber ( x , 16 ) )
end
local unescape = function ( url )
return url : gsub ( " %%(%x%x) " , hexToChar )
end
local prefix = " file:// "
if str : sub ( 1 , # prefix ) ~= prefix then
return str
end
return unescape ( str ) : sub ( # prefix + 1 )
end
local lfs = require ( " libs/libkoreader-lfs " )
local file
local directory
2014-11-06 07:07:50 +00:00
local Profiler = nil
2014-10-03 08:11:53 +00:00
local ARGV = arg
2012-11-01 02:02:53 +00:00
local argidx = 1
2013-08-06 14:53:44 +00:00
while argidx <= # ARGV do
2014-03-13 13:52:43 +00:00
local arg = ARGV [ argidx ]
argidx = argidx + 1
if arg == " -- " then break end
-- parse longopts
if arg : sub ( 1 , 2 ) == " -- " then
local opt = longopts [ arg : sub ( 3 ) ]
if opt ~= nil then arg = " - " .. opt end
end
-- code for each option
if arg == " -h " then
return showusage ( )
elseif arg == " -d " then
2016-10-25 07:08:47 +00:00
dbg : turnOn ( )
2016-08-12 06:05:18 +00:00
elseif arg == " -v " then
2016-10-25 07:08:47 +00:00
dbg : setVerbose ( true )
2014-03-13 13:52:43 +00:00
elseif arg == " -p " then
2014-05-01 03:58:05 +00:00
Profiler = require ( " jit.p " )
Profiler.start ( " la " )
2014-03-13 13:52:43 +00:00
else
2021-01-07 19:38:10 +00:00
-- not a recognized option, should be a filename or directory
local sanitized_path = getPathFromURI ( arg )
local mode = lfs.attributes ( sanitized_path , " mode " )
if mode == " file " then
file = sanitized_path
elseif mode == " directory " or mode == " link " then
directory = sanitized_path
end
2014-03-13 13:52:43 +00:00
break
end
2012-06-11 16:37:58 +00:00
end
2019-12-06 21:55:30 +00:00
-- Setup device
2019-09-11 18:00:44 +00:00
local Device = require ( " device " )
2019-12-06 21:55:30 +00:00
-- DPI
2019-09-11 18:00:44 +00:00
local dpi_override = G_reader_settings : readSetting ( " screen_dpi " )
if dpi_override ~= nil then
Device : setScreenDPI ( dpi_override )
end
2019-12-06 21:55:30 +00:00
-- Night mode
2022-03-14 18:56:18 +00:00
local hw_nightmode = Device.screen : getHWNightmode ( )
2019-04-08 21:05:08 +00:00
if G_reader_settings : isTrue ( " night_mode " ) then
2016-03-21 23:30:45 +00:00
Device.screen : toggleNightMode ( )
2014-06-08 06:06:04 +00:00
end
2021-01-07 22:58:30 +00:00
-- Ensure the proper rotation on startup.
-- We default to the rotation KOReader closed with.
-- If the rotation is not locked it will be overridden by a book or the FM when opened.
local rotation_mode = G_reader_settings : readSetting ( " closed_rotation_mode " )
if rotation_mode and rotation_mode ~= Device.screen : getRotationMode ( ) then
Device.screen : setRotationMode ( rotation_mode )
end
2019-12-06 21:55:30 +00:00
-- Dithering
2019-04-18 21:26:53 +00:00
if Device : hasEinkScreen ( ) then
Device.screen : setupDithering ( )
if Device.screen . hw_dithering and G_reader_settings : isTrue ( " dev_no_hw_dither " ) then
2020-07-23 04:01:46 +00:00
Device.screen : toggleHWDithering ( false )
2019-04-18 21:26:53 +00:00
end
if Device.screen . sw_dithering and G_reader_settings : isTrue ( " dev_no_sw_dither " ) then
2020-07-23 04:01:46 +00:00
Device.screen : toggleSWDithering ( false )
end
-- NOTE: If device can HW dither (i.e., after setupDithering(), hw_dithering is true, but sw_dithering is false),
-- but HW dither is explicitly disabled, and SW dither enabled, don't leave SW dither disabled (i.e., re-enable sw_dithering)!
if Device : canHWDither ( ) and G_reader_settings : isTrue ( " dev_no_hw_dither " ) and G_reader_settings : nilOrFalse ( " dev_no_sw_dither " ) then
Device.screen : toggleSWDithering ( true )
2019-04-18 21:26:53 +00:00
end
end
2012-06-11 16:37:58 +00:00
2019-12-06 21:55:30 +00:00
-- Document renderers canvas
local CanvasContext = require ( " document/canvascontext " )
CanvasContext : init ( Device )
2021-04-13 15:54:11 +00:00
-- Handle one time migration stuff (settings, deprecation, ...) in case of an upgrade...
require ( " ui/data/onetime_migration " )
2020-01-02 13:00:55 +00:00
-- Touch screen (this may display some widget, on first install on Kobo Touch,
-- so have it done after CanvasContext:init() but before Bidi.setup() to not
-- have mirroring mess x/y probing).
if Device : needsTouchScreenProbe ( ) then
Device : touchScreenProbe ( )
end
2019-12-06 21:55:33 +00:00
-- UI mirroring for RTL languages, and text shaping configuration
local Bidi = require ( " ui/bidi " )
Bidi.setup ( lang_locale )
-- Avoid loading UIManager and widgets before here, as they may
-- cache Bidi mirroring settings. Check that with:
-- for name, _ in pairs(package.loaded) do print(name) end
2019-12-06 21:55:30 +00:00
-- User fonts override
local fontmap = G_reader_settings : readSetting ( " fontmap " )
if fontmap ~= nil then
local Font = require ( " ui/font " )
for k , v in pairs ( fontmap ) do
Font.fontmap [ k ] = v
end
end
local UIManager = require ( " ui/uimanager " )
2017-10-02 13:17:17 +00:00
-- Inform once about color rendering on newly supported devices
-- (there are some android devices that may not have a color screen,
-- and we are not (yet?) able to guess that fact)
2019-04-08 21:05:08 +00:00
if Device : hasColorScreen ( ) and not G_reader_settings : has ( " color_rendering " ) then
2017-10-02 13:17:17 +00:00
-- enable it to prevent further display of this message
2021-03-06 21:44:18 +00:00
G_reader_settings : makeTrue ( " color_rendering " )
2017-10-02 13:17:17 +00:00
local InfoMessage = require ( " ui/widget/infomessage " )
UIManager : show ( InfoMessage : new {
text = _ ( " Documents will be rendered in color on this device. \n If your device is grayscale, you can disable color rendering in the screen sub-menu for reduced memory usage. " ) ,
} )
end
2020-02-18 01:24:48 +00:00
-- Conversely, if color is enabled on a Grayscale screen (e.g., after importing settings from a color device), warn that it'll break stuff and adversely affect performance.
if G_reader_settings : isTrue ( " color_rendering " ) and not Device : hasColorScreen ( ) then
local ConfirmBox = require ( " ui/widget/confirmbox " )
UIManager : show ( ConfirmBox : new {
text = _ ( " Color rendering is mistakenly enabled on your grayscale device. \n This will subtly break some features, and adversely affect performance. " ) ,
cancel_text = _ ( " Ignore " ) ,
cancel_callback = function ( )
return
end ,
ok_text = _ ( " Disable " ) ,
ok_callback = function ( )
local Event = require ( " ui/event " )
G_reader_settings : delSetting ( " color_rendering " )
CanvasContext : setColorRenderingEnabled ( false )
UIManager : broadcastEvent ( Event : new ( " ColorRenderingUpdate " ) )
end ,
} )
end
2021-01-07 19:38:10 +00:00
-- Get which file to start with
local last_file = G_reader_settings : readSetting ( " lastfile " )
local start_with = G_reader_settings : readSetting ( " start_with " ) or " filemanager "
2019-12-06 21:55:30 +00:00
-- Helpers
local function retryLastFile ( )
local ConfirmBox = require ( " ui/widget/confirmbox " )
return ConfirmBox : new {
text = _ ( " Cannot open last file. \n This could be because it was deleted or because external storage is still being mounted. \n Do you want to retry? " ) ,
ok_callback = function ( )
2021-01-07 19:38:10 +00:00
if lfs.attributes ( last_file , " mode " ) ~= " file " then
2019-12-06 21:55:30 +00:00
UIManager : show ( retryLastFile ( ) )
end
end ,
2021-01-07 19:38:10 +00:00
cancel_callback = function ( )
start_with = " filemanager "
end ,
2019-12-06 21:55:30 +00:00
}
end
2017-05-16 09:11:11 +00:00
2021-01-07 19:38:10 +00:00
-- Start app
local exit_code
if file then
local ReaderUI = require ( " apps/reader/readerui " )
UIManager : nextTick ( function ( )
ReaderUI : showReader ( file )
end )
exit_code = UIManager : run ( )
elseif directory then
local FileManager = require ( " apps/filemanager/filemanager " )
UIManager : nextTick ( function ( )
FileManager : showFiles ( directory )
end )
exit_code = UIManager : run ( )
2019-12-06 21:55:30 +00:00
else
local QuickStart = require ( " ui/quickstart " )
if not QuickStart : isShown ( ) then
2021-01-07 19:38:10 +00:00
start_with = " last "
2019-12-06 21:55:30 +00:00
last_file = QuickStart : getQuickStart ( )
end
2021-01-07 19:38:10 +00:00
if start_with == " last " and last_file and lfs.attributes ( last_file , " mode " ) ~= " file " then
UIManager : show ( retryLastFile ( ) )
-- no exit code as something else will be run after this.
UIManager : run ( )
2014-09-03 04:09:25 +00:00
end
2021-01-07 19:38:10 +00:00
if start_with == " last " and last_file then
2014-09-03 04:09:25 +00:00
local ReaderUI = require ( " apps/reader/readerui " )
2016-03-09 06:52:36 +00:00
UIManager : nextTick ( function ( )
ReaderUI: Saner FM/RD lifecycle
* Ensure that going from one to the other tears down the former and
its plugins before instantiating the latter and its plugins.
UIManager: Unify Event sending & broadcasting
* Make the two behave the same way (walk the widget stack from top to
bottom), and properly handle the window stack shrinking shrinking
*and* growing.
Previously, broadcasting happened bottom-to-top and didn't really
handle the list shrinking/growing, while sending only handled the list
shrinking by a single element, and hopefully that element being the one
the event was just sent to.
These two items combined allowed us to optimize suboptimal
refresh behavior with Menu and other Menu classes when
opening/closing a document.
e.g., the "opening document" Notification is now properly regional,
and the "open last doc" option no longer flashes like a crazy person
anymore.
Plugins: Allow optimizing Menu refresh with custom menus, too.
Requires moving Menu's close_callback *after* onMenuSelect, which, eh,
probably makes sense, and is probably harmless in the grand scheme of
things.
2021-05-01 16:53:04 +00:00
-- Instantiate RD
2021-01-07 19:38:10 +00:00
ReaderUI : showReader ( last_file )
2016-03-09 06:52:36 +00:00
end )
2021-01-07 19:38:10 +00:00
exit_code = UIManager : run ( )
2014-03-13 13:52:43 +00:00
else
2014-09-03 04:09:25 +00:00
local FileManager = require ( " apps/filemanager/filemanager " )
2016-02-25 08:54:41 +00:00
local home_dir =
2021-01-07 19:38:10 +00:00
G_reader_settings : readSetting ( " home_dir " ) or Device.home_dir or lfs.currentdir ( )
2016-03-09 06:52:36 +00:00
UIManager : nextTick ( function ( )
ReaderUI: Saner FM/RD lifecycle
* Ensure that going from one to the other tears down the former and
its plugins before instantiating the latter and its plugins.
UIManager: Unify Event sending & broadcasting
* Make the two behave the same way (walk the widget stack from top to
bottom), and properly handle the window stack shrinking shrinking
*and* growing.
Previously, broadcasting happened bottom-to-top and didn't really
handle the list shrinking/growing, while sending only handled the list
shrinking by a single element, and hopefully that element being the one
the event was just sent to.
These two items combined allowed us to optimize suboptimal
refresh behavior with Menu and other Menu classes when
opening/closing a document.
e.g., the "opening document" Notification is now properly regional,
and the "open last doc" option no longer flashes like a crazy person
anymore.
Plugins: Allow optimizing Menu refresh with custom menus, too.
Requires moving Menu's close_callback *after* onMenuSelect, which, eh,
probably makes sense, and is probably harmless in the grand scheme of
things.
2021-05-01 16:53:04 +00:00
-- Instantiate FM
2016-03-09 06:52:36 +00:00
FileManager : showFiles ( home_dir )
end )
ReaderUI: Saner FM/RD lifecycle
* Ensure that going from one to the other tears down the former and
its plugins before instantiating the latter and its plugins.
UIManager: Unify Event sending & broadcasting
* Make the two behave the same way (walk the widget stack from top to
bottom), and properly handle the window stack shrinking shrinking
*and* growing.
Previously, broadcasting happened bottom-to-top and didn't really
handle the list shrinking/growing, while sending only handled the list
shrinking by a single element, and hopefully that element being the one
the event was just sent to.
These two items combined allowed us to optimize suboptimal
refresh behavior with Menu and other Menu classes when
opening/closing a document.
e.g., the "opening document" Notification is now properly regional,
and the "open last doc" option no longer flashes like a crazy person
anymore.
Plugins: Allow optimizing Menu refresh with custom menus, too.
Requires moving Menu's close_callback *after* onMenuSelect, which, eh,
probably makes sense, and is probably harmless in the grand scheme of
things.
2021-05-01 16:53:04 +00:00
-- Always open FM modules on top of filemanager, so closing 'em doesn't result in an exit
-- because of an empty widget stack, and so they can interact with the FM instance as expected.
2017-08-14 11:15:12 +00:00
if start_with == " history " then
local FileManagerHistory = require ( " apps/filemanager/filemanagerhistory " )
UIManager : nextTick ( function ( )
2021-04-29 17:39:59 +00:00
FileManagerHistory : new {
ui = FileManager.instance ,
} : onShowHist ( )
2017-08-14 11:15:12 +00:00
end )
2020-08-26 18:44:56 +00:00
elseif start_with == " favorites " then
local FileManagerCollection = require ( " apps/filemanager/filemanagercollection " )
UIManager : nextTick ( function ( )
FileManagerCollection : new {
ui = FileManager.instance ,
} : onShowColl ( " favorites " )
end )
2018-09-12 19:42:24 +00:00
elseif start_with == " folder_shortcuts " then
local FileManagerShortcuts = require ( " apps/filemanager/filemanagershortcuts " )
UIManager : nextTick ( function ( )
2019-03-11 10:10:46 +00:00
FileManagerShortcuts : new {
ui = FileManager.instance ,
} : onShowFolderShortcutsDialog ( )
2018-09-12 19:42:24 +00:00
end )
2017-08-14 11:15:12 +00:00
end
2021-01-07 19:38:10 +00:00
exit_code = UIManager : run ( )
2014-03-13 13:52:43 +00:00
end
2012-10-02 22:45:45 +00:00
end
2019-12-06 21:55:30 +00:00
-- Exit
2014-11-06 07:07:50 +00:00
local function exitReader ( )
2020-09-01 19:43:21 +00:00
-- Exit code can be shoddy on some platforms due to broken library dtors calling _exit(0) from os.exit(N)
local ko_exit = os.getenv ( " KO_EXIT_CODE " )
if ko_exit then
local fo = io.open ( ko_exit , " w+ " )
fo : write ( tostring ( exit_code ) )
fo : close ( )
end
2016-02-25 08:54:41 +00:00
local ReaderActivityIndicator =
require ( " apps/reader/modules/readeractivityindicator " )
2014-11-06 07:07:50 +00:00
2017-09-04 19:05:05 +00:00
-- Save any device settings before closing G_reader_settings
Device : saveSettings ( )
2021-01-12 21:31:38 +00:00
-- Save current rotation (or the original rotation if ScreenSaver temporarily modified it) to remember it for next startup
G_reader_settings : saveSetting ( " closed_rotation_mode " , Device.orig_rotation_mode or Device.screen : getRotationMode ( ) )
2014-11-06 07:07:50 +00:00
G_reader_settings : close ( )
-- Close lipc handles
ReaderActivityIndicator : coda ( )
2022-03-14 18:56:18 +00:00
-- Restore initial inversion state
Device.screen : setHWNightmode ( hw_nightmode )
2014-11-06 07:07:50 +00:00
-- shutdown hardware abstraction
Device : exit ( )
if Profiler then Profiler.stop ( ) end
2017-05-16 09:11:11 +00:00
if type ( exit_code ) == " number " then
2020-12-12 01:27:18 +00:00
return exit_code
2017-05-16 09:11:11 +00:00
else
2020-12-24 23:38:31 +00:00
return true
2017-05-16 09:11:11 +00:00
end
2014-11-06 07:07:50 +00:00
end
2020-12-12 01:27:18 +00:00
local ret = exitReader ( )
2020-12-24 23:38:31 +00:00
-- Close the Lua state on exit
os.exit ( ret , true )