| // Copyright 2018 The ChromiumOS Authors | |
| // 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/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 kRotateSwitch[] = "rotate"; | |
| 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" | |
| " --rotate: Rotate captured screenshot 90 degrees clockwise.\n"; | |
| 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; | |
| } | |
| const bool rotate = cmdline->HasSwitch(kRotateSwitch); | |
| 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; | |
| } | |
| 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); | |
| std::unique_ptr<screenshot::DisplayBuffer> display_buffer; | |
| display_buffer.reset( | |
| new screenshot::EglDisplayBuffer(crtc.get(), x, y, width, height)); | |
| screenshot::DisplayBuffer::Result result = display_buffer->Capture(rotate); | |
| 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(); | |
| } |