From 350bc060516184774f8116e61696a8c3c45ba85d Mon Sep 17 00:00:00 2001 From: Niki Roo Date: Thu, 14 Mar 2019 09:39:26 +0100 Subject: [PATCH] reader: sync/async work --- TODO.md | 7 ++- src/be/nikiroo/fanfix/Main.java | 27 ++++----- src/be/nikiroo/fanfix/reader/BasicReader.java | 40 +++++++++++--- src/be/nikiroo/fanfix/reader/Reader.java | 16 +++++- .../fanfix/reader/android/AndroidReader.java | 8 +-- .../nikiroo/fanfix/reader/cli/CliReader.java | 2 +- .../nikiroo/fanfix/reader/tui/TuiReader.java | 2 +- .../reader/tui/TuiReaderApplication.java | 33 +++++++---- .../reader/tui/TuiReaderMainWindow.java | 5 +- .../nikiroo/fanfix/reader/ui/GuiReader.java | 55 +++++++++++++++---- .../fanfix/reader/ui/GuiReaderFrame.java | 2 +- 11 files changed, 137 insertions(+), 60 deletions(-) diff --git a/TODO.md b/TODO.md index ff3396ba..7843c812 100644 --- a/TODO.md +++ b/TODO.md @@ -6,8 +6,9 @@ My current planning for Fanfix (but not everything appears on this list): - [x] New API on FimFiction.net (faster) - [ ] Others? Any ideas? I'm open for requests - [x] [e-Hentai](https://e-hentai.org/) requested - - [ ] Find some FR comics/manga websites -- [x] A GUI library + - [x] Find some FR comics/manga websites + - [ ] Find more FR thingies +- [ ] A GUI library - [x] Make one - [x] Make it run when no args passed - [x] Fix the UI, it is ugly @@ -19,6 +20,8 @@ My current planning for Fanfix (but not everything appears on this list): - [x] ..as a screen view - [x] options screen - [x] support progress events + - [x] Real menus + - [ ] Store the long lists in [A-B], [BA-BB], ... - [ ] A TUI library - [x] Choose an output (Jexer) - [x] Implement it from --set-reader to the actual window diff --git a/src/be/nikiroo/fanfix/Main.java b/src/be/nikiroo/fanfix/Main.java index 53c4a862..f83186c1 100644 --- a/src/be/nikiroo/fanfix/Main.java +++ b/src/be/nikiroo/fanfix/Main.java @@ -354,18 +354,12 @@ public class Main { } } - // We cannot do it when in GUI mode, because it is async... - // So if we close the temp files before it is actually used, - // we have a problem... - // TODO: close it at the correct time (for now, finalize try to do it) - if (false) { - try { - Instance.getTempFiles().close(); - } catch (IOException e) { - Instance.getTraceHandler().error( - new IOException( - "Cannot dispose of the temporary files", e)); - } + try { + Instance.getTempFiles().close(); + } catch (IOException e) { + Instance.getTraceHandler() + .error(new IOException( + "Cannot dispose of the temporary files", e)); } if (exitCode == 255) { @@ -462,7 +456,7 @@ public class Main { } /** - * Start the CLI reader for this {@link Story}. + * Start the current reader for this {@link Story}. * * @param story * the LUID of the {@link Story} in the {@link LocalLibrary} @@ -488,7 +482,7 @@ public class Main { if (chapString != null) { try { reader.setChapter(Integer.parseInt(chapString)); - reader.read(); + reader.read(true); } catch (NumberFormatException e) { Instance.getTraceHandler().error( new IOException("Chapter number cannot be parsed: " @@ -496,7 +490,7 @@ public class Main { return 2; } } else { - reader.read(); + reader.read(true); } } catch (IOException e) { Instance.getTraceHandler().error(e); @@ -548,7 +542,8 @@ public class Main { BasicSupport support = BasicSupport.getSupport(source); if (support != null) { - Instance.getTraceHandler().trace("Support found: " + support.getClass()); + Instance.getTraceHandler().trace( + "Support found: " + support.getClass()); Progress pgIn = new Progress(); Progress pgOut = new Progress(); if (pg != null) { diff --git a/src/be/nikiroo/fanfix/reader/BasicReader.java b/src/be/nikiroo/fanfix/reader/BasicReader.java index 778b6338..71f19a10 100644 --- a/src/be/nikiroo/fanfix/reader/BasicReader.java +++ b/src/be/nikiroo/fanfix/reader/BasicReader.java @@ -91,8 +91,7 @@ public abstract class BasicReader implements Reader { } @Override - public synchronized void setMeta(URL url, Progress pg) - throws IOException { + public synchronized void setMeta(URL url, Progress pg) throws IOException { BasicSupport support = BasicSupport.getSupport(url); if (support == null) { throw new IOException("URL not supported: " + url.toString()); @@ -208,16 +207,20 @@ public abstract class BasicReader implements Reader { * the {@link BasicLibrary} to select the {@link Story} from * @param luid * the {@link Story} LUID + * @param sync + * execute the process synchronously (wait until it is terminated + * before returning) * * @throws IOException * in case of I/O error */ @Override - public void openExternal(BasicLibrary lib, String luid) throws IOException { + public void openExternal(BasicLibrary lib, String luid, boolean sync) + throws IOException { MetaData meta = lib.getInfo(luid); File target = lib.getFile(luid, null); - openExternal(meta, target); + openExternal(meta, target, sync); } /** @@ -228,11 +231,15 @@ public abstract class BasicReader implements Reader { * the {@link Story} to load * @param target * the target {@link File} + * @param sync + * execute the process synchronously (wait until it is terminated + * before returning) * * @throws IOException * in case of I/O error */ - protected void openExternal(MetaData meta, File target) throws IOException { + protected void openExternal(MetaData meta, File target, boolean sync) + throws IOException { String program = null; if (meta.isImageDocument()) { program = Instance.getUiConfig().getString( @@ -246,7 +253,7 @@ public abstract class BasicReader implements Reader { program = null; } - start(target, program); + start(target, program, sync); } /** @@ -257,11 +264,17 @@ public abstract class BasicReader implements Reader { * the target to open * @param program * the program to use or NULL for the default system starter + * @param sync + * execute the process synchronously (wait until it is terminated + * before returning) * * @throws IOException * in case of I/O error */ - protected void start(File target, String program) throws IOException { + protected void start(File target, String program, boolean sync) + throws IOException { + + Process proc = null; if (program == null) { boolean ok = false; for (String starter : new String[] { "xdg-open", "open", "see", @@ -269,7 +282,7 @@ public abstract class BasicReader implements Reader { try { Instance.getTraceHandler().trace( "starting external program"); - Runtime.getRuntime().exec( + proc = Runtime.getRuntime().exec( new String[] { starter, target.getAbsolutePath() }); ok = true; break; @@ -281,8 +294,17 @@ public abstract class BasicReader implements Reader { } } else { Instance.getTraceHandler().trace("starting external program"); - Runtime.getRuntime().exec( + proc = Runtime.getRuntime().exec( new String[] { program, target.getAbsolutePath() }); } + + if (proc != null && sync) { + while (proc.isAlive()) { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + } + } + } } } diff --git a/src/be/nikiroo/fanfix/reader/Reader.java b/src/be/nikiroo/fanfix/reader/Reader.java index 5fe1c5d6..b001e304 100644 --- a/src/be/nikiroo/fanfix/reader/Reader.java +++ b/src/be/nikiroo/fanfix/reader/Reader.java @@ -130,11 +130,15 @@ public interface Reader { /** * Start the {@link Story} Reading. * + * @param sync + * execute the process synchronously (wait until it is terminated + * before returning) + * * @throws IOException * in case of I/O error or if the {@link Story} was not * previously set */ - public void read() throws IOException; + public void read(boolean sync) throws IOException; /** * The selected chapter to start reading at (starting at 1, 0 = description, @@ -156,6 +160,8 @@ public interface Reader { /** * Start the reader in browse mode for the given source (or pass NULL for * all sources). + *

