From 08401f711aebf9aef96572ed8b14ed8974e7d8d8 Mon Sep 17 00:00:00 2001
From: Jim Dishaw <jim@dishaw.org>
Date: Mon, 15 Jun 2015 12:10:04 -0400
Subject: [PATCH] Fix to the multiple keypress bug on page advance

- The "wait for user input" is not part of an EOP.
  -- A new function plP_wait() was created
  -- plP_wait() calls were added next to plP_eop() calls to generate
     a wait (if specified by nopause) after an EOP event
- The plot buffer was modified to insert an EOP into the buffer
  -- No wait is inserted into the plot buffer because the plot buffer
     only regenerates plots
- A new entry was added to the PLDispatchTable for the wait function
  -- The table initialization sets all function pointers to NULL via
     a memset
  -- If a driver does not specify a wait function, either it is not
     needed (e.g. the ps driver) or it is part of the EOP handler
     (legacy behavior)
- The following drivers were modified to support the new implementation: cairo, qt, tkwin, and xwin.  The wingcc update will be in a seperate fix.
---
 drivers/cairo.c   | 77 ++++++++++++++++++++++++++++++++-----------------------
 drivers/qt.cpp    | 16 +++++++++---
 drivers/tkwin.c   | 24 ++++++++++++++++-
 drivers/xwin.c    | 31 +++++++++++++++++++---
 include/disptab.h |  2 ++
 include/plplotP.h |  5 ++++
 src/plbuf.c       |  6 +++--
 src/plcore.c      | 39 ++++++++++++++++++++++------
 src/plpage.c      |  2 ++
 9 files changed, 153 insertions(+), 49 deletions(-)

diff --git a/drivers/cairo.c b/drivers/cairo.c
index 4afa908..c3e949a 100644
--- a/drivers/cairo.c
+++ b/drivers/cairo.c
@@ -1848,6 +1848,7 @@ void plD_init_xcairo( PLStream * );
 void plD_bop_xcairo( PLStream * );
 void plD_eop_xcairo( PLStream * );
 void plD_tidy_xcairo( PLStream * );
+void plD_wait_xcairo( PLStream * );
 void plD_esc_xcairo( PLStream *, PLINT, void * );
 static void xcairo_get_cursor( PLStream *, PLGraphicsIn * );
 
@@ -1873,6 +1874,7 @@ void plD_dispatch_init_xcairo( PLDispatchTable *pdt )
     pdt->pl_tidy     = (plD_tidy_fp) plD_tidy_xcairo;
     pdt->pl_state    = (plD_state_fp) plD_state_cairo;
     pdt->pl_esc      = (plD_esc_fp) plD_esc_xcairo;
+    pdt->pl_wait     = (plD_wait_fp) plD_wait_xcairo;
 }
 
 //--------------------------------------------------------------------------
@@ -2098,11 +2100,51 @@ void plD_eop_xcairo( PLStream *pls )
 
     if ( aStream->xdrawable_mode )
         return;
+}
+
+//--------------------------------------------------------------------------
+// plD_tidy_xcairo()
+//
+// X Windows: close graphics file or otherwise clean up.
+//--------------------------------------------------------------------------
+
+void plD_tidy_xcairo( PLStream *pls )
+{
+    PLCairo *aStream;
+
+    aStream = (PLCairo *) pls->dev;
+
+    plD_tidy_cairo( pls );
+
+    // Also free up the Cairo X surface and context
+    cairo_destroy( aStream->cairoContext_X );
+    cairo_surface_destroy( aStream->cairoSurface_X );
+
+    if ( aStream->xdrawable_mode )
+        return;
+
+    // Close the window and the display.
+    XFlush( aStream->XDisplay );
+
+    XDestroyWindow( aStream->XDisplay, aStream->XWindow );
+
+    XCloseDisplay( aStream->XDisplay );
+}
+
+//--------------------------------------------------------------------------
+// plD_wait_xcairo()
+//
+// Wait for user input
+//--------------------------------------------------------------------------
 
-    // Only pause if nopause is unset.
-    if ( pls->nopause )
-        aStream->exit_event_loop = 1;
+void plD_wait_xcairo( PLStream *pls )
+{
+    PLCairo *aStream;
 
+    aStream = (PLCairo *) pls->dev;
+
+    aStream->exit_event_loop = 0;
+    
     // Loop, handling selected events, till the user elects to close the plot.
     event_mask = ButtonPressMask | KeyPressMask | ExposureMask;
     XSelectInput( aStream->XDisplay, aStream->XWindow, event_mask );
@@ -2145,35 +2187,6 @@ void plD_eop_xcairo( PLStream *pls )
 }
 
 //--------------------------------------------------------------------------
