+void
+Surface::zero_controls ()
+{
+ if (_stype != mcu || !_mcp.device_info().has_global_controls()) {
+ return;
+ }
+
+ // turn off global buttons and leds
+ // global buttons are only ever on mcu_port, so we don't have
+ // to figure out which port.
+
+ for (Controls::iterator it = controls.begin(); it != controls.end(); ++it) {
+ Control & control = **it;
+ if (!control.group().is_strip()) {
+ _port->write (control.zero());
+ }
+ }
+
+ if (_number == 0 && _mcp.device_info().has_two_character_display()) {
+ // any hardware-specific stuff
+ // clear 2-char display
+ show_two_char_display (" ");
+ }
+
+ // and the led ring for the master strip
+ blank_jog_ring ();
+}
+
+void
+Surface::periodic (uint64_t now_usecs)
+{
+ for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
+ (*s)->periodic (now_usecs);
+ }
+}
+
+void
+Surface::write (const MidiByteArray& data)
+{
+ if (_active) {
+ _port->write (data);
+ }
+}
+
+void
+Surface::map_routes (const vector<boost::shared_ptr<Route> >& routes)
+{
+ vector<boost::shared_ptr<Route> >::const_iterator r;
+ Strips::iterator s = strips.begin();
+
+ for (r = routes.begin(); r != routes.end() && s != strips.end(); ++s) {
+
+ /* don't try to assign routes to a locked strip. it won't
+ use it anyway, but if we do, then we get out of sync
+ with the proposed mapping.
+ */
+
+ if (!(*s)->locked()) {
+ (*s)->set_route (*r);
+ ++r;
+ }
+ }
+
+ for (; s != strips.end(); ++s) {
+ (*s)->set_route (boost::shared_ptr<Route>());
+ }
+
+
+}
+
+static char
+translate_seven_segment (char achar)
+{
+ achar = toupper (achar);
+
+ if (achar >= 0x40 && achar <= 0x60) {
+ return achar - 0x40;
+ } else if (achar >= 0x21 && achar <= 0x3f) {
+ return achar;
+ } else {
+ return 0x00;
+ }
+}
+
+void
+Surface::show_two_char_display (const std::string & msg, const std::string & dots)
+{
+ if (_stype != mcu || !_mcp.device_info().has_two_character_display() || msg.length() != 2 || dots.length() != 2) {
+ return;
+ }
+
+ MidiByteArray right (3, 0xb0, 0x4b, 0x00);
+ MidiByteArray left (3, 0xb0, 0x4a, 0x00);
+
+ right[2] = translate_seven_segment (msg[0]) + (dots[0] == '.' ? 0x40 : 0x00);
+ left[2] = translate_seven_segment (msg[1]) + (dots[1] == '.' ? 0x40 : 0x00);
+
+ _port->write (right);
+ _port->write (left);
+}
+
+void
+Surface::show_two_char_display (unsigned int value, const std::string & /*dots*/)
+{
+ ostringstream os;
+ os << setfill('0') << setw(2) << value % 100;
+ show_two_char_display (os.str());
+}
+
+void
+Surface::display_timecode (const std::string & timecode, const std::string & last_timecode)
+{
+ if (!_active || !_mcp.device_info().has_timecode_display()) {
+ return;
+ }
+ // if there's no change, send nothing, not even sysex header
+ if (timecode == last_timecode) return;
+
+ // length sanity checking
+ string local_timecode = timecode;
+
+ // truncate to 10 characters
+ if (local_timecode.length() > 10) {
+ local_timecode = local_timecode.substr (0, 10);
+ }
+
+ // pad to 10 characters
+ while (local_timecode.length() < 10) {
+ local_timecode += " ";
+ }
+
+ // find the suffix of local_timecode that differs from last_timecode
+ std::pair<string::const_iterator,string::iterator> pp = mismatch (last_timecode.begin(), last_timecode.end(), local_timecode.begin());
+
+ int position = 0x40;
+
+ // translate characters. These are sent in reverse order of display
+ // hence the reverse iterators
+ string::reverse_iterator rend = reverse_iterator<string::iterator> (pp.second);
+ for (string::reverse_iterator it = local_timecode.rbegin(); it != rend; ++it) {
+ MidiByteArray retval (2, 0xb0, position++);
+ retval << translate_seven_segment (*it);
+ _port->write (retval);
+ }
+}
+
+void
+Surface::update_flip_mode_display ()
+{
+ for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
+ (*s)->flip_mode_changed (true);
+ }
+}
+
+void
+Surface::update_view_mode_display ()
+{
+ string text;
+ int id = -1;
+
+ if (!_active) {
+ return;
+ }
+
+ switch (_mcp.view_mode()) {
+ case MackieControlProtocol::Mixer:
+ show_two_char_display ("Mx");
+ id = Button::Pan;
+ break;
+ case MackieControlProtocol::Dynamics:
+ show_two_char_display ("Dy");
+ id = Button::Dyn;
+ break;
+ case MackieControlProtocol::EQ:
+ show_two_char_display ("EQ");
+ id = Button::Eq;
+ break;
+ case MackieControlProtocol::Loop:
+ show_two_char_display ("LP");
+ id = Button::Loop;
+ break;
+ case MackieControlProtocol::AudioTracks:
+ show_two_char_display ("AT");
+ break;
+ case MackieControlProtocol::MidiTracks:
+ show_two_char_display ("MT");
+ break;
+ case MackieControlProtocol::Sends:
+ show_two_char_display ("Sn");
+ id = Button::Sends;
+ break;
+ case MackieControlProtocol::Plugins:
+ show_two_char_display ("Pl");
+ id = Button::Plugin;
+ break;
+ default:
+ break;
+ }
+
+ if (id >= 0) {
+
+ /* we are attempting to turn a global button/LED on */
+
+ map<int,Control*>::iterator x = controls_by_device_independent_id.find (id);
+
+ if (x != controls_by_device_independent_id.end()) {
+ Button* button = dynamic_cast<Button*> (x->second);
+ if (button) {
+ _port->write (button->set_state (on));
+ }
+ }
+ }
+
+ if (!text.empty()) {
+ for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
+ _port->write ((*s)->display (1, text));
+ }
+ }
+}
+
+void
+Surface::gui_selection_changed (const ARDOUR::StrongRouteNotificationList& routes)
+{
+ for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
+ (*s)->gui_selection_changed (routes);
+ }
+}
+
+void
+Surface::say_hello ()
+{
+ /* wakeup for Mackie Control */
+ MidiByteArray wakeup (7, MIDI::sysex, 0x00, 0x00, 0x66, 0x14, 0x00, MIDI::eox);
+ _port->write (wakeup);
+ wakeup[4] = 0x15; /* wakup Mackie XT */
+ _port->write (wakeup);
+ wakeup[4] = 0x10; /* wakupe Logic Control */
+ _port->write (wakeup);
+ wakeup[4] = 0x11; /* wakeup Logic Control XT */
+ _port->write (wakeup);
+}
+
+void
+Surface::next_jog_mode ()
+{
+}
+
+void
+Surface::set_jog_mode (JogWheel::Mode)
+{
+}
+
+bool
+Surface::route_is_locked_to_strip (boost::shared_ptr<Route> r) const
+{
+ for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
+ if ((*s)->route() == r && (*s)->locked()) {
+ return true;
+ }
+ }
+ return false;
+}