+ * Note that this must be a synchronous action. * * @param source * the type of {@link Story} to take into account, or NULL for @@ -164,16 +170,20 @@ public interface Reader { public void browse(String source); /** - * Open the {@link Story} with an external reader (the program will be + * Open the {@link Story} with an external reader (the program should be * passed the main file associated with this {@link Story}). * * @param lib * the {@link BasicLibrary} to select the {@link Story} from * @param luid * the {@link Story} LUID + * @param sync + * execute the process synchronously (wait until it is terminated + * before returning) * * @throws IOException * in case of I/O error */ - public void openExternal(BasicLibrary lib, String luid) throws IOException; + public void openExternal(BasicLibrary lib, String luid, boolean sync) + throws IOException; } diff --git a/src/be/nikiroo/fanfix/reader/android/AndroidReader.java b/src/be/nikiroo/fanfix/reader/android/AndroidReader.java index 85f07f25..7dcdd045 100644 --- a/src/be/nikiroo/fanfix/reader/android/AndroidReader.java +++ b/src/be/nikiroo/fanfix/reader/android/AndroidReader.java @@ -24,7 +24,7 @@ public class AndroidReader extends BasicReader { } @Override - public void read() throws IOException { + public void read(boolean sync) throws IOException { } @Override @@ -32,7 +32,7 @@ public class AndroidReader extends BasicReader { } @Override - protected void start(File target, String program) throws IOException { + protected void start(File target, String program, boolean sync) throws IOException { if (program == null) { try { Intent[] intents = new Intent[] { // @@ -53,10 +53,10 @@ public class AndroidReader extends BasicReader { app.startActivity(chooserIntent); } catch (UnsupportedOperationException e) { - super.start(target, program); + super.start(target, program, sync); } } else { - super.start(target, program); + super.start(target, program, sync); } } } diff --git a/src/be/nikiroo/fanfix/reader/cli/CliReader.java b/src/be/nikiroo/fanfix/reader/cli/CliReader.java index b28d4d4e..9ec37a58 100644 --- a/src/be/nikiroo/fanfix/reader/cli/CliReader.java +++ b/src/be/nikiroo/fanfix/reader/cli/CliReader.java @@ -20,7 +20,7 @@ import be.nikiroo.fanfix.reader.BasicReader; */ class CliReader extends BasicReader { @Override - public void read() throws IOException { + public void read(boolean sync) throws IOException { MetaData meta = getMeta(); if (meta == null) { diff --git a/src/be/nikiroo/fanfix/reader/tui/TuiReader.java b/src/be/nikiroo/fanfix/reader/tui/TuiReader.java index 7192854e..f94f7838 100644 --- a/src/be/nikiroo/fanfix/reader/tui/TuiReader.java +++ b/src/be/nikiroo/fanfix/reader/tui/TuiReader.java @@ -50,7 +50,7 @@ class TuiReader extends BasicReader { } @Override - public void read() throws IOException { + public void read(boolean sync) throws IOException { try { TuiReaderApplication app = new TuiReaderApplication(this, guessBackendType()); diff --git a/src/be/nikiroo/fanfix/reader/tui/TuiReaderApplication.java b/src/be/nikiroo/fanfix/reader/tui/TuiReaderApplication.java index 8cd5c0b9..f08b84cc 100644 --- a/src/be/nikiroo/fanfix/reader/tui/TuiReaderApplication.java +++ b/src/be/nikiroo/fanfix/reader/tui/TuiReaderApplication.java @@ -56,9 +56,8 @@ class TuiReaderApplication extends TApplication implements Reader { super(backend); init(reader); - MetaData meta = getMeta(); - if (meta != null) { - read(); + if (getMeta() != null) { + read(false); } } @@ -72,8 +71,8 @@ class TuiReaderApplication extends TApplication implements Reader { } @Override - public void read() throws IOException { - read(getStory(null)); + public void read(boolean sync) throws IOException { + read(getStory(null), sync); } @Override @@ -126,7 +125,20 @@ class TuiReaderApplication extends TApplication implements Reader { reader.setChapter(chapter); } - public void read(Story story) throws IOException { + /** + * Open the given {@link Story} for reading. This may or may not start an + * external program to read said {@link Story}. + * + * @param story + * the {@link Story} to read + * @param sync + * execute the process synchronously (wait until it is terminated + * before returning) + * + * @throws IOException + * in case of I/O errors + */ + public void read(Story story, boolean sync) throws IOException { if (story == null) { throw new IOException("No story to read"); } @@ -137,7 +149,7 @@ class TuiReaderApplication extends TApplication implements Reader { window.maximize(); } else { try { - openExternal(getLibrary(), story.getMeta().getLuid()); + openExternal(getLibrary(), story.getMeta().getLuid(), sync); } catch (IOException e) { messageBox("Error when trying to open the story", e.getMessage(), TMessageBox.Type.OK); @@ -230,7 +242,7 @@ class TuiReaderApplication extends TApplication implements Reader { try { openfile = fileOpenBox("."); reader.setMeta(BasicReader.getUrl(openfile), null); - read(); + read(false); } catch (IOException e) { // TODO: i18n error("Fail to open file" @@ -342,8 +354,9 @@ class TuiReaderApplication extends TApplication implements Reader { } @Override - public void openExternal(BasicLibrary lib, String luid) throws IOException { - reader.openExternal(lib, luid); + public void openExternal(BasicLibrary lib, String luid, boolean sync) + throws IOException { + reader.openExternal(lib, luid, sync); } /** diff --git a/src/be/nikiroo/fanfix/reader/tui/TuiReaderMainWindow.java b/src/be/nikiroo/fanfix/reader/tui/TuiReaderMainWindow.java index cbca857d..932cbcbb 100644 --- a/src/be/nikiroo/fanfix/reader/tui/TuiReaderMainWindow.java +++ b/src/be/nikiroo/fanfix/reader/tui/TuiReaderMainWindow.java @@ -22,7 +22,6 @@ import be.nikiroo.fanfix.Instance; import be.nikiroo.fanfix.data.MetaData; import be.nikiroo.fanfix.library.BasicLibrary; import be.nikiroo.fanfix.output.BasicOutput.OutputType; -import be.nikiroo.fanfix.reader.Reader; import be.nikiroo.jexer.TSizeConstraint; /** @@ -43,7 +42,7 @@ class TuiReaderMainWindow extends TWindow { private TList list; private List listKeys; private List listItems; - private Reader reader; + private TuiReaderApplication reader; private Mode mode = Mode.SOURCE; private String target = null; @@ -300,7 +299,7 @@ class TuiReaderMainWindow extends TWindow { try { reader.setChapter(-1); reader.setMeta(meta); - reader.read(); + reader.read(false); } catch (IOException e) { Instance.getTraceHandler().error(e); } diff --git a/src/be/nikiroo/fanfix/reader/ui/GuiReader.java b/src/be/nikiroo/fanfix/reader/ui/GuiReader.java index 519a507e..02f153cd 100644 --- a/src/be/nikiroo/fanfix/reader/ui/GuiReader.java +++ b/src/be/nikiroo/fanfix/reader/ui/GuiReader.java @@ -2,6 +2,8 @@ package be.nikiroo.fanfix.reader.ui; import java.awt.Desktop; import java.awt.EventQueue; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; import java.io.File; import java.io.IOException; import java.net.URISyntaxException; @@ -79,14 +81,14 @@ class GuiReader extends BasicReader { } @Override - public void read() throws IOException { + public void read(boolean sync) throws IOException { MetaData meta = getMeta(); if (meta == null) { throw new IOException("No story to read"); } - read(meta.getLuid(), null); + read(meta.getLuid(), sync, null); } /** @@ -107,6 +109,7 @@ class GuiReader extends BasicReader { // TODO: improve presentation of update message final VersionCheck updates = VersionCheck.check(); StringBuilder builder = new StringBuilder(); + final Boolean[] done = new Boolean[] { false }; final JEditorPane updateMessage = new JEditorPane("text/html", ""); if (updates.isNewVersionAvailable()) { @@ -162,21 +165,48 @@ class GuiReader extends BasicReader { } } - new GuiReaderFrame(GuiReader.this, typeFinal).setVisible(true); + try { + GuiReaderFrame gui = new GuiReaderFrame(GuiReader.this, + typeFinal); + gui.setVisible(true); + gui.addWindowListener(new WindowAdapter() { + @Override + public void windowClosed(WindowEvent e) { + super.windowClosed(e); + done[0] = true; + } + }); + } catch (Exception e) { + Instance.getTraceHandler().error(e); + done[0] = true; + } } }); + + // This action must be synchronous, so wait until the frame is closed + while (!done[0]) { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + } + } } @Override - public void start(File target, String program) throws IOException { - if (program == null) { + public void start(File target, String program, boolean sync) + throws IOException { + + boolean handled = false; + if (program == null && !sync) { try { Desktop.getDesktop().browse(target.toURI()); + handled = true; } catch (UnsupportedOperationException e) { - super.start(target, program); } - } else { - super.start(target, program); + } + + if (!handled) { + super.start(target, program, sync); } } @@ -218,9 +248,14 @@ class GuiReader extends BasicReader { /** * "Open" the given {@link Story}. It usually involves starting an external * program adapted to the given file type. + *

+ * Asynchronous method. * * @param luid * the luid of the {@link Story} to open + * @param sync + * execute the process synchronously (wait until it is terminated + * before returning) * @param pg * the optional progress (we may need to prepare the * {@link Story} for reading @@ -228,13 +263,13 @@ class GuiReader extends BasicReader { * @throws IOException * in case of I/O errors */ - void read(String luid, Progress pg) throws IOException { + void read(String luid, boolean sync, Progress pg) throws IOException { File file = cacheLib.getFile(luid, pg); // TODO: show a special page for the chapter? // We could also implement an internal viewer, both for image and // non-image documents - openExternal(getLibrary().getInfo(luid), file); + openExternal(getLibrary().getInfo(luid), file, sync); } /** diff --git a/src/be/nikiroo/fanfix/reader/ui/GuiReaderFrame.java b/src/be/nikiroo/fanfix/reader/ui/GuiReaderFrame.java index 39187b96..b8db8f74 100644 --- a/src/be/nikiroo/fanfix/reader/ui/GuiReaderFrame.java +++ b/src/be/nikiroo/fanfix/reader/ui/GuiReaderFrame.java @@ -858,7 +858,7 @@ class GuiReaderFrame extends JFrame { @Override public void run() { try { - reader.read(book.getMeta().getLuid(), pg); + reader.read(book.getMeta().getLuid(), false, pg); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { -- 2.27.0