-// plD_tidy_xcairo()
-//
-// X Windows: close graphics file or otherwise clean up.
-//--------------------------------------------------------------------------
-
-void plD_tidy_xcairo( PLStream *pls )
-{
-    PLCairo *aStream;
-
-    aStream = (PLCairo *) pls->dev;
-
-    plD_tidy_cairo( pls );
-
-    // Also free up the Cairo X surface and context
-    cairo_destroy( aStream->cairoContext_X );
-    cairo_surface_destroy( aStream->cairoSurface_X );
-
-    if ( aStream->xdrawable_mode )
-        return;
-
-    // Close the window and the display.
-    XFlush( aStream->XDisplay );
-
-    XDestroyWindow( aStream->XDisplay, aStream->XWindow );
-
-    XCloseDisplay( aStream->XDisplay );
-}
-
-//--------------------------------------------------------------------------
 // plD_esc_xcairo()
 //
 // Escape function, specialized for the xcairo driver
diff --git a/drivers/qt.cpp b/drivers/qt.cpp
index 17bf807..da0727c 100644
--- a/drivers/qt.cpp
+++ b/drivers/qt.cpp
@@ -217,6 +217,7 @@ void plD_bop_pdfqt( PLStream * );
 void plD_dispatch_init_qtwidget( PLDispatchTable *pdt );
 void plD_init_qtwidget( PLStream * );
 void plD_eop_qtwidget( PLStream * );
+void plD_wait_qtwidget( PLStream * );
 void plD_line_qtwidget( PLStream *, short, short, short, short );
 void plD_polyline_qtwidget( PLStream *, short*, short*, PLINT );
 void plD_tidy_qtwidget( PLStream * );
@@ -1249,6 +1250,7 @@ void plD_dispatch_init_qtwidget( PLDispatchTable *pdt )
     pdt->pl_tidy     = (plD_tidy_fp) plD_tidy_qtwidget;
     pdt->pl_state    = (plD_state_fp) plD_state_qtwidget;
     pdt->pl_esc      = (plD_esc_fp) plD_esc_qtwidget;
+    pdt->pl_wait     = (plD_wait_fp) plD_wait_qtwidget;
 }
 
 void plD_init_qtwidget( PLStream * pls )
@@ -1321,13 +1323,21 @@ void plD_init_qtwidget( PLStream * pls )
 void plD_eop_qtwidget( PLStream *pls )
 {
     QtPLWidget* widget    = ( (QtPLWidget *) pls->dev );
-    int       currentPage = widget->pageNumber;
+
     widget->flush();
     widget->raise();
-    while ( currentPage == widget->pageNumber && handler.isMasterDevice( widget ) && !pls->nopause )
+}
+
+void plD_wait_qtwidget( PLStream *pls )
+{
+    QtPLWidget* widget    = ( (QtPLWidget *) pls->dev );
+    int       currentPage = widget->pageNumber;
+
+    widget->raise();
+    while ( currentPage == widget->pageNumber && handler.isMasterDevice( widget ) )
     {
         qApp->processEvents( QEventLoop::WaitForMoreEvents );
-    }
+    }  
 }
 
 void plD_bop_qtwidget( PLStream *pls )
diff --git a/drivers/tkwin.c b/drivers/tkwin.c
index 2f0000b..b8f8b09 100644
--- a/drivers/tkwin.c
+++ b/drivers/tkwin.c
@@ -211,6 +211,7 @@ void plD_bop_tkwin( PLStream * );
 void plD_tidy_tkwin( PLStream * );
 void plD_state_tkwin( PLStream *, PLINT );
 void plD_esc_tkwin( PLStream *, PLINT, void * );
+void plD_wait_tkwin( PLStream * );
 void plD_open_tkwin( PLStream *pls );
 
 void plD_dispatch_init_tkwin( PLDispatchTable *pdt )
@@ -229,6 +230,7 @@ void plD_dispatch_init_tkwin( PLDispatchTable *pdt )
     pdt->pl_tidy     = (plD_tidy_fp) plD_tidy_tkwin;
     pdt->pl_state    = (plD_state_fp) plD_state_tkwin;
     pdt->pl_esc      = (plD_esc_fp) plD_esc_tkwin;
+    pdt->pl_wait     = (plD_wait_fp) plD_wait_tkwin;
 }
 
 //--------------------------------------------------------------------------
@@ -523,7 +525,7 @@ plD_polyline_tkwin( PLStream *pls, short *xa, short *ya, PLINT npts )
 //--------------------------------------------------------------------------
 // plD_eop_tkwin()
 //
-// End of page. User must hit return (or third mouse button) to continue.
+// End of page. 
 //--------------------------------------------------------------------------
 
 void
