diff --git a/.gitignore b/.gitignore
index bea0dc85e4..9bf866d3bc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,9 @@
 *~
 /build/shared/reference.zip
 
+# temporary, until we complete the move to IntelliJ
+*.iml
+/.idea
 
 # via https://github.com/github/gitignore/blob/master/Global/JetBrains.gitignore
 # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
diff --git a/app/src/processing/app/RunnerListenerEdtAdapter.java b/app/src/processing/app/RunnerListenerEdtAdapter.java
new file mode 100644
index 0000000000..a436eefbca
--- /dev/null
+++ b/app/src/processing/app/RunnerListenerEdtAdapter.java
@@ -0,0 +1,48 @@
+package processing.app;
+
+import java.awt.EventQueue;
+
+public class RunnerListenerEdtAdapter implements RunnerListener {
+
+  private RunnerListener wrapped;
+
+  public RunnerListenerEdtAdapter(RunnerListener wrapped) {
+    this.wrapped = wrapped;
+  }
+
+  @Override
+  public void statusError(String message) {
+    EventQueue.invokeLater(() -> wrapped.statusError(message));
+  }
+
+  @Override
+  public void statusError(Exception exception) {
+    EventQueue.invokeLater(() -> wrapped.statusError(exception));
+  }
+
+  @Override
+  public void statusNotice(String message) {
+    EventQueue.invokeLater(() -> wrapped.statusNotice(message));
+  }
+
+  @Override
+  public void startIndeterminate() {
+    EventQueue.invokeLater(() -> wrapped.startIndeterminate());
+  }
+
+  @Override
+  public void stopIndeterminate() {
+    EventQueue.invokeLater(() -> wrapped.stopIndeterminate());
+  }
+
+  @Override
+  public void statusHalt() {
+    EventQueue.invokeLater(() -> wrapped.statusHalt());
+  }
+
+  @Override
+  public boolean isHalted() {
+    return wrapped.isHalted();
+  }
+}
+
diff --git a/app/src/processing/app/Sketch.java b/app/src/processing/app/Sketch.java
index 40a3d7fb2b..7d3caf8988 100644
--- a/app/src/processing/app/Sketch.java
+++ b/app/src/processing/app/Sketch.java
@@ -91,6 +91,8 @@ public class Sketch {
   /** Moved out of Editor and into here for cleaner access. */
   private boolean untitled;
 
+  /** true if we've posted a "sketch disappeared" warning */
+  private boolean disappearedWarning;
 
   /**
    * Used by the command-line version to create a sketch object.
@@ -123,6 +125,7 @@ protected void load(String path) {
     int suffixLength = mode.getDefaultExtension().length() + 1;
     name = mainFilename.substring(0, mainFilename.length() - suffixLength);
     folder = new File(new File(path).getParent());
+    disappearedWarning = false;
     load();
   }
 
@@ -1197,6 +1200,7 @@ protected void updateInternal(String sketchName, File sketchFolder) {
 
     name = sketchName;
     folder = sketchFolder;
+    disappearedWarning = false;
     codeFolder = new File(folder, "code");
     dataFolder = new File(folder, "data");
 
@@ -1498,28 +1502,34 @@ public void prepareBuild(File targetFolder) throws SketchException {
 
 
   /**
-   * Make sure the sketch hasn't been moved or deleted by some
-   * nefarious user. If they did, try to re-create it and save.
-   * Only checks to see if the main folder is still around,
-   * but not its contents.
+   * Make sure the sketch hasn't been moved or deleted by a nefarious user.
+   * If they did, try to re-create it and save. Only checks whether the
+   * main folder is still around, but not its contents.
    */
   public void ensureExistence() {
     if (!folder.exists()) {
-      // Disaster recovery, try to salvage what's there already.
-      Messages.showWarning(Language.text("ensure_exist.messages.missing_sketch"),
-                           Language.text("ensure_exist.messages.missing_sketch.description"));
-      try {
-        folder.mkdirs();
-        modified = true;
+      // Avoid an infinite loop if we've already warned about this
+      // https://github.com/processing/processing/issues/4805
+      if (!disappearedWarning) {
+        disappearedWarning = true;
+
+        // Disaster recovery, try to salvage what's there already.
+        Messages.showWarning(Language.text("ensure_exist.messages.missing_sketch"),
+                             Language.text("ensure_exist.messages.missing_sketch.description"));
+        try {
+          folder.mkdirs();
+          modified = true;
+
+          for (int i = 0; i < codeCount; i++) {
+            code[i].save();  // this will force a save
+          }
+          calcModified();
 
-        for (int i = 0; i < codeCount; i++) {
-          code[i].save();  // this will force a save
+        } catch (Exception e) {
+          // disappearedWarning prevents infinite loop in this scenario
+          Messages.showWarning(Language.text("ensure_exist.messages.unrecoverable"),
+                               Language.text("ensure_exist.messages.unrecoverable.description"), e);
         }
-        calcModified();
-
-      } catch (Exception e) {
-        Messages.showWarning(Language.text("ensure_exist.messages.unrecoverable"),
-                             Language.text("ensure_exist.messages.unrecoverable.description"), e);
       }
     }
   }
diff --git a/app/src/processing/app/SketchException.java b/app/src/processing/app/SketchException.java
index ccf8b924e6..4a32d2e79d 100644
--- a/app/src/processing/app/SketchException.java
+++ b/app/src/processing/app/SketchException.java
@@ -130,6 +130,11 @@ public void hideStackTrace() {
   }
 
 
+  public boolean isStackTraceEnabled() {
+    return showStackTrace;
+  }
+
+
   /**
    * Nix the java.lang crap out of an exception message
    * because it scares the children.
diff --git a/app/src/processing/app/ui/Editor.java b/app/src/processing/app/ui/Editor.java
index be85d5b5bb..0fbd5b1ea6 100644
--- a/app/src/processing/app/ui/Editor.java
+++ b/app/src/processing/app/ui/Editor.java
@@ -320,6 +320,17 @@ public BasicSplitPaneDivider createDefaultDivider() {
         status = new EditorStatus(this, Editor.this);
         return status;
       }
+
+
+      @Override
+      public void finishDraggingTo(int location) {
+        super.finishDraggingTo(location);
+        // JSplitPane issue: if you only make the lower component visible at
+        // the last minute, its minmum size is ignored.
+        if (location > splitPane.getMaximumDividerLocation()) {
+          splitPane.setDividerLocation(splitPane.getMaximumDividerLocation());
+        }
+      }
     });
 
     box.add(splitPane);
@@ -2900,6 +2911,17 @@ public void statusError(Exception e) {
 
     if (e instanceof SketchException) {
       SketchException re = (SketchException) e;
+
+      // Make sure something is printed into the console
+      // Status bar is volatile
+      if (!re.isStackTraceEnabled()) {
+        System.err.println(re.getMessage());
+      }
+
+      // Move the cursor to the line before updating the status bar, otherwise
+      // status message might get hidden by a potential message caused by moving
+      // the cursor to a line with warning in it
+
       if (re.hasCodeIndex()) {
         sketch.setCurrentCode(re.getCodeIndex());
       }
diff --git a/app/src/processing/app/ui/EditorConsole.java b/app/src/processing/app/ui/EditorConsole.java
index da7fe54728..653fe40c84 100644
--- a/app/src/processing/app/ui/EditorConsole.java
+++ b/app/src/processing/app/ui/EditorConsole.java
@@ -233,6 +233,8 @@ public void message(String what, boolean err) {
       // https://github.com/processing/processing/issues/5462
       // Some discussion on the Apple's developer forums seems to suggest that is not serious:
       // https://forums.developer.apple.com/thread/105244
+    } else if (err && what.contains("NSWindow drag regions should only be invalidated on the Main Thread")) {  
+      // Keep hiding warnings triggered by JOGL on recent macOS versions (this is from 10.14 onwards I think).      
     } else if (err && what.contains("Make pbuffer:")) {
       // Remove initalization warning from LWJGL.
     } else if (err && what.contains("XInitThreads() called for concurrent")) {
diff --git a/app/src/processing/app/ui/EditorStatus.java b/app/src/processing/app/ui/EditorStatus.java
index 963867c287..db77a932c0 100644
--- a/app/src/processing/app/ui/EditorStatus.java
+++ b/app/src/processing/app/ui/EditorStatus.java
@@ -49,7 +49,7 @@
 /**
  * Panel just below the editing area that contains status messages.
  */
-public class EditorStatus extends BasicSplitPaneDivider {  //JPanel {
+public class EditorStatus extends BasicSplitPaneDivider {
   static final int HIGH = Toolkit.zoom(28);
   static final int LEFT_MARGIN = Editor.LEFT_GUTTER;
   static final int RIGHT_MARGIN = Toolkit.zoom(20);
@@ -60,16 +60,16 @@ public class EditorStatus extends BasicSplitPaneDivider {  //JPanel {
   Image[] bgImage;
 
   @SuppressWarnings("hiding")
-  static public final int ERROR   = 1;
+  static public final int ERROR = 1;
   static public final int CURSOR_LINE_ERROR = 2;
   static public final int WARNING = 3;
   static public final int CURSOR_LINE_WARNING = 4;
-  static public final int NOTICE  = 0;
+  static public final int NOTICE = 0;
 
-  static final int YES    = 1;
-  static final int NO     = 2;
+  static final int YES = 1;
+  static final int NO = 2;
   static final int CANCEL = 3;
-  static final int OK     = 4;
+  static final int OK = 4;
 
   Editor editor;
 
@@ -77,22 +77,43 @@ public class EditorStatus extends BasicSplitPaneDivider {  //JPanel {
   String message = "";
 
   String url;
+
   int rightEdge;
   int mouseX;
-  boolean urlRollover;
+
+  static final int ROLLOVER_NONE = 0;
+  static final int ROLLOVER_URL = 1;
+  static final int ROLLOVER_COLLAPSE = 2;
+  static final int ROLLOVER_CLIPBOARD = 3;
+  int rolloverState;
 
   Font font;
   FontMetrics metrics;
   int ascent;
 
-  // used to draw the clipboard icon
-  static final int EMOJI_OFFSET = 27;
-  Font emojiFont;
-  int emojiLeft;
-  boolean emojiRollover;
+  // actual Clipboard character not available [fry 180326]
+  //static final String CLIPBOARD_GLYPH = "\uD83D\uDCCB";
+  // other apps seem to use this one as a hack
+  static final String CLIPBOARD_GLYPH = "\u2398";
+
+  // https://en.wikipedia.org/wiki/Geometric_Shapes
+//  static final String COLLAPSE_GLYPH = "\u25B3";  // large up
+//  static final String EXPAND_GLYPH = "\u25BD";  // large down
+//  static final String COLLAPSE_GLYPH = "\u25B5";  // small up (unavailable)
+//  static final String EXPAND_GLYPH = "\u25BF";  // small down (unavailable)
+  static final String COLLAPSE_GLYPH = "\u25C1";  // left
+  static final String EXPAND_GLYPH = "\u25B7";  // right
+//  static final String COLLAPSE_GLYPH = "\u25F8";  // upper-left (unavailable)
+//  static final String EXPAND_GLYPH = "\u25FF";  // lower-right (unavailable)
+
+  // a font that supports the Unicode glyphs we need
+  Font glyphFont;
 
   Image offscreen;
   int sizeW, sizeH;
+  // size of the glyph buttons (width and height are identical)
+  int buttonSize;
+  boolean collapsed = false;
 
   int response;
 
@@ -115,10 +136,10 @@ public void mouseEntered(MouseEvent e) {
 
       @Override
       public void mousePressed(MouseEvent e) {
-        if (urlRollover) {
+        if (rolloverState == ROLLOVER_URL) {
           Platform.openURL(url);
 
-        } else if (emojiRollover) {
+        } else if (rolloverState == ROLLOVER_CLIPBOARD) {
           if (e.isShiftDown()) {
             // open the text in a browser window as a search
             final String fmt = Preferences.get("search.format");
@@ -131,17 +152,28 @@ public void mousePressed(MouseEvent e) {
             System.out.println("Copied to the clipboard. " +
                                "Use shift-click to search the web instead.");
           }
+
+        } else if (rolloverState == ROLLOVER_COLLAPSE) {
+          setCollapsed(!collapsed);
         }
       }
 
       @Override
       public void mouseExited(MouseEvent e) {
+        mouseX = -100;
         updateMouse();
       }
 
     });
 
     addMouseMotionListener(new MouseMotionAdapter() {
+      @Override
+      public void mouseDragged(MouseEvent e) {
+        // BasicSplitPaneUI.startDragging gets called even when you click but
+        // don't drag, so we can't expand the console whenever that gets called
+        // or the button wouldn't work.
+        setCollapsed(false);
+      }
 
       @Override
       public void mouseMoved(MouseEvent e) {
@@ -152,13 +184,27 @@ public void mouseMoved(MouseEvent e) {
   }
 
 
+  void setCollapsed(boolean newState) {
+    if (collapsed != newState) {
+      collapsed = newState;
+      editor.footer.setVisible(!newState);
+      splitPane.resetToPreferredSizes();
+    }
+  }
+
+
   void updateMouse() {
-    if (urlRollover) {
-      setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
-    } else if (emojiRollover) {
+    switch (rolloverState) {
+    case ROLLOVER_CLIPBOARD:
+    case ROLLOVER_URL:
       setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
-    } else {
+      break;
+    case ROLLOVER_COLLAPSE:
+      setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+      break;
+    case ROLLOVER_NONE:
       setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
+      break;
     }
     repaint();
   }
@@ -203,8 +249,7 @@ public void updateMode() {
     };
 
     font = mode.getFont("status.font");
-    //emojiFont = new Font("Dialog", Font.PLAIN, 21);
-    emojiFont = mode.getFont("status.emoji.font");
+    glyphFont = mode.getFont("status.emoji.font");
     metrics = null;
   }
 
@@ -282,8 +327,8 @@ public void stopIndeterminate() {
   }
 
 
+  //public void paintComponent(Graphics screen) {
   public void paint(Graphics screen) {
-//  public void paint(Graphics screen) {
     Dimension size = getSize();
     if ((size.width != sizeW) || (size.height != sizeH)) {
       // component has been resized
@@ -293,7 +338,7 @@ public void paint(Graphics screen) {
     if (offscreen == null) {
       sizeW = size.width;
       sizeH = size.height;
-
+      buttonSize = sizeH;
       offscreen = Toolkit.offscreenGraphics(this, sizeW, sizeH);
     }
 
@@ -308,21 +353,29 @@ public void paint(Graphics screen) {
 
     g.drawImage(bgImage[mode], 0, 0, sizeW, sizeH, this);
 
-    g.setColor(fgColor[mode]);
+    rolloverState = ROLLOVER_NONE;
+    if (mouseX > sizeW - buttonSize && mouseX < sizeW) {
+      rolloverState = ROLLOVER_COLLAPSE;
+
+    } else if (message != null && !message.isEmpty()) {
+      if (sizeW - 2*buttonSize < mouseX) {
+        rolloverState = ROLLOVER_CLIPBOARD;
+
+      } else if (url != null && mouseX > LEFT_MARGIN &&
+        // calculate right edge of the text for rollovers (otherwise the pane
+        // cannot be resized up or down whenever a URL is being displayed)
+        mouseX < (LEFT_MARGIN + g.getFontMetrics().stringWidth(message))) {
+        rolloverState = ROLLOVER_URL;
+      }
+    }
+
     // https://github.com/processing/processing/issues/3265
     if (message != null) {
-      // needs to be set each time on osx
+      // font needs to be set each time on osx
       g.setFont(font);
-      // calculate right edge of the text for rollovers (otherwise the pane
-      // cannot be resized up or down whenever a URL is being displayed)
-      rightEdge = LEFT_MARGIN + g.getFontMetrics().stringWidth(message);
       // set the highlight color on rollover so that the user's not surprised
       // to see the web browser open when they click
-      urlRollover = (url != null) &&
-        (mouseX > LEFT_MARGIN && mouseX < rightEdge);
-      if (urlRollover) {
-        g.setColor(urlColor);
-      }
+      g.setColor((rolloverState == ROLLOVER_URL) ? urlColor : fgColor[mode]);
       g.drawString(message, LEFT_MARGIN, (sizeH + ascent) / 2);
     }
 
@@ -330,9 +383,9 @@ public void paint(Graphics screen) {
       //int x = cancelButton.getX();
       //int w = cancelButton.getWidth();
       int w = Toolkit.getButtonWidth();
-      int x = getWidth() - RIGHT_MARGIN - w;
-      int y = getHeight() / 3;
-      int h = getHeight() / 3;
+      int x = getWidth() - Math.max(RIGHT_MARGIN, (int)(buttonSize*1.2)) - w;
+      int y = sizeH / 3;
+      int h = sizeH / 3;
       g.setColor(new Color(0x80000000, true));
       g.drawRect(x, y, w, h);
       for (int i = 0; i < 10; i++) {
@@ -341,20 +394,43 @@ public void paint(Graphics screen) {
       }
 
     } else if (!message.isEmpty()) {
-      g.setColor(Color.WHITE);
-      g.setFont(emojiFont);
-      // actual Clipboard character not available [fry 180326]
-      //g.drawString("\uD83D\uDCCB", sizeW - LEFT_MARGIN, (sizeH + ascent) / 2);
-      // other apps seem to use this one as a hack
-      emojiLeft = sizeW - Toolkit.zoom(EMOJI_OFFSET);
-      g.drawString("\u2398", emojiLeft, (sizeH + ascent) / 2);
-      emojiRollover = mouseX > emojiLeft - 4;
+      g.setFont(glyphFont);
+      drawButton(g, CLIPBOARD_GLYPH, 1, rolloverState == ROLLOVER_CLIPBOARD);
+      g.setFont(font);
     }
 
+    // draw collapse/expand button
+    String collapseGlyph = collapsed ? EXPAND_GLYPH : COLLAPSE_GLYPH;
+    drawButton(g, collapseGlyph, 0, rolloverState == ROLLOVER_COLLAPSE);
+
     screen.drawImage(offscreen, 0, 0, sizeW, sizeH, null);
   }
 
 
+  //private final Color whitishTint = new Color(0x20eeeeee, true);
+
+  /**
+   * @param pos A zero-based button index with 0 as the rightmost button
+   */
+  private void drawButton(Graphics g, String symbol, int pos, boolean highlight) {
+    int left = sizeW - (pos + 1) * buttonSize;
+    // Overlap very long errors
+    g.drawImage(bgImage[mode], left, 0, buttonSize, sizeH, this);
+
+    if (highlight) {
+      // disabling since this doesn't match any of our other UI
+//      g.setColor(whitishTint);
+//      g.fillRect(left, 0, sizeH, sizeH);
+      g.setColor(urlColor);
+    } else {
+      g.setColor(fgColor[mode]);
+    }
+    g.drawString(symbol,
+                 left + (buttonSize - g.getFontMetrics().stringWidth(symbol))/2,
+                 (sizeH + ascent) / 2);
+  }
+
+
   public Dimension getPreferredSize() {
     return getMinimumSize();
   }
diff --git a/build/build.xml b/build/build.xml
index 5093425351..00c72d77fb 100644
--- a/build/build.xml
+++ b/build/build.xml
@@ -74,9 +74,10 @@
   
   
   
+  
   
-  
+  
 
   
 
diff --git a/build/shared/lib/languages/PDE.properties b/build/shared/lib/languages/PDE.properties
index 7acab2a196..37b191509f 100644
--- a/build/shared/lib/languages/PDE.properties
+++ b/build/shared/lib/languages/PDE.properties
@@ -116,15 +116,15 @@ menu.help.tools_reference = Tools Reference
 menu.help.empty = (empty)
 menu.help.online = Online
 menu.help.getting_started = Getting Started
-menu.help.getting_started.url = http://processing.org/learning/gettingstarted/
+menu.help.getting_started.url = https://processing.org/tutorials/gettingstarted/
 menu.help.troubleshooting = Troubleshooting
-menu.help.troubleshooting.url = http://wiki.processing.org/w/Troubleshooting
+menu.help.troubleshooting.url = https://github.com/processing/processing/wiki/troubleshooting
 menu.help.faq = Frequently Asked Questions
-menu.help.faq.url = http://wiki.processing.org/w/FAQ
+menu.help.faq.url = https://github.com/processing/processing/wiki/FAQ
 menu.help.foundation = The Processing Foundation
-menu.help.foundation.url = http://processing.org/foundation/
+menu.help.foundation.url = https://processing.foundation/
 menu.help.visit = Visit Processing.org
-menu.help.visit.url = http://processing.org/
+menu.help.visit.url = https://processing.org/
 
 
 # ---------------------------------------
diff --git a/core/src/processing/awt/PSurfaceAWT.java b/core/src/processing/awt/PSurfaceAWT.java
index 4cf50e87fd..71a59e87f5 100644
--- a/core/src/processing/awt/PSurfaceAWT.java
+++ b/core/src/processing/awt/PSurfaceAWT.java
@@ -1316,16 +1316,6 @@ protected void nativeMouseEvent(java.awt.event.MouseEvent nativeEvent) {
       peButton = PConstants.RIGHT;
     }
 
-    // If running on Mac OS, allow ctrl-click as right mouse. Prior to 0215,
-    // this used isPopupTrigger() on the native event, but that doesn't work
-    // for mouseClicked and mouseReleased (or others).
-    if (PApplet.platform == PConstants.MACOSX) {
-      //if (nativeEvent.isPopupTrigger()) {
-      if ((modifiers & InputEvent.CTRL_MASK) != 0) {
-        peButton = PConstants.RIGHT;
-      }
-    }
-
     sketch.postEvent(new MouseEvent(nativeEvent, nativeEvent.getWhen(),
                                     peAction, peModifiers,
                                     nativeEvent.getX() / windowScaleFactor,
diff --git a/core/src/processing/core/PApplet.java b/core/src/processing/core/PApplet.java
index 457b50ea4b..f4f5d97c6b 100644
--- a/core/src/processing/core/PApplet.java
+++ b/core/src/processing/core/PApplet.java
@@ -561,6 +561,14 @@ public class PApplet implements PConstants {
    */
   public boolean mousePressed;
 
+  // MACOSX: CTRL + Left Mouse is converted to Right Mouse. This boolean keeps
+  // track of whether the conversion happened on PRESS, because we should report
+  // the same button during DRAG and on RELEASE, even though CTRL might have
+  // been released already. Otherwise the events are inconsistent, e.g.
+  // Left Pressed - Left Drag - CTRL Pressed - Right Drag - Right Released.
+  // See: https://github.com/processing/processing/issues/5672
+  private boolean macosxLeftButtonWithCtrlPressed;
+
 
   /** @deprecated Use a mouse event handler that passes an event instead. */
   @Deprecated
@@ -705,7 +713,7 @@ public class PApplet implements PConstants {
    * @see PApplet#frameRate(float)
    * @see PApplet#frameCount
    */
-  public float frameRate = 10;
+  public float frameRate = 60;
 
   protected boolean looping = true;
 
@@ -2426,9 +2434,34 @@ public void handleDraw() {
 
     } else {  // frameCount > 0, meaning an actual draw()
       // update the current frameRate
-      double rate = 1000000.0 / ((now - frameRateLastNanos) / 1000000.0);
-      float instantaneousRate = (float) (rate / 1000.0);
-      frameRate = (frameRate * 0.9f) + (instantaneousRate * 0.1f);
+
+      // Calculate frameRate through average frame times, not average fps, e.g.:
+      //
+      // Alternating 2 ms and 20 ms frames (JavaFX or JOGL sometimes does this)
+      // is around 90.91 fps (two frames in 22 ms, one frame 11 ms).
+      //
+      // However, averaging fps gives us: (500 fps + 50 fps) / 2 = 275 fps.
+      // This is because we had 500 fps for 2 ms and 50 fps for 20 ms, but we
+      // counted them with equal weight.
+      //
+      // If we average frame times instead, we get the right result:
+      // (2 ms + 20 ms) / 2 = 11 ms per frame, which is 1000/11 = 90.91 fps.
+      //
+      // The counter below uses exponential moving average. To do the
+      // calculation, we first convert the accumulated frame rate to average
+      // frame time, then calculate the exponential moving average, and then
+      // convert the average frame time back to frame rate.
+      {
+        // Get the frame time of the last frame
+        double frameTimeSecs = (now - frameRateLastNanos) / 1e9;
+        // Convert average frames per second to average frame time
+        double avgFrameTimeSecs = 1.0 / frameRate;
+        // Calculate exponential moving average of frame time
+        final double alpha = 0.05;
+        avgFrameTimeSecs = (1.0 - alpha) * avgFrameTimeSecs + alpha * frameTimeSecs;
+        // Convert frame time back to frames per second
+        frameRate = (float) (1.0 / avgFrameTimeSecs);
+      }
 
       if (frameCount != 0) {
         handleMethods("pre");
@@ -2650,8 +2683,27 @@ protected void handleMouseEvent(MouseEvent event) {
       mouseY = event.getY();
     }
 
+    int button = event.getButton();
+
+    // If running on Mac OS, allow ctrl-click as right mouse.
+    if (PApplet.platform == PConstants.MACOSX && event.getButton() == PConstants.LEFT) {
+      if (action == MouseEvent.PRESS && event.isControlDown()) {
+        macosxLeftButtonWithCtrlPressed = true;
+      }
+      if (macosxLeftButtonWithCtrlPressed) {
+        button = PConstants.RIGHT;
+        event = new MouseEvent(event.getNative(), event.getMillis(),
+                               event.getAction(), event.getModifiers(),
+                               event.getX(), event.getY(),
+                               button, event.getCount());
+      }
+      if (button == MouseEvent.RELEASE) {
+        macosxLeftButtonWithCtrlPressed = false;
+      }
+    }
+
     // Get the (already processed) button code
-    mouseButton = event.getButton();
+    mouseButton = button;
 
     /*
     // Compatibility for older code (these have AWT object params, not P5)
diff --git a/core/src/processing/core/PFont.java b/core/src/processing/core/PFont.java
index 632cd7ec03..b4b9a0fe69 100644
--- a/core/src/processing/core/PFont.java
+++ b/core/src/processing/core/PFont.java
@@ -145,6 +145,12 @@ public class PFont implements PConstants {
   /** True if already tried to find the native AWT version of this font. */
   protected boolean fontSearched;
 
+  /**
+   * The name of the font that is used as default when a font isn't found.
+   * See {@link #findFont(String)} and {@link #loadFonts()} for more info.
+   */
+  static protected String defaultFontName;
+
   /**
    * Array of the native system fonts. Used to lookup native fonts by their
    * PostScript name. This is a workaround for a several year old Apple Java
@@ -901,6 +907,7 @@ static public void loadFonts() {
       GraphicsEnvironment ge =
         GraphicsEnvironment.getLocalGraphicsEnvironment();
       fonts = ge.getAllFonts();
+      defaultFontName = new Font("",Font.PLAIN,1).getFontName();
       if (PApplet.platform == PConstants.MACOSX) {
         fontDifferent = new HashMap();
         for (Font font : fonts) {
@@ -917,6 +924,10 @@ static public void loadFonts() {
    * Starting with Java 1.5, Apple broke the ability to specify most fonts.
    * This bug was filed years ago as #4769141 at bugreporter.apple.com. More:
    * Bug 407.
+   *
+   * This function displays a warning when the font isn't found and the default font is
+   * used.
+   * See: issue #5481
    */
   static public Font findFont(String name) {
     loadFonts();
@@ -931,7 +942,14 @@ static public Font findFont(String name) {
 //        }
 //      }
     }
-    return new Font(name, Font.PLAIN, 1);
+    Font font = new Font(name, Font.PLAIN, 1);
+    if(!defaultFontName.equals(name) && font.getFontName().equals(defaultFontName)) {
+      System.err.println("Warning: font \"" + name
+                         + "\" is missing or inaccessible on this system. Default font \""
+                         + defaultFontName + "\" is used.");
+
+    }
+    return font;
   }
 
 
diff --git a/core/src/processing/core/PShape.java b/core/src/processing/core/PShape.java
index 60b4521f8b..b587e3e9b0 100644
--- a/core/src/processing/core/PShape.java
+++ b/core/src/processing/core/PShape.java
@@ -22,10 +22,14 @@
 
 package processing.core;
 
+import java.awt.Image;
+import java.awt.color.ColorSpace;
+import java.awt.image.BufferedImage;
 import java.util.HashMap;
 import java.util.Map;
 
-import processing.core.PApplet;
+import javax.swing.ImageIcon;
+import javax.xml.bind.DatatypeConverter;
 
 
 /**
@@ -106,6 +110,7 @@ public class PShape implements PConstants {
 
   /** Texture or image data associated with this shape. */
   protected PImage image;
+  protected String imagePath = null;
 
   public static final String OUTSIDE_BEGIN_END_ERROR =
     "%1$s can only be called between beginShape() and endShape()";
@@ -1645,6 +1650,10 @@ protected void drawPrimitive(PGraphics g) {
              params[6], params[7]);
 
     } else if (kind == RECT) {
+
+      if (imagePath != null){
+          loadImage(g);
+      }
       if (image != null) {
         int oldMode = g.imageMode;
         g.imageMode(CORNER);
@@ -1879,6 +1888,63 @@ protected void drawPath(PGraphics g) {
     g.endShape(close ? CLOSE : OPEN);
   }
 
+  private void loadImage(PGraphics g){
+
+      if(this.imagePath.startsWith("data:image")){
+          loadBase64Image();
+      }
+
+      if(this.imagePath.startsWith("file://")){
+          loadFileSystemImage(g);
+      }
+      this.imagePath = null;
+  }
+
+  private void loadFileSystemImage(PGraphics g){
+    imagePath = imagePath.substring(7);
+    PImage loadedImage = g.parent.loadImage(imagePath);
+    if(loadedImage == null){
+      System.err.println("Error loading image file: " + imagePath);
+    }else{
+      setTexture(loadedImage);
+    }
+  }
+
+ private void loadBase64Image(){
+    String[] parts = this.imagePath.split(";base64,");
+    String extension = parts[0].substring(11);
+    String encodedData = parts[1];
+
+    byte[] decodedBytes = DatatypeConverter.parseBase64Binary(encodedData);
+
+    if(decodedBytes == null){
+      System.err.println("Decode Error on image: " + imagePath.substring(0, 20));
+      return;
+    }
+
+    Image awtImage = new ImageIcon(decodedBytes).getImage();
+
+    if (awtImage instanceof BufferedImage) {
+      BufferedImage buffImage = (BufferedImage) awtImage;
+      int space = buffImage.getColorModel().getColorSpace().getType();
+      if (space == ColorSpace.TYPE_CMYK) {
+       return;
+      }
+    }
+
+    PImage loadedImage = new PImage(awtImage);
+    if (loadedImage.width == -1) {
+      // error...
+    }
+
+    // if it's a .gif image, test to see if it has transparency
+    if (extension.equals("gif") || extension.equals("png") ||
+      extension.equals("unknown")) {
+    loadedImage.checkAlpha();
+    }
+
+    setTexture(loadedImage);
+  }
 
   // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
 
diff --git a/core/src/processing/core/PShapeSVG.java b/core/src/processing/core/PShapeSVG.java
index bc81512e0b..d74253af6e 100644
--- a/core/src/processing/core/PShapeSVG.java
+++ b/core/src/processing/core/PShapeSVG.java
@@ -24,6 +24,9 @@
 
 package processing.core;
 
+import static java.awt.Font.BOLD;
+import static java.awt.Font.ITALIC;
+import static java.awt.Font.PLAIN;
 import processing.data.*;
 
 // TODO replace these with PMatrix2D
@@ -332,6 +335,10 @@ protected PShape parseChild(XML elem) {
       shape = createShape(this, elem, true);
       shape.parseRect();
 
+    } else if (name.equals("image")) {
+      shape = createShape(this, elem, true);
+      shape.parseImage();
+
     } else if (name.equals("polygon")) {
       shape = createShape(this, elem, true);
       shape.parsePoly(true);
@@ -360,8 +367,10 @@ protected PShape parseChild(XML elem) {
 //      return new FontGlyph(this, elem);
 
     } else if (name.equals("text")) {  // || name.equals("font")) {
-      PGraphics.showWarning("Text and fonts in SVG files are " +
-        "not currently supported, convert text to outlines instead.");
+        return new Text(this, elem);
+
+    } else if (name.equals("tspan")) {
+        return new LineOfText(this, elem);
 
     } else if (name.equals("filter")) {
       PGraphics.showWarning("Filters are not supported.");
@@ -443,6 +452,21 @@ protected void parseRect() {
   }
 
 
+  protected void parseImage() {
+    kind = RECT;
+    textureMode = NORMAL;
+
+    family = PRIMITIVE;
+    params = new float[] {
+      getFloatWithUnit(element, "x", svgWidth),
+      getFloatWithUnit(element, "y", svgHeight),
+      getFloatWithUnit(element, "width", svgWidth),
+      getFloatWithUnit(element, "height", svgHeight)
+    };
+
+    this.imagePath = element.getString("xlink:href");
+  }
+
   /**
    * Parse a polyline or polygon from an SVG file.
    * Syntax defined at http://www.w3.org/TR/SVG/shapes.html#PointsBNF
@@ -1513,7 +1537,10 @@ public Gradient(PShapeSVG parent, XML properties) {
   }
 
 
-  public class LinearGradient extends Gradient {
+  // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+
+  static public class LinearGradient extends Gradient {
     public float x1, y1, x2, y2;
 
     public LinearGradient(PShapeSVG parent, XML properties) {
@@ -1543,7 +1570,10 @@ public LinearGradient(PShapeSVG parent, XML properties) {
   }
 
 
-  public class RadialGradient extends Gradient {
+  // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+
+  static public class RadialGradient extends Gradient {
     public float cx, cy, r;
 
     public RadialGradient(PShapeSVG parent, XML properties) {
@@ -1574,7 +1604,179 @@ public RadialGradient(PShapeSVG parent, XML properties) {
   // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
 
 
-  public static class Font extends PShapeSVG {
+//  static private float TEXT_QUALITY = 1;
+
+  static private PFont parseFont(XML properties) {
+    String fontFamily = null;
+    float size = 10;
+    int weight = PLAIN; // 0
+    int italic = 0;
+
+    if (properties.hasAttribute("style")) {
+      String styleText = properties.getString("style");
+      String[] styleTokens = PApplet.splitTokens(styleText, ";");
+
+      //PApplet.println(styleTokens);
+      for (int i = 0; i < styleTokens.length; i++) {
+        String[] tokens = PApplet.splitTokens(styleTokens[i], ":");
+        //PApplet.println(tokens);
+
+        tokens[0] = PApplet.trim(tokens[0]);
+
+        if (tokens[0].equals("font-style")) {
+          // PApplet.println("font-style: " + tokens[1]);
+          if (tokens[1].contains("italic")) {
+            italic = ITALIC;
+          }
+        } else if (tokens[0].equals("font-variant")) {
+          // PApplet.println("font-variant: " + tokens[1]);
+          // setFillOpacity(tokens[1]);
+
+        } else if (tokens[0].equals("font-weight")) {
+          // PApplet.println("font-weight: " + tokens[1]);
+
+          if (tokens[1].contains("bold")) {
+            weight = BOLD;
+            // PApplet.println("Bold weight ! ");
+          }
+
+
+        } else if (tokens[0].equals("font-stretch")) {
+          // not supported.
+
+        } else if (tokens[0].equals("font-size")) {
+          // PApplet.println("font-size: " + tokens[1]);
+          size = Float.parseFloat(tokens[1].split("px")[0]);
+          // PApplet.println("font-size-parsed: " + size);
+        } else if (tokens[0].equals("line-height")) {
+                // not supported
+
+        } else if (tokens[0].equals("font-family")) {
+          // PApplet.println("Font-family: " + tokens[1]);
+          fontFamily = tokens[1];
+
+        } else if (tokens[0].equals("text-align")) {
+          // not supported
+
+        } else if (tokens[0].equals("letter-spacing")) {
+                // not supported
+
+        } else if (tokens[0].equals("word-spacing")) {
+                // not supported
+
+        } else if (tokens[0].equals("writing-mode")) {
+          // not supported
+
+        } else if (tokens[0].equals("text-anchor")) {
+          // not supported
+
+        } else {
+          // Other attributes are not yet implemented
+        }
+      }
+    }
+    if (fontFamily == null) {
+      return null;
+    }
+//    size = size * TEXT_QUALITY;
+
+    return createFont(fontFamily, weight | italic, size, true);
+  }
+
+
+  static protected PFont createFont(String name, int weight,
+                                    float size, boolean smooth) {
+    //System.out.println("Try to create a font of " + name + " family, " + weight);
+    java.awt.Font baseFont = new java.awt.Font(name, weight, (int) size);
+
+    //System.out.println("Resulting family : " + baseFont.getFamily() + " " + baseFont.getStyle());
+    return new PFont(baseFont.deriveFont(size), smooth, null);
+  }
+
+
+  // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+
+  static public class Text extends PShapeSVG {
+    protected PFont font;
+
+    public Text(PShapeSVG parent, XML properties) {
+      super(parent, properties, true);
+
+            // get location
+      float x = Float.parseFloat(properties.getString("x"));
+      float y = Float.parseFloat(properties.getString("y"));
+
+      if (matrix == null) {
+        matrix = new PMatrix2D();
+      }
+      matrix.translate(x, y);
+
+      family = GROUP;
+
+      font = parseFont(properties);
+    }
+  }
+
+
+  // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+
+  static public class LineOfText extends PShapeSVG {
+    String textToDisplay;
+    PFont font;
+
+    public LineOfText(PShapeSVG parent, XML properties) {
+      // TODO: child should ideally be parsed too for inline content.
+      super(parent, properties, false);
+
+      //get location
+      float x = Float.parseFloat(properties.getString("x"));
+      float y = Float.parseFloat(properties.getString("y"));
+
+      float parentX = Float.parseFloat(parent.element.getString("x"));
+      float parentY = Float.parseFloat(parent.element.getString("y"));
+
+      if (matrix == null) matrix = new PMatrix2D();
+      matrix.translate(x - parentX, (y - parentY) / 2f);
+
+      // get the first properties
+      parseColors(properties);
+      font = parseFont(properties);
+
+      // cleaned up syntax but removing b/c unused [fry 190118]
+      //boolean isLine = properties.getString("role").equals("line");
+
+      if (this.childCount > 0) {
+        // no inline content yet.
+      }
+
+      String text = properties.getContent();
+      textToDisplay = text;
+    }
+
+    @Override
+    public void drawImpl(PGraphics g) {
+      if (font == null) {
+        font = ((Text) parent).font;
+        if (font == null) {
+          return;
+        }
+      }
+
+      pre(g);
+//      g.textFont(font, font.size / TEXT_QUALITY);
+      g.textFont(font, font.size);
+      g.text(textToDisplay, 0, 0);
+      post(g);
+    }
+  }
+
+
+  // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+
+  static public class Font extends PShapeSVG {
     public FontFace face;
 
     public Map namedGlyphs;
@@ -1595,8 +1797,8 @@ public Font(PShapeSVG parent, XML properties) {
 
       horizAdvX = properties.getInt("horiz-adv-x", 0);
 
-      namedGlyphs = new HashMap();
-      unicodeGlyphs = new HashMap();
+      namedGlyphs = new HashMap<>();
+      unicodeGlyphs = new HashMap<>();
       glyphCount = 0;
       glyphs = new FontGlyph[elements.length];
 
@@ -1725,7 +1927,7 @@ protected void drawShape() {
   // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
 
 
-  public static class FontGlyph extends PShapeSVG {  // extends Path
+  static public class FontGlyph extends PShapeSVG {  // extends Path
     public String name;
     char unicode;
     int horizAdvX;
diff --git a/core/src/processing/javafx/PSurfaceFX.java b/core/src/processing/javafx/PSurfaceFX.java
index 64be6c21b3..8462f887ad 100644
--- a/core/src/processing/javafx/PSurfaceFX.java
+++ b/core/src/processing/javafx/PSurfaceFX.java
@@ -860,14 +860,6 @@ protected void fxMouseEvent(MouseEvent fxEvent) {
         break;
     }
 
-    // If running on Mac OS, allow ctrl-click as right mouse.
-    // Verified to be necessary with Java 8u45.
-    if (PApplet.platform == PConstants.MACOSX &&
-        fxEvent.isControlDown() &&
-        button == PConstants.LEFT) {
-      button = PConstants.RIGHT;
-    }
-
     //long when = nativeEvent.getWhen();  // from AWT
     long when = System.currentTimeMillis();
     int x = (int) fxEvent.getX();  // getSceneX()?
diff --git a/core/src/processing/opengl/PGraphicsOpenGL.java b/core/src/processing/opengl/PGraphicsOpenGL.java
index 6b0f42c635..7801326ecd 100644
--- a/core/src/processing/opengl/PGraphicsOpenGL.java
+++ b/core/src/processing/opengl/PGraphicsOpenGL.java
@@ -1190,7 +1190,7 @@ protected void createPolyBuffers() {
     if (!polyBuffersCreated || polyBuffersContextIsOutdated()) {
       polyBuffersContext = pgl.getCurrentContext();
 
-      bufPolyVertex = new VertexBuffer(this, PGL.ARRAY_BUFFER, 3, PGL.SIZEOF_FLOAT);
+      bufPolyVertex = new VertexBuffer(this, PGL.ARRAY_BUFFER, 4, PGL.SIZEOF_FLOAT);
       bufPolyColor = new VertexBuffer(this, PGL.ARRAY_BUFFER, 1, PGL.SIZEOF_INT);
       bufPolyNormal = new VertexBuffer(this, PGL.ARRAY_BUFFER, 3, PGL.SIZEOF_FLOAT);
       bufPolyTexcoord = new VertexBuffer(this, PGL.ARRAY_BUFFER, 2, PGL.SIZEOF_FLOAT);
@@ -3772,6 +3772,13 @@ static protected void invTranslate(PMatrix3D matrix,
   }
 
 
+  static protected void invTranslate(PMatrix2D matrix,
+                                     float tx, float ty) {
+    matrix.preApply(1, 0, -tx,
+                    0, 1, -ty);
+  }
+
+
   static protected float matrixScale(PMatrix matrix) {
     // Volumetric scaling factor that is associated to the given
     // transformation matrix, which is given by the absolute value of its
@@ -3857,8 +3864,8 @@ protected void rotateImpl(float angle, float v0, float v1, float v2) {
   }
 
 
-  static private void invRotate(PMatrix3D matrix, float angle,
-                                float v0, float v1, float v2) {
+  static protected void invRotate(PMatrix3D matrix, float angle,
+                                  float v0, float v1, float v2) {
     float c = PApplet.cos(-angle);
     float s = PApplet.sin(-angle);
     float t = 1.0f - c;
@@ -3870,6 +3877,11 @@ static private void invRotate(PMatrix3D matrix, float angle,
   }
 
 
+  static protected void invRotate(PMatrix2D matrix, float angle) {
+    matrix.rotate(-angle);
+  }
+
+
   /**
    * Same as scale(s, s, s).
    */
@@ -3911,6 +3923,11 @@ static protected void invScale(PMatrix3D matrix, float x, float y, float z) {
   }
 
 
+  static protected void invScale(PMatrix2D matrix, float x, float y) {
+    matrix.preApply(1/x, 0, 0, 1/y, 0, 0);
+  }
+
+
   @Override
   public void shearX(float angle) {
     float t = (float) Math.tan(angle);
@@ -6412,9 +6429,16 @@ protected Object initCache(PImage img) {
     if (tex == null || tex.contextIsOutdated()) {
       tex = addTexture(img);
       if (tex != null) {
+        boolean dispose = img.pixels == null;
         img.loadPixels();
         tex.set(img.pixels, img.format);
         img.setModified();
+        if (dispose) {
+          // We only used the pixels to load the image into the texture and the user did not request
+          // to load the pixels, so we should dispose the pixels array to avoid wasting memory
+          img.pixels = null;
+          img.loaded = false;
+        }
       }
     }
     return tex;
@@ -6797,6 +6821,9 @@ protected void setGLSettings() {
       } else {
         // offscreen surfaces are transparent by default.
         background(0x00 << 24 | (backgroundColor & 0xFFFFFF));
+
+        // Recreate offscreen FBOs
+        restartPGL();
       }
 
       // Sets the default projection and camera (initializes modelview).
diff --git a/core/src/processing/opengl/PShapeOpenGL.java b/core/src/processing/opengl/PShapeOpenGL.java
index 69701acc1d..c240847512 100644
--- a/core/src/processing/opengl/PShapeOpenGL.java
+++ b/core/src/processing/opengl/PShapeOpenGL.java
@@ -43,7 +43,6 @@
 import java.nio.Buffer;
 import java.util.Arrays;
 import java.util.HashSet;
-import java.util.Stack;
 
 /**
  * This class holds a 3D model composed of vertices, normals, colors
@@ -171,7 +170,8 @@ public class PShapeOpenGL extends PShape {
   // Geometric transformations.
 
   protected PMatrix transform;
-  protected Stack transformStack;
+  protected PMatrix transformInv;
+  protected PMatrix matrixInv;
 
   // ........................................................
 
@@ -847,7 +847,7 @@ protected void scaleTextureUV(float uFactor, float vFactor) {
 
   protected void addTexture(PImage tex) {
     if (textures == null) {
-      textures = new HashSet();
+      textures = new HashSet<>();
     }
     textures.add(tex);
     if (parent != null) {
@@ -1334,39 +1334,34 @@ public void applyMatrix(float n00, float n01, float n02, float n03,
 
   @Override
   public void resetMatrix() {
-    if (shapeCreated && matrix != null && transformStack != null) {
+    if (shapeCreated && matrix != null && matrixInv != null) {
       if (family == GROUP) {
         updateTessellation();
       }
       if (tessellated) {
-        PMatrix mat = popTransform();
-        while (mat != null) {
-          boolean res = mat.invert();
-          if (res) {
-            applyMatrixImpl(mat);
-          } else {
-            PGraphics.showWarning("Transformation applied on the shape cannot be inverted");
-          }
-          mat = popTransform();
-        }
+        applyMatrixImpl(matrixInv);
       }
       matrix.reset();
-      transformStack.clear();
+      matrixInv.reset();
     }
   }
 
 
   protected void transform(int type, float... args) {
     int dimensions = is3D ? 3 : 2;
+    boolean invertible = true;
     checkMatrix(dimensions);
     if (transform == null) {
       if (dimensions == 2) {
         transform = new PMatrix2D();
+        transformInv = new PMatrix2D();
       } else {
         transform = new PMatrix3D();
+        transformInv = new PMatrix3D();
       }
     } else {
       transform.reset();
+      transformInv.reset();
     }
 
     int ncoords = args.length;
@@ -1380,22 +1375,28 @@ protected void transform(int type, float... args) {
     case TRANSLATE:
       if (ncoords == 3) {
         transform.translate(args[0], args[1], args[2]);
+        PGraphicsOpenGL.invTranslate((PMatrix3D)transformInv, args[0], args[1], args[2]);
       } else {
         transform.translate(args[0], args[1]);
+        PGraphicsOpenGL.invTranslate((PMatrix2D)transformInv, args[0], args[1]);
       }
       break;
     case ROTATE:
       if (ncoords == 3) {
         transform.rotate(args[0], args[1], args[2], args[3]);
+        PGraphicsOpenGL.invRotate((PMatrix3D)transformInv, args[0], args[1], args[2], args[3]);
       } else {
         transform.rotate(args[0]);
+        PGraphicsOpenGL.invRotate((PMatrix2D)transformInv, -args[0]);
       }
       break;
     case SCALE:
       if (ncoords == 3) {
         transform.scale(args[0], args[1], args[2]);
+        PGraphicsOpenGL.invScale((PMatrix3D)transformInv, args[0], args[1], args[2]);
       } else {
         transform.scale(args[0], args[1]);
+        PGraphicsOpenGL.invScale((PMatrix2D)transformInv, args[0], args[1]);
       }
       break;
     case MATRIX:
@@ -1408,32 +1409,20 @@ protected void transform(int type, float... args) {
         transform.set(args[0], args[1], args[2],
                       args[3], args[4], args[5]);
       }
+      transformInv.set(transform);
+      invertible = transformInv.invert();
       break;
     }
     matrix.preApply(transform);
-    pushTransform();
-    if (tessellated) applyMatrixImpl(transform);
-  }
-
-
-  protected void pushTransform() {
-    if (transformStack == null) transformStack = new Stack();
-    PMatrix mat;
-    if (transform instanceof PMatrix2D) {
-      mat = new PMatrix2D();
+    if (invertible) {
+      matrixInv.apply(transformInv);
     } else {
-      mat = new PMatrix3D();
+      PGraphics.showWarning("Transformation applied on the shape cannot be inverted");
     }
-    mat.set(transform);
-    transformStack.push(mat);
+    if (tessellated) applyMatrixImpl(transform);
   }
 
 
-  protected PMatrix popTransform() {
-    if (transformStack == null || transformStack.size() == 0) return null;
-    return transformStack.pop();
-  }
-
   protected void applyMatrixImpl(PMatrix matrix) {
     if (hasPolys) {
       tessGeo.applyMatrixOnPolyGeometry(matrix,
@@ -1465,6 +1454,23 @@ protected void applyMatrixImpl(PMatrix matrix) {
   }
 
 
+  @Override
+  protected void checkMatrix(int dimensions) {
+    if (matrix == null) {
+      if (dimensions == 2) {
+        matrix = new PMatrix2D();
+        matrixInv = new PMatrix2D();
+      } else {
+        matrix = new PMatrix3D();
+        matrixInv = new PMatrix3D();
+      }
+    } else if (dimensions == 3 && (matrix instanceof PMatrix2D)) {
+      matrix = new PMatrix3D(matrix);
+      matrixInv = new PMatrix3D(matrixInv);
+    }
+  }
+
+
   ///////////////////////////////////////////////////////////
 
   //
@@ -1755,10 +1761,11 @@ public void setAttrib(String name, int index, float... values) {
       return;
     }
 
-    VertexAttribute attrib = polyAttribs.get(name);
+    VertexAttribute attrib = attribImpl(name, VertexAttribute.OTHER, PGL.FLOAT,
+                                        values.length);
     float[] array = inGeo.fattribs.get(name);
     for (int i = 0; i < values.length; i++) {
-      array[attrib.size * index + 0] = values[i];
+      array[attrib.size * index + i] = values[i];
     }
     markForTessellation();
   }
@@ -1771,10 +1778,11 @@ public void setAttrib(String name, int index, int... values) {
       return;
     }
 
-    VertexAttribute attrib = polyAttribs.get(name);
+    VertexAttribute attrib = attribImpl(name, VertexAttribute.OTHER, PGL.INT,
+                                        values.length);
     int[] array = inGeo.iattribs.get(name);
     for (int i = 0; i < values.length; i++) {
-      array[attrib.size * index + 0] = values[i];
+      array[attrib.size * index + i] = values[i];
     }
     markForTessellation();
   }
@@ -1787,10 +1795,11 @@ public void setAttrib(String name, int index, boolean... values) {
       return;
     }
 
-    VertexAttribute attrib = polyAttribs.get(name);
+    VertexAttribute attrib = attribImpl(name, VertexAttribute.OTHER, PGL.BOOL,
+                                        values.length);
     byte[] array = inGeo.battribs.get(name);
     for (int i = 0; i < values.length; i++) {
-      array[attrib.size * index + 0] = (byte)(values[i]?1:0);
+      array[attrib.size * index + i] = (byte)(values[i]?1:0);
     }
     markForTessellation();
   }
@@ -2822,15 +2831,21 @@ protected void initModified() {
 
   protected void tessellate() {
     if (root == this && parent == null) { // Root shape
+      boolean initAttr = false;
       if (polyAttribs == null) {
         polyAttribs = PGraphicsOpenGL.newAttributeMap();
-        collectPolyAttribs();
+        initAttr = true;
       }
 
       if (tessGeo == null) {
         tessGeo = PGraphicsOpenGL.newTessGeometry(pg, polyAttribs, PGraphicsOpenGL.RETAINED);
       }
       tessGeo.clear();
+
+      if (initAttr) {
+        collectPolyAttribs();
+      }
+
       for (int i = 0; i < polyAttribs.size(); i++) {
         VertexAttribute attrib = polyAttribs.get(i);
         tessGeo.initAttrib(attrib);
@@ -2848,6 +2863,7 @@ protected void tessellate() {
 
   protected void collectPolyAttribs() {
     AttributeMap rootAttribs = root.polyAttribs;
+    tessGeo = root.tessGeo;
 
     if (family == GROUP) {
       for (int i = 0; i < childCount; i++) {
diff --git a/core/src/processing/opengl/PSurfaceJOGL.java b/core/src/processing/opengl/PSurfaceJOGL.java
index 17d2d916eb..a9de3f0b82 100644
--- a/core/src/processing/opengl/PSurfaceJOGL.java
+++ b/core/src/processing/opengl/PSurfaceJOGL.java
@@ -1033,13 +1033,6 @@ protected void nativeMouseEvent(com.jogamp.newt.event.MouseEvent nativeEvent,
         break;
     }
 
-    if (PApplet.platform == PConstants.MACOSX) {
-      //if (nativeEvent.isPopupTrigger()) {
-      if ((modifiers & InputEvent.CTRL_MASK) != 0) {
-        peButton = PConstants.RIGHT;
-      }
-    }
-
     int peCount = 0;
     if (peAction == MouseEvent.WHEEL) {
       // Invert wheel rotation count so it matches JAVA2D's
diff --git a/core/todo.txt b/core/todo.txt
index c59fbbf38b..42c1e0aefc 100644
--- a/core/todo.txt
+++ b/core/todo.txt
@@ -11,6 +11,8 @@ X   had to back this fix out again
 X Fixes blend mode not being set correctly, fixing #5645
 X   https://github.com/processing/processing/issues/5645
 X   https://github.com/processing/processing/pull/5647
+X extended SVG support
+X   https://github.com/processing/processing/pull/4168
 
 gohai
 X Profile GL3bc is not available on X11GraphicsDevice
@@ -20,6 +22,34 @@ X   https://github.com/processing/processing/pull/5652
 andres
 X silence TIS/TSM warning message with P2D/P3D/OPENGL since macOS 10.13.4
 X   https://github.com/processing/processing/issues/5462
+X improve matrix performance in P2D/P3D
+X   https://github.com/processing/processing/issues/5685
+X Initializing textures loads the pixels array, even if not needed aferwards
+X   https://github.com/processing/processing/issues/5748
+X Processing 3.4 takes 60 seconds before can edit file
+X   https://github.com/processing/processing/issues/5707
+X   skip Android's SDK folder when adding sketches
+X   https://github.com/processing/processing/commit/5b653263cc6151f838c11a61689d756901c11e37
+X PShape.attrib() and PShape.setAttrib() are not working
+X   https://github.com/processing/processing/issues/5560
+
+jakub
+X Fix freeze when restarting sketch with variables in size
+X   https://github.com/processing/processing/pull/5708
+X Make sure Ctrl+Left Mouse on MacOS is consistent
+X   https://github.com/processing/processing/issues/5672
+X   https://github.com/processing/processing/pull/5674
+X Stop frame rate counter from exaggerating
+X   https://github.com/processing/processing/pull/5697
+X Fix vertex buffer initialized with wrong number of components
+X   https://github.com/processing/processing/pull/5698
+X Recreate FBOs when offscreen PGraphicsOpenGL is resized
+X   https://github.com/processing/processing/pull/5699
+
+cleaning
+o WARNING: GL pipe is running in software mode (Renderer ID=0x1020400)
+o   is this coming from us? if so, need to provide actions
+X   haven't seen for a while, maybe fixed
 
 
 _ NullPointerException at java.awt.Window.init(Window.java:497) when using Airplay
@@ -46,8 +76,6 @@ _   create icon.png or have an 'icons' folder with multiple sizes
 
 
 contribs
-_ extended SVG support
-_   https://github.com/processing/processing/pull/4168
 _ show a warning when a font isn't what the user expected
 _   https://github.com/processing/processing/issues/5481
 _   https://github.com/processing/processing/pull/5605
@@ -63,20 +91,12 @@ _ Switch to getModifiersEx() and fix the AWT modifiers used in PSurfaceAWT
 _   this is an easy fix, but need to check impact elsewhere
 _   does anything else rely on these modifiers?
 
-_ Fix Java 9 incompatibilities inside PSurfaceFX
-_   https://github.com/processing/processing/issues/5286
-
-_ Hitting ESC in FX2D app on macOS throws IllegalStateException
-_   https://github.com/processing/processing/issues/5249
 
 _ when doing createFont, can we add it to the os fonts available?
 
-_ add separator option to loadTable() 
+_ add separator option to loadTable()
 _   https://github.com/processing/processing/issues/5068
 
-_ WARNING: GL pipe is running in software mode (Renderer ID=0x1020400)
-_   is this coming from us? if so, need to provide actions
-
 _ implement sketch scaling into PApplet
 _   https://github.com/processing/processing/issues/4897
 _ Sketches on Windows don't take UI sizing into account
@@ -91,7 +111,7 @@ _   https://github.com/processing/processing/issues/4678
 _ should we drop the 'default' prefix from the ex handler so it's not static?
 _   http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Thread.html
 
-_ option to disable OpenGL ES on Linux? 
+_ option to disable OpenGL ES on Linux?
 _   https://github.com/processing/processing/issues/4584
 
 _ textAlign(CENTER) and pixelDensity(2) aligning incorrectly with Java2D
@@ -139,17 +159,21 @@ _   https://github.com/processing/processing/issues/3753
 
 
 javafx
+_ Fix Java 11 incompatibilities inside PSurfaceFX
+_   https://github.com/processing/processing/issues/5286
+_ Hitting ESC in FX2D app on macOS throws IllegalStateException
+_   https://github.com/processing/processing/issues/5249
 _ wrong window size with fullScreen()
 _   https://github.com/processing/processing/issues/4737
 _ menu bar not hiding properly in exported applications with FX2D
 _   https://github.com/processing/processing/issues/4527
 _   hideMenuBar() called from setup() works fine
-_   just call it around setup time? 
+_   just call it around setup time?
 _ the --hide-stop option not working (FX only? traces?)
 _ make wiki about quirks
 _   starving the thread makes things really slow down
 _   keyPressed() is always uppercase, keyTyped() will be correct
-_ do we really need setTextFont/Size when we already have Impl? 
+_ do we really need setTextFont/Size when we already have Impl?
 _ need keyPressed() to do lower and upper case
 _ static mode sketches (draw once and halt w/o closing window)
 _ fix display handling, line up the device order with AWT
@@ -183,7 +207,7 @@ _   also not API we want to expose, so sort this out
 _   or maybe we're fine b/c now FX2D needs it as well
 _ when did setPath() sneak into PShape? API is nothing like anything else
 _   probably from the material stuff, but we need to fix that
-_ why is createShape() implemented 4x (for P2D, P3D, and 2x versions)? 
+_ why is createShape() implemented 4x (for P2D, P3D, and 2x versions)?
 _   shouldn't be static, run it from the renderer, that's point of createXxx()
 _ public createShape() method that takes another shape as param?
 _   should just be the constructor doing this, or copy()
@@ -235,7 +259,7 @@ _   row count and array size are combined.. need to break apart
 _ match and iterators
 _   add match version that returns table that's only a pointer to original
 _   save the constructor for the version that actually copies data
-_   the table pointer version will be speedy and allow chaining 
+_   the table pointer version will be speedy and allow chaining
 
 
 high
@@ -248,7 +272,7 @@ _   https://github.com/processing/processing/issues/2428
 _ finish PFont.getShape() implementation
 _   needs to have a way to set width/height properly
 _   draw(s) doesn't work on the returned PShape
-_ y-location of frame might be negative, but that might be ok 
+_ y-location of frame might be negative, but that might be ok
 _   right now, on Windows it moves it on-screen (b/c of previous bug reports)
 _   may be cause of some of the display placement issues w/ multiple displays
 _   seem to recall one of the bugs mentioning stacked displays
@@ -260,7 +284,7 @@ _ don't override the window icon w/ p5 logo if already set
 
 
 decisions/misc
-_ Separately, if we wanted, we could add a method that can safely do drawing calls, but that won't be any faster or make use of more processors the way that `thread()` does. 
+_ Separately, if we wanted, we could add a method that can safely do drawing calls, but that won't be any faster or make use of more processors the way that `thread()` does.
 _ need reference update for createShape()
 _   though we need to verify the behavior here
 _   createShape(RECT) uses x/y/w/h, and optionally adds a mode param
@@ -301,16 +325,16 @@ _   public float textWidth(char[] chars, int start, int length)
 _ add version of math functions that use doubles?
 _   what other methods should work with doubles? all math functions?
 _   seems like internal (mostly static) things, but not graphics api
-_ new PGraphics(... OutputStream) 
+_ new PGraphics(... OutputStream)
 _   https://github.com/processing/processing/issues/285
 _   already added for PDF, just need to work out the API
 _ if save() returns boolean, does saveFrame()?
 _   also need to copy this over to android
 _ "translate, or this variation of it" when using text(s, x, y, z) accidentally
 _   change to be the text command
-_ size() and resize() and whatever? 
+_ size() and resize() and whatever?
 _   should be setSize(), but that's odd for image files
-_   -> resize() is fine with PImage and PGraphics... 
+_   -> resize() is fine with PImage and PGraphics...
 _   we may be inheriting the resize() from Java -> make it final?
 _   add resize().. make it call setSize().
 _     also needs to do a redraw if noLoop() has been called
@@ -344,7 +368,7 @@ _   have to pass PApplet just to make the rendering work to both renderers
 _   should instead be a renderer that splits things out
 _ wrap exceptions with die() and warn() methods
 _   this way, people can make use of exceptions if they would rather
-_   or shut them off if they don't want to 
+_   or shut them off if they don't want to
 _   also need to explain exception handling in general
 _   https://github.com/processing/processing/issues/222
 _ bring PConstants back in line w/ previous 1.5 (can't renumber)
@@ -357,12 +381,12 @@ _ add reference/docs for urlEncode() and urlDecode()
 _ add explanation to the reference about using beginRecord() with fonts
 _   pdf.textMode(SHAPE)
 _   also set the font *after* the record has started
-_   maybe should instead make textMode(SHAPE) the norm? 
+_   maybe should instead make textMode(SHAPE) the norm?
 _   and people can change it to textMode(MODEL) if they want?
 _   http://dev.processing.org/bugs/show_bug.cgi?id=1535 (no gcode)
 _ explain the new PGL interface
 _ decide how disconnectEvent should actually be handled (and name?)
-_   was disconnect always there? 
+_   was disconnect always there?
 _   will need documentation
 _ negative indices so that we can work relative to the end in data classes?
 _ add requestFocus() method to PApplet (or surface?)
@@ -385,7 +409,7 @@ _   http://www.javalobby.org/forums/thread.jspa?threadID=16867&tstart=0
 _   http://www.gamedev.net/page/resources/_/technical/general-programming/java-games-active-rendering-r2418
 
 
-CORE / stop() 
+CORE / stop()
 
 _ in PApplet.main(), windowClosing() should probably be calling 'exit()'
 _   or rather, we should never call System.exit(0), ja?
@@ -415,7 +439,7 @@ _   if not looping, need to do it immediately
 _   does stop() unwind the thread, or does the thread unwind call stop?
 _   browser will call start() and stop() methods
 _ need to figure out start/stop signals coming from the browser
-_   is it dispose/destroy? 
+_   is it dispose/destroy?
 _ when closing a sketch via the close box, make sure stop() getting called
 _   test to see if it's working
 _ what's up with stop() vs exit()?
@@ -429,10 +453,10 @@ _ noloop ref even says that redraw will be called on resize, make sure it is
 _ focus not coming through, ESC no longer working(?)
 _ hitting cmd-q when an applet is running quits p5 (on macosx)
 _   but cmd-q when running externally is ok because it just quits
-_   is there a way to catch cmd-q when running a sketch? 
+_   is there a way to catch cmd-q when running a sketch?
 _     so that it could avoid quitting if the sketch hasn't been stopped
 _     or if the sketch window is foremost
-_     maybe a hack where a new menubar is added? 
+_     maybe a hack where a new menubar is added?
 o destroy() removed, but bring back? is that better than dispose()?
 _   destroy() only called dispose(), so no difference
 _   Python Mode has a hook for when it's called
@@ -446,7 +470,7 @@ _ Text rotation, placement and font metrics incorrect when scaled
 _   https://github.com/processing/processing/issues/2167
 
 _ remove subsetting stuff from PFont?
-_   or use hint(ENABLE_FONT_SUBSETTING)? 
+_   or use hint(ENABLE_FONT_SUBSETTING)?
 _ what's the difference with ascent on loadFont vs. createFont?
 _ svg fonts
 _   would be nifty if we could convert on the fly to ttf for speed...
@@ -460,7 +484,7 @@ _   book example 25-03
 _ accessors inside PFont need a lot of work
 _ improve font metrics
 _   http://java.sun.com/products/java-media/2D/reference/faqs/index.html#Q_How_do_I_obtain_font_metrics
-_ font encoding issues 
+_ font encoding issues
 _   java seems to force straight windows encoding.. (problem for pi fonts)
 _   opentype/cff fonts don't work with live loading from the app
 _   many (all?) opentype fonts won't show up or aren't supported
@@ -473,11 +497,11 @@ _ make screen space fonts use get/set as well?
 _   too much to debug on their own
 _   unfortunately tint not set with setImpl, but...
 _ not having kerning really blows
-_   could this be pulled from the OpenType font stuff? 
+_   could this be pulled from the OpenType font stuff?
 _   it could be placed at the end of the file
 _ not having fractional widths on small fonts really blows
 _   screen space text looks crappy
-_ working with vector fonts? 
+_ working with vector fonts?
 _   need to be able to handle shapes within shapes (reverse winding)
 _   ftgl: main code is in FTVectoriser
 _   uses gluTessBeginContour and gluTessEndContour
@@ -528,7 +552,7 @@ _ trying to remember why primitive was changed to kind? (better name?)
 _ these aren't per-vertex:
 _   get/setStrokeWeight
 _   get/setAmbient
-_   get/setEmissive 
+_   get/setEmissive
 _   get/setShininess
 _ boolean isGL() -> now removed
 _ unapproved (made protected)
@@ -620,7 +644,7 @@ _   touchEvent(), gestureEvent()?
 LIBRARIES / PDF
 
 _ beginRecord() doesn't use current settings of the sketch
-_   sometimes reported as a bug, but probably not a good way to 
+_   sometimes reported as a bug, but probably not a good way to
 _   consistently carry these over
 _ pdf sketches exiting before writing has finished
 _   people have to call exit() (so that dispose() is called in particular)
@@ -628,7 +652,7 @@ _ when using noLoop() and the PDF renderer, sketch should exit gracefully
 _   because isDisplayable() returns false, there's no coming back from noLoop
 
 
-LIBRARIES / Video 
+LIBRARIES / Video
 
 _ remove dispose() being used in the Movie and Capture
 _   added warning notes, but shouldn't be necessary
@@ -653,7 +677,7 @@ _ can we use String... for options? or does it screw up the method signature?
 _   and would we have to always concatenate arrays to prepend extensions, etc
 _ include Instant and Interval? (or just Time and TimeSpan)
 _ it's going to be File or Reader (mostly BufferedReader) everywhere
-_   though TableODS needs an InputStream... 
+_   though TableODS needs an InputStream...
 _   and XML could use InputStream if we hope to be able to reflect doc encoding
 _   ok, so File, Reader, and InputStream (where needed)
 _ setMissingXxxx() -> should this live in PApplet? be static?
@@ -670,7 +694,7 @@ _     that way we can do what's most efficient
 _   then we add keyIterator() and others to handle the other case (deletion)
 _   blogs.oracle.com/CoreJavaTechTips/entry/using_enhanced_for_loops_with
 _ means that JSON.keys() -> JSON.keyIterator(), JSON.keySet() -> JSON.keys()
-_ what should they all return? 
+_ what should they all return?
 _   remove -> true/false based on whether it found something?
 _   remove all -> the number removed?
 _ List: remove(), append(), index(), etc all use values
@@ -709,7 +733,7 @@ _   i.e. get unique tables based on a particular column
 _   or, get uniques, then grab rows that match a name in a particular col
 _ create table from TableRow iterator...allows for subset and find
 _   downside is types are not included
-_ getMaxFloat() (whole table) or getMaxFloat(col) 
+_ getMaxFloat() (whole table) or getMaxFloat(col)
 _   that's max(getFloatColumn(n))
 _   also important b/c can leave out missing values
 _ dictionary support
@@ -734,12 +758,12 @@ _   that way, methods could use the information when reading
 _ loadBytes() needs optimization
 _   don't bother using a buffering stream, just handle internally. gah!
 _ can loadBytes() be improved by querying file size first?
-_   background 
+_   background
 _     this would require a new version of createInput(), which would query
 _     the URL (or file) for an actual file size. the size is not always
 _     available, so it can't be relied upon, but would help in some cases.
 _   loadBytes() is used for images.. ouch
-_   might be worth doing a test to see if it actually would help at all 
+_   might be worth doing a test to see if it actually would help at all
 _     before rewriting all of createInput()
 
 
@@ -763,10 +787,10 @@ _ createColor() instead of color()?
 _ route all exceptions through PException and catch method
 _   advanced users can override the method if they want
 _   or you can set an option to have PExceptions be raised
-_   decision: just copy & paste the serial/net code.. 
+_   decision: just copy & paste the serial/net code..
 _     until we can find a more compelling example
 _ actual shape api for things like rectangles and whatnot?
-_ should we kill import xxxx syntax for libraries? 
+_ should we kill import xxxx syntax for libraries?
 _   just give up and use a gui for it
 _   cons: copy & paste breaks
 _   pro: there's no guaranteed 1:1 between packages and single libraries
diff --git a/java/libraries/dxf/.settings/org.eclipse.jdt.core.prefs b/java/libraries/dxf/.settings/org.eclipse.jdt.core.prefs
index 2770cf1bf3..87b7a7a3a6 100644
--- a/java/libraries/dxf/.settings/org.eclipse.jdt.core.prefs
+++ b/java/libraries/dxf/.settings/org.eclipse.jdt.core.prefs
@@ -1,12 +1,13 @@
-#Sat Nov 12 10:56:00 CST 2011
 eclipse.preferences.version=1
 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
-org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.compliance=1.8
 org.eclipse.jdt.core.compiler.debug.lineNumber=generate
 org.eclipse.jdt.core.compiler.debug.localVariable=generate
 org.eclipse.jdt.core.compiler.debug.sourceFile=generate
 org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
 org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.source=1.6
+org.eclipse.jdt.core.compiler.release=disabled
+org.eclipse.jdt.core.compiler.source=1.8
diff --git a/java/libraries/net/.settings/org.eclipse.jdt.core.prefs b/java/libraries/net/.settings/org.eclipse.jdt.core.prefs
index a11eccb8d4..f256b10aad 100644
--- a/java/libraries/net/.settings/org.eclipse.jdt.core.prefs
+++ b/java/libraries/net/.settings/org.eclipse.jdt.core.prefs
@@ -1,14 +1,16 @@
 eclipse.preferences.version=1
 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
-org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.compliance=1.8
 org.eclipse.jdt.core.compiler.debug.lineNumber=generate
 org.eclipse.jdt.core.compiler.debug.localVariable=generate
 org.eclipse.jdt.core.compiler.debug.sourceFile=generate
 org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
 org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.source=1.6
+org.eclipse.jdt.core.compiler.release=disabled
+org.eclipse.jdt.core.compiler.source=1.8
 org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
 org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=18
 org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
diff --git a/java/libraries/pdf/.settings/org.eclipse.jdt.core.prefs b/java/libraries/pdf/.settings/org.eclipse.jdt.core.prefs
index f3b4e11d65..160529e9d0 100644
--- a/java/libraries/pdf/.settings/org.eclipse.jdt.core.prefs
+++ b/java/libraries/pdf/.settings/org.eclipse.jdt.core.prefs
@@ -1,14 +1,16 @@
 eclipse.preferences.version=1
 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
-org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.compliance=1.8
 org.eclipse.jdt.core.compiler.debug.lineNumber=generate
 org.eclipse.jdt.core.compiler.debug.localVariable=generate
 org.eclipse.jdt.core.compiler.debug.sourceFile=generate
 org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
 org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.source=1.6
+org.eclipse.jdt.core.compiler.release=disabled
+org.eclipse.jdt.core.compiler.source=1.8
 org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
 org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=18
 org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
diff --git a/java/libraries/serial/.settings/org.eclipse.jdt.core.prefs b/java/libraries/serial/.settings/org.eclipse.jdt.core.prefs
index f709eac1ee..87b7a7a3a6 100644
--- a/java/libraries/serial/.settings/org.eclipse.jdt.core.prefs
+++ b/java/libraries/serial/.settings/org.eclipse.jdt.core.prefs
@@ -1,12 +1,13 @@
-#Sat Nov 12 10:56:44 CST 2011
 eclipse.preferences.version=1
 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
-org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.compliance=1.8
 org.eclipse.jdt.core.compiler.debug.lineNumber=generate
 org.eclipse.jdt.core.compiler.debug.localVariable=generate
 org.eclipse.jdt.core.compiler.debug.sourceFile=generate
 org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
 org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.source=1.6
+org.eclipse.jdt.core.compiler.release=disabled
+org.eclipse.jdt.core.compiler.source=1.8
diff --git a/java/src/processing/mode/java/Debugger.java b/java/src/processing/mode/java/Debugger.java
index ac6835b21e..3fcb069d5d 100644
--- a/java/src/processing/mode/java/Debugger.java
+++ b/java/src/processing/mode/java/Debugger.java
@@ -39,6 +39,7 @@
 import javax.swing.tree.DefaultMutableTreeNode;
 
 import processing.app.Messages;
+import processing.app.RunnerListenerEdtAdapter;
 import processing.app.Sketch;
 import processing.app.SketchCode;
 import processing.mode.java.debug.*;
@@ -201,7 +202,7 @@ public synchronized void startDebug() {
         //lineMap = LineMapping.generateMapping(srcPath + File.separator + mainClassName + ".java");
 
         log("launching debuggee runtime");
-        runtime = new Runner(build, editor);
+        runtime = new Runner(build, new RunnerListenerEdtAdapter(editor));
         VirtualMachine vm = runtime.debug(null); // non-blocking
         if (vm == null) {
           loge("error 37: launch failed", null);
diff --git a/java/src/processing/mode/java/JavaEditor.java b/java/src/processing/mode/java/JavaEditor.java
index 71cbea4b46..209a1e4277 100644
--- a/java/src/processing/mode/java/JavaEditor.java
+++ b/java/src/processing/mode/java/JavaEditor.java
@@ -1094,10 +1094,11 @@ protected void handleLaunch(boolean present, boolean tweak) {
         synchronized (runtimeLock) {
           if (runtimeLaunchRequested) {
             runtimeLaunchRequested = false;
+            RunnerListener listener = new RunnerListenerEdtAdapter(JavaEditor.this);
             if (!tweak) {
-              runtime = jmode.handleLaunch(sketch, JavaEditor.this, present);
+              runtime = jmode.handleLaunch(sketch, listener, present);
             } else {
-              runtime = jmode.handleTweak(sketch, JavaEditor.this);
+              runtime = jmode.handleTweak(sketch, listener);
             }
           }
         }
diff --git a/java/src/processing/mode/java/preproc/PdePreprocessor.java b/java/src/processing/mode/java/preproc/PdePreprocessor.java
index 432f5c4d4d..f22de1387c 100644
--- a/java/src/processing/mode/java/preproc/PdePreprocessor.java
+++ b/java/src/processing/mode/java/preproc/PdePreprocessor.java
@@ -27,6 +27,7 @@
 
 package processing.mode.java.preproc;
 
+import java.awt.EventQueue;
 import java.io.*;
 import java.util.*;
 import java.util.regex.MatchResult;
@@ -380,7 +381,9 @@ make sure that it uses numbers (or displayWidth/Height), copy into settings
           "The size of this sketch could not be determined from your code.\n" +
           "Use only numbers (not variables) for the size() command.\n" +
           "Read the size() reference for more details.";
-        Messages.showWarning("Could not find sketch size", message, null);
+        EventQueue.invokeLater(() -> {
+          Messages.showWarning("Could not find sketch size", message, null);
+        });
 //        new Exception().printStackTrace(System.out);
 //        return null;
         throw new SketchException("Please fix the size() line to continue.", false);
diff --git a/todo.txt b/todo.txt
index f1dfc5ee11..7db71da76a 100755
--- a/todo.txt
+++ b/todo.txt
@@ -1,5 +1,21 @@
 0266 (3.4.1 or 3.5)
 X update to Java 8u192
+o processing-java doesn't handle sketch exceptions by quitting the sketch
+X   https://github.com/processing/processing/issues/5375
+X   this is by design/follows PDE behavior
+X fix the link to the FAQ in the menu
+X   https://github.com/processing/processing/issues/5729
+X update to Java 8u202
+X "Sketch disappeared" infinite pop up dialogs
+X   https://github.com/processing/processing/pull/4808
+X   https://github.com/processing/processing/issues/4805
+
+fixed earlier
+X Could not initialize class com.sun.jna.Native on startup (Windows)
+X   https://github.com/processing/processing/issues/4929
+X   closed earlier; fixed as best we could
+X sharing usage metrics about libraries
+X   https://github.com/processing/processing/issues/4708
 
 contrib
 X Updated russian translation, now can choose russian in preferences
@@ -9,23 +25,35 @@ X   https://github.com/processing/processing/pull/5636
 X Examples dialog causes high CPU load
 X   https://github.com/processing/processing/issues/5246
 X   https://github.com/processing/processing/pull/5654
+X console hiding button
+X   https://github.com/processing/processing/pull/5115
+_ NullPointerException in Contribution Manager
+_   https://github.com/processing/processing/issues/5524
+_   https://github.com/processing/processing/pull/5742
+
+jakub
+X Fix sketch exception getting hidden by warning
+X   https://github.com/processing/processing/pull/5486
+X   https://github.com/processing/processing/issues/5412
+X EventQueue problems with "could not find sketch size" message
+X   https://github.com/processing/processing/issues/4893
+X   https://github.com/processing/processing/pull/5708
+X   https://github.com/processing/processing/issues/5030 (duplicate)
+X size(0, 0) just freezes instead of showing an error
+X   https://github.com/processing/processing/issues/5233 (duplicate)
+
+
 _ Find in Reference disabled for various keywords (draw, for, if, catch, while)
 _   https://github.com/processing/processing/issues/5562
 _   https://github.com/processing/processing/pull/5642
-
-_ Examples dialog causes high CPU load
-_   https://github.com/processing/processing/issues/5246
+_   discuss with Casey
 _ Welcome screen doesn't size properly for HiDPI screens
 _   https://github.com/processing/processing/issues/4896
-_ Find in Reference disabled for various keywords (draw, for, if, catch, while)
-_   https://github.com/processing/processing/issues/5562
-_ "Could not find a examples in the downloaded file" is a poorly worded message
 
 
-jakub
-_ Fix sketch exception getting hidden by warning
-_   https://github.com/processing/processing/pull/5486
-_   https://github.com/processing/processing/issues/5412
+nasty ones
+_ errors inside setup() aren't coming through at all?
+_   seen in Eclipse; have to turn on the debugger
 
 
 manager
@@ -44,6 +72,7 @@ _   mode > add mode > libraries > install video
 _   did not update the examples window, had to restart pde
 _ was able to save over the video capture examples b/c they were a library
 _   lib examples not properly marked as read-only
+_ "Could not find a examples in the downloaded file" is a poorly worded message
 
 
 temp
@@ -67,26 +96,6 @@ _ clean Windows temp folders
 _   https://github.com/processing/processing/issues/1896
 
 
-contrib
-_ console hiding button?
-_   https://github.com/processing/processing/pull/5115
-_ alternate handling of duplicate library conflicts
-_   https://github.com/processing/processing/pull/5126
-
-
-nasty ones
-_ errors inside setup() aren't coming through at all?
-_   seen in Eclipse; have to turn on the debugger
-_ "Sketch disappeared" infinite pop up dialogs
-_   https://github.com/processing/processing/pull/4808
-_   https://github.com/processing/processing/issues/4805
-_ EventQueue problems with "could not find sketch size" message
-_   https://github.com/processing/processing/issues/4893
-_   https://github.com/processing/processing/issues/5030
-_   size(0, 0) just freezes instead of showing an error (as a result)
-_   https://github.com/processing/processing/issues/5233
-
-
 _ sketch.properties not being written if initial mode is p5.js?
 _ when creating a sketch within non-Java mode, should write the settings file
 _   so that it re-loads in the proper environment
@@ -112,9 +121,6 @@ _   see the 'examples' section below
 _ how are file associations handled in Linux? (for .pde, .psk)
 
 
-_ Could not initialize class com.sun.jna.Native on startup (Windows)
-_   https://github.com/processing/processing/issues/4929
-
 _ implement fallback fonts instead of giving up and using Dialog and Mono
 _   https://github.com/processing/processing/issues/5023
 
@@ -157,11 +163,6 @@ _   https://github.com/processing/processing/issues/1476#issuecomment-23229990
 _ could not write to temporary directory (virus checker problems)
 _   https://github.com/processing/processing/issues/4757
 
-_ Export Application fails on machines w/ non-ASCII chars in user name
-_   at least give a warning about this?
-_   https://github.com/processing/processing/issues/4736
-_   related: https://github.com/processing/processing/issues/3543
-
 _ fix appbundler problems due to rollback
 _   https://github.com/processing/processing/issues/3790
 _   the rollback re-introduces two bugs (serial export and scrolling)
@@ -177,9 +178,6 @@ _   https://github.com/processing/processing/issues/4703
 _ right bracket missing error
 _   https://github.com/processing/processing/issues/4702
 
-_ sharing usage metrics about libraries
-_   https://github.com/processing/processing/issues/4708
-
 _ library compilations handled oddly
 _   https://github.com/processing/processing/issues/4630
 
@@ -223,17 +221,19 @@ _   https://github.com/processing/processing/pull/4097
 _   solution is to create a sprite sheet as a psd that'll have better type
 _     no way we're gonna fix the sizing and spacing for all platforms
 
-_ need docs for translations
-_   https://github.com/processing/processing/issues/4018
 
 _ setting a bad font/size causes a crash on startup
 _   https://github.com/processing/processing/issues/4085
 o   https://github.com/processing/processing/pull/4087
 
 
-more contribs
+translations
+_ need docs for translations
+_   https://github.com/processing/processing/issues/4018
 _ question about PDE_pt-br instead of PDE_pt
 _   https://github.com/processing/processing/issues/4018
+
+more contribs
 _ Saving sketch with the same name as a class
 _   https://github.com/processing/processing/pull/4033
 _ Pasting text into PDE results in "Clipboard does not contain a string"
@@ -246,6 +246,10 @@ _   but anything else reports "font sadness" b/c it's using the system JRE
 _   https://github.com/processing/processing/issues/3543
 _   move to javapackager or another option?
 _   http://www.excelsiorjet.com/kb/35/howto-create-a-single-exe-from-your-java-application
+_ Export Application fails on machines w/ non-ASCII chars in user name
+_   at least give a warning about this?
+_   https://github.com/processing/processing/issues/4736
+_   related: https://github.com/processing/processing/issues/3543
 _ mouse events (i.e. toggle breakpoint) seem to be firing twice
 
 
@@ -707,6 +711,8 @@ _ see how library installation goes, then possibly do same w/ examples
 
 PDE / Libraries
 
+_ alternate handling of duplicate library conflicts
+_   https://github.com/processing/processing/pull/5126
 _ Add a means to specify packages to import in library.properties
 _   https://github.com/processing/processing/issues/2134
 _ need to deal with classpath conflicts