+
+/** render host-side frame buffer (a Cairo ImageSurface) to the current
+ * device-side frame buffer. The device frame buffer will be pushed to the
+ * device on the next call to vblank()
+ */
+
+int
+Push2::render ()
+{
+ /* ensure that all drawing has been done before we fetch pixel data */
+
+ frame_buffer->flush ();
+
+ const int stride = 3840; /* bytes per row for Cairo::FORMAT_ARGB32 */
+ const uint8_t* data = frame_buffer->get_data ();
+
+ /* fill frame buffer (320kB) */
+
+ Glib::Threads::Mutex::Lock lm (fb_lock);
+
+ uint16_t* fb = (uint16_t*) device_frame_buffer[device_buffer];
+
+ for (int row = 0; row < rows; ++row) {
+
+ const uint8_t* dp = data + row * stride;
+
+ for (int col = 0; col < cols; ++col) {
+
+ /* fetch r, g, b (range 0..255). Ignore alpha */
+
+ const int r = (*((const uint32_t*)dp) >> 16) & 0xff;
+ const int g = (*((const uint32_t*)dp) >> 8) & 0xff;
+ const int b = *((const uint32_t*)dp) & 0xff;
+
+ /* convert to 5 bits, 6 bits, 5 bits, respectively */
+ /* generate 16 bit BGB565 value */
+
+ *fb++ = (r >> 3) | ((g & 0xfc) << 3) | ((b & 0xf8) << 8);
+
+ dp += 4;
+ }
+
+ /* skip 128 bytes to next line. This is filler, used to avoid line borders occuring in the middle of 512
+ byte USB buffers
+ */
+
+ fb += 64; /* 128 bytes = 64 int16_t */
+ }
+
+ /* swap buffers (under lock protection) */
+ // device_buffer = (device_buffer ? 0 : 1);
+
+ return 0;
+}
+
+bool
+Push2::vblank ()
+{
+ int transferred = 0;
+ const int timeout_msecs = 1000;
+ int err;
+
+ if ((err = libusb_bulk_transfer (handle, 0x01, frame_header, sizeof (frame_header), &transferred, timeout_msecs))) {
+ return false;
+ }
+
+ {
+ Glib::Threads::Mutex::Lock lm (fb_lock);
+
+ if ((err = libusb_bulk_transfer (handle, 0x01, (uint8_t*) device_frame_buffer[device_buffer] , 2 * rows * pixels_per_row, &transferred, timeout_msecs))) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+int
+Push2::set_active (bool yn)
+{
+ DEBUG_TRACE (DEBUG::Push2, string_compose("Push2Protocol::set_active init with yn: '%1'\n", yn));
+
+ if (yn == active()) {
+ return 0;
+ }
+
+ if (yn) {
+
+ if (open ()) {
+ DEBUG_TRACE (DEBUG::Push2, "device open failed\n");
+ close ();
+ return -1;
+ }
+
+ /* start event loop */
+
+ BaseUI::run ();
+
+ // connect_session_signals ();
+
+ /* say hello */
+
+ Cairo::RefPtr<Cairo::Context> context = Cairo::Context::create (frame_buffer);
+ if (!context) {
+ cerr << "Cannot create context\n";
+ return -1;
+ }
+ Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create (context);
+ if (!layout) {
+ cerr << "Cannot create layout\n";
+ return -1;
+ }
+
+ layout->set_text ("hello, Ardour");
+ Pango::FontDescription fd ("Sans Bold 12");
+ layout->set_font_description (fd);
+
+ context->set_source_rgb (0.0, 1.0, 1.0);
+ context->rectangle (0, 0, 960, 160);
+ context->fill ();
+ context->set_source_rgb (0.0, 0.0, 0.0);
+ context->rectangle (50, 50, 860, 60);
+ context->fill ();
+ context->move_to (60, 60);
+ context->set_source_rgb ((random()%255) / 255.0, (random()%255) / 255.0, (random()%255) / 255.0);
+ layout->update_from_cairo_context (context);
+ layout->show_in_cairo_context (context);
+
+ render ();
+
+ /* set up periodic task used to push a frame buffer to the
+ * device (25fps). The device can handle 60fps, but we don't
+ * need that frame rate.
+ */
+
+ Glib::RefPtr<Glib::TimeoutSource> vblank_timeout = Glib::TimeoutSource::create (40); // milliseconds
+ vblank_connection = vblank_timeout->connect (sigc::mem_fun (*this, &Push2::vblank));
+ vblank_timeout->attach (main_loop()->get_context());
+
+ } else {
+
+ BaseUI::quit ();
+ close ();
+
+ }
+
+ ControlProtocol::set_active (yn);
+
+ DEBUG_TRACE (DEBUG::Push2, string_compose("Push2Protocol::set_active done with yn: '%1'\n", yn));
+
+ return 0;
+}