Index: tranzport_control_protocol.h
===================================================================
--- tranzport_control_protocol.h	(revision 1247)
+++ tranzport_control_protocol.h	(working copy)
@@ -72,7 +72,8 @@
 	enum WheelShiftMode {
 		WheelShiftGain,
 		WheelShiftPan,
-		WheelShiftMaster
+		WheelShiftMaster,
+		WheelShiftMarker
 	};
 		
 	enum WheelMode {
@@ -102,8 +103,10 @@
 	Glib::Mutex update_lock;
 	char current_screen[2][20];
 	char pending_screen[2][20];
+	char flash_screen[2][20];
 	bool lights[7];
 	bool pending_lights[7];
+	bool flash_lights[7];
 
 	bool           last_negative;
 	uint32_t       last_hrs;
@@ -121,12 +124,14 @@
 	int open ();
 	int read (uint32_t timeout_override = 0);
 	int write (uint8_t* cmd, uint32_t timeout_override = 0);
+	int write_noretry (uint8_t* cmd, uint32_t timeout_override = 0);
 	int close ();
 
 	int open_core (struct usb_device*);
 
 	void lcd_clear ();
 	void print (int row, int col, const char* text);
+	void print_noretry (int row, int col, const char* text);
 	int  light_on (LightID);
 	int  light_off (LightID);
 	void lights_off ();
@@ -141,6 +146,8 @@
 	void show_current_track ();
 	void show_track_gain ();
 	void show_transport_time ();
+	void show_bbt (nframes_t where);
+	void show_smpte (nframes_t where);
 	void show_wheel_mode ();
 	void show_gain ();
 	void show_pan ();
@@ -208,6 +215,9 @@
 
 	int process (uint8_t *);
 	int update_state();
+	int flush();
+	int flush_buttons();
+	int flush_screen();
 };
 
 
Index: tranzport_control_protocol.cc
===================================================================
--- tranzport_control_protocol.cc	(revision 1247)
+++ tranzport_control_protocol.cc	(working copy)
@@ -18,6 +18,27 @@
     $Id$
 */
 
+/* Design notes: The tranzport is a unique device, basically a 
+   20 lcd gui with 22 shift keys and 8 blinking lights. 
+   You can think of it as an altair for music.
+
+   It might be good one day, to break the gui, keyboard, and blinking light
+   components into separate parts, but for now, this remains monolithic.
+
+   As such it has several unique constraints. The device exerts flow control
+   by having a usb write fail. It is pointless to retry madly at that point,
+   the device is busy, and it's not going to become unbusy very quickly. 
+
+   So writes need to be either "mandatory" or "unreliable", and therein 
+   lies the rub.
+
+   mike.taht@gmail.com
+ */
+
+#define DEFAULT_USB_TIMEOUT 10
+#define MAX_RETRY 1
+#define DEBUG_TRANZPORT 1
+
 #include <iostream>
 #include <algorithm>
 #include <cmath>
@@ -33,6 +54,7 @@
 #include <ardour/route.h>
 #include <ardour/audio_track.h>
 #include <ardour/session.h>
+#include <ardour/tempo.h>
 #include <ardour/location.h>
 #include <ardour/dB.h>
 
@@ -75,7 +97,7 @@
 
 	set_route_table_size (1);
 	
-	timeout = 60000;
+	timeout = 6000;
 	buttonmask = 0;
 	_datawheel = 0;
 	_device_status = STATUS_OFFLINE;
@@ -92,6 +114,7 @@
 
 	memset (current_screen, 0, sizeof (current_screen));
 	memset (pending_screen, 0, sizeof (pending_screen));
+	memset (flash_screen, 0, sizeof (flash_screen));
 
 	for (uint32_t i = 0; i < sizeof(lights)/sizeof(lights[0]); ++i) {
 		lights[i] = false;
@@ -100,6 +123,10 @@
 	for (uint32_t i = 0; i < sizeof(pending_lights)/sizeof(pending_lights[0]); ++i) {
 		pending_lights[i] = false;
 	}
+
+	for (uint32_t i = 0; i < sizeof(flash_lights)/sizeof(flash_lights[0]); ++i) {
+		flash_lights[i] = false;
+	}
 }
 
 TranzportControlProtocol::~TranzportControlProtocol ()
