diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py
index 6108f42774af..8d80964b631a 100644
--- a/lib/matplotlib/backend_bases.py
+++ b/lib/matplotlib/backend_bases.py
@@ -2342,11 +2342,11 @@ def key_press_handler(event, canvas, toolbar=None):
         # pan mnemonic (default key 'p')
         elif event.key in pan_keys:
             toolbar.pan()
-            toolbar._set_cursor(event)
+            toolbar._update_cursor(event)
         # zoom mnemonic (default key 'o')
         elif event.key in zoom_keys:
             toolbar.zoom()
-            toolbar._set_cursor(event)
+            toolbar._update_cursor(event)
         # saving current figure (default key 's')
         elif event.key in save_keys:
             toolbar.save_figure()
@@ -2700,7 +2700,10 @@ class implementation.
         """
         raise NotImplementedError
 
-    def _set_cursor(self, event):
+    def _update_cursor(self, event):
+        """
+        Update the cursor after a mouse move event or a tool (de)activation.
+        """
         if not event.inaxes or not self._active:
             if self._lastCursor != cursors.POINTER:
                 self.set_cursor(cursors.POINTER)
@@ -2715,8 +2718,30 @@ def _set_cursor(self, event):
                 self.set_cursor(cursors.MOVE)
                 self._lastCursor = cursors.MOVE
 
+    @contextmanager
+    def _wait_cursor_for_draw_cm(self):
+        """
+        Set the cursor to a wait cursor when drawing the canvas.
+
+        In order to avoid constantly changing the cursor when the canvas
+        changes frequently, do nothing if this context was triggered during the
+        last second.  (Optimally we'd prefer only setting the wait cursor if
+        the *current* draw takes too long, but the current draw blocks the GUI
+        thread).
+        """
+        self._draw_time, last_draw_time = (
+            time.time(), getattr(self, "_draw_time", -np.inf))
+        if self._draw_time - last_draw_time > 1:
+            try:
+                self.set_cursor(cursors.WAIT)
+                yield
+            finally:
+                self.set_cursor(self._lastCursor)
+        else:
+            yield
+
     def mouse_move(self, event):
-        self._set_cursor(event)
+        self._update_cursor(event)
 
         if event.inaxes and event.inaxes.get_navigate():
 
diff --git a/lib/matplotlib/backends/backend_agg.py b/lib/matplotlib/backends/backend_agg.py
index 901b6f03fa95..f105107e8f08 100644
--- a/lib/matplotlib/backends/backend_agg.py
+++ b/lib/matplotlib/backends/backend_agg.py
@@ -23,8 +23,14 @@
     import threading
 except ImportError:
     import dummy_threading as threading
-import numpy as np
+try:
+    from contextlib import nullcontext
+except ImportError:
+    from contextlib import ExitStack as nullcontext  # Py 3.6.
 from math import radians, cos, sin
+
+import numpy as np
+
 from matplotlib import cbook, rcParams, __version__
 from matplotlib.backend_bases import (
     _Backend, FigureCanvasBase, FigureManagerBase, RendererBase)
@@ -383,7 +389,10 @@ def draw(self):
         Draw the figure using the renderer.
         """
         self.renderer = self.get_renderer(cleared=True)
-        with RendererAgg.lock:
+        # Acquire a lock on the shared font cache.
+        with RendererAgg.lock, \
+             (self.toolbar._wait_cursor_for_draw_cm() if self.toolbar
+              else nullcontext()):
             self.figure.draw(self.renderer)
             # A GUI class may be need to update a window using this draw, so
             # don't forget to call the superclass.
diff --git a/lib/matplotlib/backends/backend_gtk3cairo.py b/lib/matplotlib/backends/backend_gtk3cairo.py
index fae367a79cb3..af1355e49d7c 100644
--- a/lib/matplotlib/backends/backend_gtk3cairo.py
+++ b/lib/matplotlib/backends/backend_gtk3cairo.py
@@ -1,3 +1,8 @@
+try:
+    from contextlib import nullcontext
+except ImportError:
+    from contextlib import ExitStack as nullcontext  # Py 3.6.
+
 from . import backend_cairo, backend_gtk3
 from .backend_gtk3 import Gtk, _BackendGTK3
 from matplotlib import cbook
@@ -22,18 +27,15 @@ def _render_figure(self, width, height):
 
     def on_draw_event(self, widget, ctx):
         """GtkDrawable draw event."""
-        # toolbar = self.toolbar
-        # if toolbar:
-        #     toolbar.set_cursor(cursors.WAIT)
-        self._renderer.set_context(ctx)
-        allocation = self.get_allocation()
-        Gtk.render_background(
-            self.get_style_context(), ctx,
-            allocation.x, allocation.y, allocation.width, allocation.height)
-        self._render_figure(allocation.width, allocation.height)
-        # if toolbar:
-        #     toolbar.set_cursor(toolbar._lastCursor)
-        return False  # finish event propagation?
+        with (self.toolbar._wait_cursor_for_draw_cm() if self.toolbar
+              else nullcontext()):
+            self._renderer.set_context(ctx)
+            allocation = self.get_allocation()
+            Gtk.render_background(
+                self.get_style_context(), ctx,
+                allocation.x, allocation.y,
+                allocation.width, allocation.height)
+            self._render_figure(allocation.width, allocation.height)
 
 
 @cbook.deprecated("3.1", alternative="backend_gtk3.FigureManagerGTK3")