From 9b4729dd40c5bed9fa2a63658daa816cb5716c87 Mon Sep 17 00:00:00 2001 From: Aziz Berkay Yesilyurt Date: Wed, 7 Jul 2021 01:36:23 +0200 Subject: [PATCH] Userland: Add ability to capture rectangular region in shot A click and drag selectable, transparent, fullscreen window is displayed with the command line argument -r for screenshots. --- Userland/Utilities/shot.cpp | 99 ++++++++++++++++++++++++++++++++++++- 1 file changed, 98 insertions(+), 1 deletion(-) diff --git a/Userland/Utilities/shot.cpp b/Userland/Utilities/shot.cpp index 2a4cffe76a..6ccd4e9433 100644 --- a/Userland/Utilities/shot.cpp +++ b/Userland/Utilities/shot.cpp @@ -1,19 +1,91 @@ /* * Copyright (c) 2021, Andreas Kling + * Copyright (c) 2021, Aziz Berkay Yesilyurt * * SPDX-License-Identifier: BSD-2-Clause */ #include +#include #include #include #include #include #include +#include +#include +#include #include #include +#include #include +class SelectableLayover final : public GUI::Widget { + C_OBJECT(SelectableLayover) +public: + SelectableLayover(GUI::Window* window) + : m_window(window) + , m_background_color(palette().threed_highlight()) + { + set_override_cursor(Gfx::StandardCursor::Crosshair); + } + + virtual ~SelectableLayover() override {}; + + Gfx::IntRect region() const + { + return m_region; + } + +private: + virtual void mousedown_event(GUI::MouseEvent& event) override + { + if (event.button() == GUI::MouseButton::Left) + m_anchor_point = event.position(); + }; + + virtual void mousemove_event(GUI::MouseEvent& event) override + { + if (m_anchor_point.has_value()) { + m_region = Gfx::IntRect::from_two_points(*m_anchor_point, event.position()); + update(); + } + }; + + virtual void mouseup_event(GUI::MouseEvent& event) override + { + if (event.button() == GUI::MouseButton::Left) + m_window->close(); + }; + + virtual void paint_event(GUI::PaintEvent&) override + { + if (m_region.is_empty()) { + GUI::Painter painter(*this); + painter.fill_rect(m_window->rect(), m_background_color); + return; + } + + GUI::Painter painter(*this); + painter.fill_rect(m_region, Gfx::Color::Transparent); + for (auto rect : m_window->rect().shatter(m_region)) + painter.fill_rect(rect, m_background_color); + } + + virtual void keydown_event(GUI::KeyEvent& event) override + { + if (event.key() == Key_Escape) { + m_region = Gfx::IntRect(); + m_window->close(); + } + } + + Optional m_anchor_point; + Gfx::IntRect m_region; + GUI::Window* m_window = nullptr; + Gfx::Color const m_background_color; +}; + int main(int argc, char** argv) { Core::ArgsParser args_parser; @@ -21,12 +93,14 @@ int main(int argc, char** argv) String output_path; bool output_to_clipboard = false; unsigned delay = 0; + bool select_region = false; int screen = -1; args_parser.add_positional_argument(output_path, "Output filename", "output", Core::ArgsParser::Required::No); args_parser.add_option(output_to_clipboard, "Output to clipboard", "clipboard", 'c'); args_parser.add_option(delay, "Seconds to wait before taking a screenshot", "delay", 'd', "seconds"); args_parser.add_option(screen, "The index of the screen (default: -1 for all screens)", "screen", 's', "index"); + args_parser.add_option(select_region, "Select a region to capture", "region", 'r'); args_parser.parse(argc, argv); @@ -35,6 +109,25 @@ int main(int argc, char** argv) } auto app = GUI::Application::construct(argc, argv); + Gfx::IntRect crop_region; + if (select_region) { + auto window = GUI::Window::construct(); + auto& container = window->set_main_widget(window); + container.set_fill_with_background_color(true); + + window->set_title("shot"); + window->set_opacity(0.2); + window->set_fullscreen(true); + window->show(); + app->exec(); + + crop_region = container.region(); + if (crop_region.is_empty()) { + dbgln("cancelled..."); + return 0; + } + } + sleep(delay); Optional screen_index; if (screen >= 0) @@ -43,12 +136,16 @@ int main(int argc, char** argv) auto shared_bitmap = GUI::WindowServerConnection::the().get_screen_bitmap({}, screen_index); dbgln("got screenshot"); - auto* bitmap = shared_bitmap.bitmap(); + RefPtr bitmap = shared_bitmap.bitmap(); if (!bitmap) { warnln("Failed to grab screenshot"); return 1; } + if (select_region) { + bitmap = bitmap->cropped(crop_region); + } + if (output_to_clipboard) { GUI::Clipboard::the().set_bitmap(*bitmap); return 0;