@@ -545,6 +547,26 @@ plD_eop_tkwin( PLStream *pls )
 }
 
 //--------------------------------------------------------------------------
+// plD_wait_tkwin()
+//
+// User must hit return (or third mouse button) to continue.
+//--------------------------------------------------------------------------
+
+void
+plD_wait_tkwin( PLStream *pls )
+{
+    TkwDev     *dev  = (TkwDev *) pls->dev;
+    TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
+
+    dbug_enter( "plD_wait_tkw" );
+    if ( dev->flags & 1 )
+        return;
+
+    if ( !pls->nopause )
+        WaitForPage( pls );
+}
+
+//--------------------------------------------------------------------------
 // WaitForPage()
 //
 // This routine waits for the user to advance the plot, while handling
diff --git a/drivers/xwin.c b/drivers/xwin.c
index 982d28f..9635715 100644
--- a/drivers/xwin.c
+++ b/drivers/xwin.c
@@ -132,6 +132,7 @@ void plD_polyline_xw( PLStream *, short *, short *, PLINT );
 void plD_eop_xw( PLStream * );
 void plD_bop_xw( PLStream * );
 void plD_tidy_xw( PLStream * );
+void plD_wait_xw( PLStream * );
 void plD_state_xw( PLStream *, PLINT );
 void plD_esc_xw( PLStream *, PLINT, void * );
 
@@ -229,6 +230,7 @@ void plD_dispatch_init_xw( PLDispatchTable *pdt )
     pdt->pl_tidy     = (plD_tidy_fp) plD_tidy_xw;
     pdt->pl_state    = (plD_state_fp) plD_state_xw;
     pdt->pl_esc      = (plD_esc_fp) plD_esc_xw;
+    pdt->pl_wait     = (plD_wait_fp) plD_wait_xw;
 }
 
 //--------------------------------------------------------------------------
@@ -488,9 +490,6 @@ plD_eop_xw( PLStream *pls )
     if ( pls->db )
         ExposeCmd( pls, NULL );
 
-    if ( dev->is_main && !pls->nopause )
-        WaitForPage( pls );
-
 #ifdef PL_HAVE_PTHREAD
     if ( usepthreads )
         pthread_mutex_unlock( &events_mutex );
@@ -592,6 +591,32 @@ plD_tidy_xw( PLStream *pls )
 }
 
 //--------------------------------------------------------------------------
+// plD_wait_xw()
+//
+// Wait for user input
+//--------------------------------------------------------------------------
+
+void
+plD_wait_xw( PLStream *pls )
+{
+  XwDev     *dev = (XwDev *) pls->dev;
+  dbug_enter( "plD_eop_xw" );
+
+#ifdef PL_HAVE_PTHREAD
+  if ( usepthreads )
+    pthread_mutex_lock( &events_mutex );
+#endif
+  
+  if ( dev->is_main )
+        WaitForPage( pls );
+
+#ifdef PL_HAVE_PTHREAD
+    if ( usepthreads )
+        pthread_mutex_unlock( &events_mutex );
+#endif
+}
+
+//--------------------------------------------------------------------------
 // plD_state_xw()
 //
 // Handle change in PLStream state (color, pen width, fill attribute, etc).
diff --git a/include/disptab.h b/include/disptab.h
index d8d2362..94ade28 100644
--- a/include/disptab.h
+++ b/include/disptab.h
@@ -72,6 +72,7 @@ typedef void ( *plD_bop_fp )( struct PLStream_struct * );
 typedef void ( *plD_tidy_fp )( struct PLStream_struct * );
 typedef void ( *plD_state_fp )( struct PLStream_struct *, PLINT );
 typedef void ( *plD_esc_fp )( struct PLStream_struct *, PLINT, void * );
+typedef void ( *plD_wait_fp )( struct PLStream_struct * );
 
 typedef struct
 {
@@ -87,6 +88,7 @@ typedef struct
     plD_tidy_fp     pl_tidy;
     plD_state_fp    pl_state;
     plD_esc_fp      pl_esc;
+    plD_wait_fp     pl_wait;
 } PLDispatchTable;
 
 #endif // __DISPATCH_H__
diff --git a/include/plplotP.h b/include/plplotP.h
index bc4f3c4..892411b 100644
--- a/include/plplotP.h
+++ b/include/plplotP.h
@@ -1101,6 +1101,11 @@ plP_esc( PLINT op, void *ptr );
 void
 plP_swin( PLWindow *plwin );
 
+// Wait for user input
+
+PLDLLIMPEXP void
+plP_wait( void );
+  
 // Return file pointer to lib file.
 
 FILE *
