blob: a574ad2313015e2aa0edb509c4553b188e8a619d [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <cstdlib>
#include <iostream>
#include <memory>
#include <fuchsia/ui/scenic/cpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <trace-provider/provider.h>
#include "lib/component/cpp/startup_context.h"
#include "lib/fsl/vmo/vector.h"
#include "lib/fxl/command_line.h"
#include "lib/fxl/log_settings_command_line.h"
#include "lib/fxl/logging.h"
// These are the only values we want to track
const int kBlack = 0x000000;
const int kWhite = 0xeeeeee;
const int kGreen = 0x4dac26;
const int kRed = 0xd01c8b;
const int kMinExpectedPixels = 950000; // Typical value is ~1.5M
const int kMinPixelsForReport = 50000;
class ScreenshotTaker {
public:
explicit ScreenshotTaker(async::Loop* loop, bool output_screen)
: loop_(loop),
output_screen_(output_screen),
context_(component::StartupContext::CreateFromStartupInfo()) {
// Connect to the Scenic service.
scenic_ =
context_->ConnectToEnvironmentService<fuchsia::ui::scenic::Scenic>();
scenic_.set_error_handler([this](zx_status_t status) {
FXL_LOG(ERROR) << "Lost connection to Scenic service.";
encountered_error_ = true;
loop_->Quit();
});
}
bool encountered_error() const { return encountered_error_; }
void TakeScreenshot() {
FXL_LOG(INFO) << "start TakeScreenshot";
// If we wait for a call back from GetDisplayInfo, we are guaranteed that
// the GFX system is initialized, which is a prerequisite for taking a
// screenshot. TODO(SCN-678): Remove call to GetDisplayInfo once bug done.
scenic_->GetDisplayInfo([this](fuchsia::ui::gfx::DisplayInfo /*unused*/) {
TakeScreenshotInternal();
});
}
private:
void TakeScreenshotInternal() {
FXL_LOG(INFO) << "start TakeScreenshotInternal";
scenic_->TakeScreenshot(
[this](fuchsia::ui::scenic::ScreenshotData screenshot, bool status) {
FXL_LOG(INFO) << "start pixel capture";
std::vector<uint8_t> imgdata;
if (!status || !fsl::VectorFromVmo(screenshot.data, &imgdata)) {
FXL_LOG(ERROR) << "TakeScreenshot failed";
encountered_error_ = true;
loop_->Quit();
return;
}
std::map<int, int> histogram;
if (output_screen_) {
std::cout << "P6\n";
std::cout << screenshot.info.width << "\n";
std::cout << screenshot.info.height << "\n";
std::cout << 255 << "\n";
}
FXL_LOG(INFO) << "capturing pixels";
const uint8_t* pchannel = &imgdata[0];
for (uint32_t pixel = 0;
pixel < screenshot.info.width * screenshot.info.height;
pixel++) {
uint8_t rgb[] = {pchannel[2], pchannel[1], pchannel[0]};
if (output_screen_) {
std::cout.write(reinterpret_cast<const char*>(rgb), 3);
} else {
int rgb_value = (rgb[0] << 16) + (rgb[1] << 8) + rgb[2];
if (histogram.find(rgb_value) != histogram.end()) {
histogram[rgb_value] = histogram[rgb_value] + 1;
} else {
histogram[rgb_value] = 1;
}
}
pchannel += 4;
}
if (!output_screen_) {
// For success, there should be at least 1M green or red pixels
// combined The typical number is > 1.5M
if (histogram[kGreen] + histogram[kRed] > kMinExpectedPixels) {
FXL_LOG(INFO) << "success";
printf("success\n");
} else {
FXL_LOG(INFO) << "failure";
printf("failure\n");
printf("black: %d, white: %d, green: %d, red: %d\n",
histogram[kBlack], histogram[kWhite], histogram[kGreen],
histogram[kRed]);
// To help debug failures, if the majority of values aren't
// already expected, output the values that were over a threshold
// count.
if (histogram[kBlack] + histogram[kWhite] + histogram[kGreen] +
histogram[kRed] <
kMinExpectedPixels) {
const int MIN_REPORT_THRESHOLD = kMinPixelsForReport;
std::map<int, int>::iterator it;
for (const auto& pair : histogram) {
if (pair.second > MIN_REPORT_THRESHOLD) {
printf("Pixel 0x%06x occurred %d times\n", pair.first,
pair.second);
}
}
}
encountered_error_ = true;
}
}
loop_->Quit();
});
}
async::Loop* loop_;
const bool output_screen_;
std::unique_ptr<component::StartupContext> context_;
bool encountered_error_ = false;
fuchsia::ui::scenic::ScenicPtr scenic_;
};
int main(int argc, const char** argv) {
FXL_LOG(INFO) << "starting screen capture";
bool output_screen = true;
auto command_line = fxl::CommandLineFromArgcArgv(argc, argv);
if (!fxl::SetLogSettingsFromCommandLine(command_line))
return 1;
const auto& positional_args = command_line.positional_args();
if (!positional_args.empty()) {
if (positional_args.size() == 1 &&
positional_args[0].compare("-histogram") == 0) {
output_screen = false;
FXL_LOG(INFO) << "in histogram mode";
} else {
FXL_LOG(ERROR) << "Usage: screencap\n"
<< "Takes a screenshot in PPM format and writes it "
<< "to stdout.\n"
<< "To write to a file, redirect stdout, e.g.: "
<< "screencap > \"${DST}\"";
return 1;
}
}
FXL_LOG(INFO) << "setting up event loop";
async::Loop loop(&kAsyncLoopConfigAttachToThread);
trace::TraceProvider trace_provider(loop.dispatcher());
FXL_LOG(INFO) << "starting taker";
ScreenshotTaker taker(&loop, output_screen);
taker.TakeScreenshot();
FXL_LOG(INFO) << "starting Run()";
loop.Run();
FXL_LOG(INFO) << "returning result";
return taker.encountered_error() ? EXIT_FAILURE : EXIT_SUCCESS;
}