@@ -149,9 +176,9 @@
 		} else {
 			cerr << "Begin tranzport shutdown\n";
 			pthread_cancel_one (thread);
-			cerr << "Thread dead\n";
-			// lcd_clear ();
-			// lights_off ();
+			cerr << "Tranzport Thread dead\n";
+			lcd_clear ();
+			lights_off ();
 			// cerr << "dev reset\n";
 			close ();
 			_active = false;
@@ -168,7 +195,7 @@
 	if (route_table[0]) {
 		gain_t g = route_get_gain (0);
 		if (g != last_track_gain) {
-			char buf[16];
+			char buf[9];
 			snprintf (buf, sizeof (buf), "%6.1fdB", coefficient_to_dB (route_get_effective_gain (0)));
 			print (0, 9, buf); 
 			last_track_gain = g;
@@ -219,9 +246,9 @@
 	lcd_clear ();
 	lights_off ();
 	show_current_track ();
+	show_transport_time ();
+	show_track_gain ();
 	show_wheel_mode ();
-	show_wheel_mode ();
-	show_transport_time ();
 	display_mode = DisplayNormal;
 }
 
@@ -230,10 +257,11 @@
 log_meter (float db)
 {
 	float def = 0.0f; /* Meter deflection %age */
- 
-	if (db < -70.0f) {
-		def = 0.0f;
-	} else if (db < -60.0f) {
+
+	if (db < -70.0f) return 0.0f;
+ 	if (db > 6.0f) return 1.0f;
+
+	if (db < -60.0f) {
 		def = (db + 70.0f) * 0.25f;
 	} else if (db < -50.0f) {
 		def = (db + 60.0f) * 0.5f + 2.5f;
@@ -245,8 +273,6 @@
 		def = (db + 30.0f) * 2.0f + 30.0f;
 	} else if (db < 6.0f) {
 		def = (db + 20.0f) * 2.5f + 50.0f;
-	} else {
-		def = 115.0f;
 	}
 	
 	/* 115 is the deflection %age that would be 
@@ -267,6 +293,8 @@
 	float level = route_get_peak_input_power (0, 0);
 	float fraction = log_meter (level);
 
+	/* Someday add a peak bar*/
+
 	/* we draw using a choice of a sort of double colon-like character ("::") or a single, left-aligned ":".
 	   the screen is 20 chars wide, so we can display 40 different levels. compute the level,
 	   then figure out how many "::" to fill. if the answer is odd, make the last one a ":"
@@ -318,10 +346,36 @@
 }
 
 void
+TranzportControlProtocol::show_bbt (nframes_t where)
+{ 
+	if (where != last_where) {
+	  char buf[16];
+	  BBT_Time bbt;
+	  session->tempo_map().bbt_time (where, bbt);
+	  sprintf (buf, "%03" PRIu32 "|%02" PRIu32 "|%04" PRIu32, bbt.bars,bbt.beats,bbt.ticks);
+	  print (1, 9, buf);
+	  last_where = where;
+
+	  //        if (bbt_upper_info_label) {
+	  //     TempoMap::Metric m (session->tempo_map().metric_at (when));
+	  //     sprintf (buf, "%-5.2f", m.tempo().beats_per_minute());
+          //      bbt_lower_info_label->set_text (buf);
+          //      sprintf (buf, "%g|%g", m.meter().beats_per_bar(), m.meter().note_divisor());
+          //      bbt_upper_info_label->set_text (buf);
+        }
+}
+
+
+void
 TranzportControlProtocol::show_transport_time ()
 {
 	nframes_t where = session->transport_frame();
-	
+	show_bbt(where);
+}	
+
+void
+TranzportControlProtocol::show_smpte (nframes_t where)
+{
 	if (where != last_where) {
 
 		char buf[5];
@@ -343,7 +397,7 @@
 		print (1, 15, buf);
 
 		sprintf (buf, "%02" PRIu32, smpte.frames);
-		print (1, 18, buf);
+		print_noretry (1, 18, buf); 
 
 		last_where = where;
 	}
@@ -426,20 +480,93 @@
 }
 	
 int
-TranzportControlProtocol::write (uint8_t* cmd, uint32_t timeout_override)
+TranzportControlProtocol::write_noretry (uint8_t* cmd, uint32_t timeout_override)
 {
 	int val;
 
 	val = usb_interrupt_write (udev, WRITE_ENDPOINT, (char*) cmd, 8, timeout_override ? timeout_override : timeout);
 
-	if (val < 0)
+	if (val < 0) {
+		printf("usb_interrupt_write failed: %d\n", val);
 		return val;
-	if (val != 8)
+		}
+
+	if (val != 8) {
+		printf("usb_interrupt_write failed: %d\n", val);
 		return -1;
+		}
+
 	return 0;
 
 }	
 
+int
+TranzportControlProtocol::write (uint8_t* cmd, uint32_t timeout_override)
+{
+	int val;
+	int retry = 0;
+
+	while((val = usb_interrupt_write (udev, WRITE_ENDPOINT, (char*) cmd, 8, timeout_override ? timeout_override : timeout))!=8 && retry++ < MAX_RETRY) {
+		printf("usb_interrupt_write failed, retrying: %d\n", val);
+	}
+
+	if (retry == MAX_RETRY) {
+		printf("Too many retries on a tranzport write, aborting\n");
+		}
+
+	if (val < 0) {
+		printf("usb_interrupt_write failed: %d\n", val);
+		return val;
+		}
+	if (val != 8) {
+		printf("usb_interrupt_write failed: %d\n", val);
+		return -1;
+		}
+	return 0;
+
+}	
+
+
+// We have a state "Unknown"
+// We have another state - no_retry. Misleading, as we still retry on the next pass
+// We have an "displayed" screen
+// We always draw into the pending screen
+// We have an active screen
+// Print arg - we have 
+// setactive
+// flush
+
+/*
+flash_screen.clear();
+flash_screen.print(0,0,"Undone:"); // Someday pull the undo stack from somewhere
+flash_screen.print(1,0,"Nextup:"); 
+
+if(flash_messages && lcd.getactive() != flash_screen) lcd.setactive(flash_screen,2000);
+
+screen::setactive(screen_name,duration); // duration in ms
+screen::getactive();
+*/
+
+
+int
+TranzportControlProtocol::flush ()
+{
+    return flush_buttons() || flush_screen(); 
+}
+ 
+int
+TranzportControlProtocol::flush_buttons ()
+{
+  return 0;
+}
+
+int
+TranzportControlProtocol::flush_screen ()
+{
+  return 0;
+}
+
+
 void
 TranzportControlProtocol::lcd_clear ()
 {
@@ -457,11 +584,12 @@
 
 	for (uint8_t i = 0; i < 10; ++i) {
 		cmd[2] = i;
-		usb_interrupt_write (udev, WRITE_ENDPOINT, (char*) cmd, 8, 1000);
+		write (cmd, DEFAULT_USB_TIMEOUT);
 	}
 	
-	memset (current_screen, ' ', sizeof (current_screen));
+ 	memset (current_screen, ' ', sizeof (current_screen));
 	memset (pending_screen, ' ', sizeof (pending_screen));
+	memset (flash_screen, ' ', sizeof (pending_screen));
 }
 
 void
@@ -478,31 +606,31 @@
 	cmd[7] = 0x00;
 
 	cmd[2] = LightRecord;
-	if (write (cmd, 1000) == 0) {
+	if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
 		lights[LightRecord] = false;
 	}
 	cmd[2] = LightTrackrec;
-	if (write (cmd, 1000) == 0) {
+	if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
 		lights[LightTrackrec] = false;
 	}
 	cmd[2] = LightTrackmute;
-	if (write (cmd, 1000) == 0) {
+	if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
 		lights[LightTrackmute] = false;
 	}
 	cmd[2] = LightTracksolo;
-	if (write (cmd, 1000) == 0) {
+	if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
 		lights[LightTracksolo] = false;
 	}
 	cmd[2] = LightAnysolo;
-	if (write (cmd, 1000) == 0) {
+	if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
 		lights[LightAnysolo] = false;
 	}
 	cmd[2] = LightLoop;
-	if (write (cmd, 1000) == 0) {
+	if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
 		lights[LightLoop] = false;
 	}
 	cmd[2] = LightPunch;
-	if (write (cmd, 1000) == 0) {
+	if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
 		lights[LightPunch] = false;
 	}
 }
@@ -523,7 +651,7 @@
 		cmd[6] = 0x00;
 		cmd[7] = 0x00;
 
-		if (write (cmd, 1000) == 0) {
+		if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
 			lights[light] = true;
 			return 0;
 		} else {
@@ -551,7 +679,7 @@
 		cmd[6] = 0x00;
 		cmd[7] = 0x00;
 
-		if (write (cmd, 1000) == 0) {
+		if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
 			lights[light] = false;
 			return 0;
 		} else {
@@ -571,11 +699,13 @@
 	uint8_t buf[8];
 	int val;
 	bool first_time = true;
+	int pending = 0;
+	uint8_t offline = 0;
 
 	PBD::ThreadCreated (pthread_self(), X_("Tranzport"));
 
 	memset (&rtparam, 0, sizeof (rtparam));
-	rtparam.sched_priority = 3; /* XXX should be relative to audio (JACK) thread */
+	rtparam.sched_priority = 50; /* XXX should be relative to audio (JACK) thread */
 	
 	if ((err = pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam)) != 0) {
 		// do we care? not particularly.
@@ -594,12 +724,15 @@
 		/* anything to read ? */
 
 		if (_device_status == STATUS_OFFLINE) {
-			light_off (LightRecord);
-			first_time = true;
+		  if(offline == 1) { 
+		  cerr << "Transport has gone offline\n";
+		  light_off (LightRecord); // do we really want to keep poking the device while it's toast?
+		  }
+		  first_time = true;
 		}
 
 		pthread_testcancel();
-		val = usb_interrupt_read (udev, READ_ENDPOINT, (char*) buf, 8, 10);
+		val = usb_interrupt_read (udev, READ_ENDPOINT, (char*) buf, 8, 20);
 		pthread_testcancel();
 
 		if (val == 8) {
@@ -611,9 +744,12 @@
 				lcd_clear ();
 				lights_off ();
 				first_time = false;
+				offline = 0;
 			}
 			/* update whatever needs updating */
-			update_state ();
+			pending = update_state ();
+		} else {
+		  offline++;
 		}
 	}
 
@@ -627,6 +763,9 @@
 	int col_base;
 	int col;
 	int cell;
+	int retry;
+	int val;
+	int pending = 0;
 
 	/* do the text updates */
 
@@ -647,7 +786,6 @@
 	for (row = 0; row < 2; ++row) {
 
 		for (col_base = 0, col = 0; col < 20; ) {
-			
 			if (pending_screen[row][col] != current_screen[row][col]) {
 
 				/* something in this cell is different, so dump the cell
@@ -664,10 +802,14 @@
 				cmd[5] = pending_screen[row][col_base+2];
 				cmd[6] = pending_screen[row][col_base+3];
 				cmd[7] = 0x00;
-
-				if (usb_interrupt_write (udev, WRITE_ENDPOINT, (char *) cmd, 8, 1000) == 8) {
+				retry= write(cmd);
+				if(retry == -(MAX_RETRY)) {
+				  /* try to update this cell on the next go-round */
+					printf("usb screen update failed for some reason... why? cmd and data were %02x %02x %02x %02x %02x %02x %02x %02x\n", cmd[0],cmd[1],cmd[2], cmd[3], cmd[4], cmd[5],cmd[6],cmd[7]); 
+					pending += 1;
+				} else {
 					/* successful write: copy to current */
-					memcpy (&current_screen[row][col_base], &pending_screen[row][col_base], 4);
+				  memcpy (&current_screen[row][col_base], &pending_screen[row][col_base], 4);
 				}
 
 				/* skip the rest of the 4 character cell since we wrote+copied it already */
@@ -808,7 +950,7 @@
 		}
 	}
 
-	return 0;
+	return pending;
 }
 
 int
@@ -981,11 +1123,20 @@
 void
 TranzportControlProtocol::show_current_track ()
 {
-	if (route_table[0] == 0) {
-		print (0, 0, "--------");
-	} else {
-		print (0, 0, route_get_name (0).substr (0, 8).c_str());
-	}
+  char pad[9];
+  char *v;
+  int len;
+  if (route_table[0] == 0) {
+    print (0, 0, "--------");
+    last_track_gain = -999.0;
+  } else {
+    strcpy(pad,"        ");
+    v =  (char *)route_get_name (0).substr (0, 8).c_str();
+    if((len = strlen(v)) > 0) {
+      strncpy(pad,(char *)v,len);
+    }
+    print (0, 0, pad);
+  }
 }
 
 void
@@ -1006,6 +1157,11 @@
 void
 TranzportControlProtocol::button_event_backlight_release (bool shifted)
 {
+	if (shifted) {
+	   	memset (current_screen, ' ', sizeof (current_screen));
+		last_track_gain = FLT_MAX;
+		normal_update(); //  redraw_screen();  
+	}
 }
 
 void
@@ -1523,8 +1679,13 @@
 	print (1, 0, text.c_str());
 }
 
+	void
+TranzportControlProtocol::print (int row, int col, const char *text) {
+	print_noretry(row,col,text);
+}
+
 void
-TranzportControlProtocol::print (int row, int col, const char *text)
+TranzportControlProtocol::print_noretry (int row, int col, const char *text)
 {
 	int cell;
 	uint32_t left = strlen (text);