diff --git a/src/plbuf.c b/src/plbuf.c
index 0e781b2..fd0f0c3 100644
--- a/src/plbuf.c
+++ b/src/plbuf.c
@@ -119,9 +119,11 @@ plbuf_init( PLStream *pls )
 //--------------------------------------------------------------------------
 
 void
-plbuf_eop( PLStream * PL_UNUSED( pls ) )
+plbuf_eop( PLStream * pls )
 {
     dbug_enter( "plbuf_eop" );
+
+    wr_command( pls, (U_CHAR) EOP );
 }
 
 //--------------------------------------------------------------------------
@@ -1425,7 +1427,7 @@ plFlushBuffer( PLStream *pls, PLBOOL restart, size_t amount )
             //of a page.
             //Doing this ensures that the first bop command in the
             //buffer actually does something
-            plP_eop();
+            //plP_eop();
         }
 
         finalReadPos = amount == (size_t) ( -1 ) ? pls->plbuf_top : MIN( pls->plbuf_readpos + amount, pls->plbuf_top );
diff --git a/src/plcore.c b/src/plcore.c
index 02152c5..36c4a3a 100644
--- a/src/plcore.c
+++ b/src/plcore.c
@@ -349,6 +349,26 @@ plP_swin( PLWindow *plwin )
     }
 }
 
+// Calls the device specific wait for user input function.  This
+// action depends on the state of the nopause flag and whether
+// user input is supported by the driver.
+
+void
+plP_wait( void )
+{
+    // If the nopause is disabled (which means pauses are wanted) and the
+    // the device supports waiting for user input
+    if( ! plsc->nopause && *plsc->dispatch_table->pl_wait != NULL)
+    {
+      char *save_locale = plsave_set_locale();
+      if ( !plsc->stream_closed )
+      {
+	( *plsc->dispatch_table->pl_wait )( (struct PLStream_struct *) plsc );
+      }
+      plrestore_locale( save_locale );
+    }
+}
+
 //--------------------------------------------------------------------------
 //  Drawing commands.
 //--------------------------------------------------------------------------
@@ -2498,6 +2518,7 @@ c_plend1( void )
     if ( plsc->level > 0 )
     {
         plP_eop();
+	plP_wait();
         plP_tidy();
         plsc->level = 0;
     }
@@ -3090,6 +3111,11 @@ plInitDispatchTable()
 #endif
             plexit( "plInitDispatchTable: Insufficient memory" );
         }
+	
+        // Initialize to zero to force all function pointers to NULL.  That way optional capabilities
+        // (e.g. wait for user input) do not need to be explicitly set to NULL in the driver's
+        // initialization function
+        memset( dispatch_table[n], 0, sizeof( PLDispatchTable) );
 
         ( *static_device_initializers[n] )( dispatch_table[n] );
     }
@@ -3144,19 +3170,16 @@ plInitDispatchTable()
             plexit( "plInitDispatchTable: Insufficient memory" );
         }
 
+        // Initialize to zero to force all function pointers to NULL.  That way optional capabilities
+        // (e.g. wait for user input) do not need to be explicitly set to NULL in the driver's
+        // initialization function nor do we need to do it in this function. 
+        memset( dispatch_table[n], 0, sizeof( PLDispatchTable) );
+	
         // Fill in the dispatch table entries.
         dispatch_table[n]->pl_MenuStr  = plstrdup( devdesc );
         dispatch_table[n]->pl_DevName  = plstrdup( devnam );
         dispatch_table[n]->pl_type     = atoi( devtype );
         dispatch_table[n]->pl_seq      = seq;
-        dispatch_table[n]->pl_init     = 0;
-        dispatch_table[n]->pl_line     = 0;
-        dispatch_table[n]->pl_polyline = 0;
-        dispatch_table[n]->pl_eop      = 0;
-        dispatch_table[n]->pl_bop      = 0;
-        dispatch_table[n]->pl_tidy     = 0;
-        dispatch_table[n]->pl_state    = 0;
-        dispatch_table[n]->pl_esc      = 0;
 
         // Add a record to the loadable device list
         loadable_device_list[i].devnam      = plstrdup( devnam );
diff --git a/src/plpage.c b/src/plpage.c
index a171a38..8c8c6aa 100644
--- a/src/plpage.c
+++ b/src/plpage.c
@@ -47,6 +47,7 @@ c_pladv( PLINT page )
         if ( plsc->cursub >= plsc->nsubx * plsc->nsuby )
         {
             plP_eop();
+	    plP_wait();
             plP_bop();
             plsc->cursub = 1;
         }
@@ -107,6 +108,7 @@ c_pleop( void )
 
     plsc->cursub = plsc->nsubx * plsc->nsuby;
     plP_eop();
+    plP_wait();
 }
 
 //--------------------------------------------------------------------------
-- 
2.3.2 (Apple Git-55)

