blob: a828243ecb9f7aea6080fb2a572804c568cc15d1 [file] [log] [blame]
// Copyright 2018 The Chromium OS 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 <inttypes.h>
#include <stdio.h>
#include <iostream>
#include <memory>
#include <base/check_op.h>
#include <base/command_line.h>
#include <base/logging.h>
#include <base/strings/string_number_conversions.h>
#include "screen-capture-utils/bo_import_capture.h"
#include "screen-capture-utils/capture.h"
#include "screen-capture-utils/crtc.h"
#include "screen-capture-utils/egl_capture.h"
#include "screen-capture-utils/png.h"
namespace screenshot {
namespace {
constexpr const char kHelpSwitch[] = "help";
constexpr const char kInternalSwitch[] = "internal";
constexpr const char kExternalSwitch[] = "external";
constexpr const char kCrtcIdSwitch[] = "crtc-id";
constexpr const char kCropSwitch[] = "crop";
constexpr const char kMethodSwitch[] = "method";
constexpr const char kHelp[] =
"Usage: screenshot [options...] path/to/output.png\n"
"\n"
"Takes a screenshot and saves as a PNG file.\n"
"By default, a screenshot is captured from any active display.\n"
"\n"
"Options:\n"
" --internal: Capture from internal display.\n"
" --external: Capture from external display.\n"
" --crtc-id=ID: Capture from the specified display.\n"
" --crop=WxH+X+Y: Specify a subregion to capture.\n"
" --method=[egl|bo]: Force capture method to EGL or bo.\n";
enum class CaptureMethod {
AUTODETECT,
EGL,
BO,
};
void PrintHelp() {
std::cerr << kHelp;
}
int Main() {
auto* cmdline = base::CommandLine::ForCurrentProcess();
if (cmdline->HasSwitch(kHelpSwitch) || cmdline->GetArgs().empty()) {
PrintHelp();
return 1;
}
if (cmdline->GetArgs().size() != 1) {
LOG(ERROR) << "Must specify single output path";
return 1;
}
int crtc_specs = (cmdline->HasSwitch(kInternalSwitch) ? 1 : 0) +
(cmdline->HasSwitch(kExternalSwitch) ? 1 : 0) +
(cmdline->HasSwitch(kCrtcIdSwitch) ? 1 : 0);
if (crtc_specs > 1) {
LOG(ERROR) << "--internal, --external and --crtc-id are exclusive";
return 1;
}
bool crop_set = false;
uint32_t x, y, width, height;
if (cmdline->HasSwitch(kCropSwitch)) {
auto spec = cmdline->GetSwitchValueASCII(kCropSwitch);
int read_size;
int scan_size = sscanf(spec.c_str(),
"%" SCNu32 "x%" SCNu32 "+%" SCNu32 "+%" SCNu32 "%n",
&width, &height, &x, &y, &read_size);
if (scan_size != 4 || read_size != static_cast<int>(spec.size())) {
LOG(ERROR) << "Invalid --crop specification";
return 1;
}
CHECK_GT(width, 0);
CHECK_GT(height, 0);
crop_set = true;
}
CrtcFinder finder;
if (cmdline->HasSwitch(kInternalSwitch)) {
finder.SetSpec(CrtcFinder::Spec::kInternalDisplay);
} else if (cmdline->HasSwitch(kExternalSwitch)) {
finder.SetSpec(CrtcFinder::Spec::kExternalDisplay);
} else if (cmdline->HasSwitch(kCrtcIdSwitch)) {
uint32_t crtc_id;
if (!base::StringToUint(cmdline->GetSwitchValueASCII(kCrtcIdSwitch),
&crtc_id)) {
LOG(ERROR) << "Invalid --crtc-id specification";
return 1;
}
finder.SetSpec(CrtcFinder::Spec::kById);
finder.SetCrtcId(crtc_id);
}
auto crtc = finder.Find();
if (!crtc) {
LOG(ERROR) << "CRTC not found. Is the screen on?";
return 1;
}
CaptureMethod method = CaptureMethod::AUTODETECT;
if (cmdline->HasSwitch(kMethodSwitch)) {
std::string method_str = cmdline->GetSwitchValueASCII(kMethodSwitch);
if (method_str == "egl") {
method = CaptureMethod::EGL;
} else if (method_str == "bo") {
method = CaptureMethod::BO;
} else {
LOG(ERROR) << "Invalid --method specification";
return 1;
}
}
uint32_t crtc_width;
uint32_t crtc_height;
crtc_width = crtc->width();
crtc_height = crtc->height();
if (!crop_set) {
x = 0;
y = 0;
width = crtc_width;
height = crtc_height;
}
CHECK_LT(x, crtc_width);
CHECK_LT(y, crtc_height);
CHECK_LE(x + width, crtc_width);
CHECK_LE(y + height, crtc_height);
if (method == CaptureMethod::AUTODETECT) {
// TODO(andrescj): is it possible to still use the EGL path even if this
// is nullptr? e.g., if drmModeGetFB2() fails for the CRTC but not for
// individual planes.
//
// Also, it might be cleaner to move this logic to Crtc.
if (crtc->fb2())
method = CaptureMethod::EGL;
else
method = CaptureMethod::BO;
}
std::unique_ptr<screenshot::DisplayBuffer> display_buffer;
if (method == CaptureMethod::EGL) {
display_buffer.reset(
new screenshot::EglDisplayBuffer(crtc.get(), x, y, width, height));
} else {
display_buffer.reset(
new screenshot::GbmBoDisplayBuffer(crtc.get(), x, y, width, height));
}
screenshot::DisplayBuffer::Result result = display_buffer->Capture();
screenshot::SaveAsPng(cmdline->GetArgs()[0].c_str(), result.buffer,
result.width, result.height, result.stride);
return 0;
}
} // namespace
} // namespace screenshot
int main(int argc, char** argv) {
base::CommandLine::Init(argc, argv);
return screenshot::Main();
}