Userland: Add ability to screenshot rectangular region in shot (#8515)

* LibGUI: Verify m_window_id is not-zero in set_maximized

Window::set_maximized requires non-zero window id to be a valid call,
i.e. calling Window::show beforehand. A verify statement before the
server call can help developers by hinting correct usage.

* LibGUI: Paint background when the fullscreen window is transparent

The windows in the background are ignored when the window is fullscreen.
However, we still would like to see the background if that window is
transparent.

* 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.
This commit is contained in:
Aziz Berkay Yesilyurt 2021-07-09 10:41:24 +02:00 committed by GitHub
parent ec389adaa6
commit 1c06d77262
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
Notes: sideshowbarker 2024-07-18 10:02:05 +09:00
3 changed files with 105 additions and 4 deletions

View file

@ -948,6 +948,7 @@ bool Window::is_maximized() const
void Window::set_maximized(bool maximized)
{
VERIFY(m_window_id != 0);
WindowServerConnection::the().async_set_maximized(m_window_id, maximized);
}

View file

@ -504,7 +504,8 @@ void Compositor::compose()
// Paint the window stack.
if (m_invalidated_window) {
if (auto* fullscreen_window = wm.active_fullscreen_window()) {
auto* fullscreen_window = wm.active_fullscreen_window();
if (fullscreen_window && fullscreen_window->is_opaque()) {
compose_window(*fullscreen_window);
fullscreen_window->clear_dirty_rects();
} else {
@ -1071,7 +1072,8 @@ void Compositor::recompute_occlusions()
bool window_stack_transition_in_progress = m_transitioning_to_window_stack != nullptr;
auto& main_screen = Screen::main();
if (auto* fullscreen_window = wm.active_fullscreen_window()) {
auto* fullscreen_window = wm.active_fullscreen_window();
if (fullscreen_window) {
// TODO: support fullscreen windows on all screens
auto screen_rect = main_screen.rect();
wm.for_each_visible_window_from_front_to_back([&](Window& w) {
@ -1099,7 +1101,8 @@ void Compositor::recompute_occlusions()
});
m_opaque_wallpaper_rects.clear();
} else {
}
if (!fullscreen_window || (fullscreen_window && !fullscreen_window->is_opaque())) {
Gfx::DisjointRectSet visible_rects;
visible_rects.add_many(Screen::rects());
bool have_transparent = false;

View file

@ -1,19 +1,91 @@
/*
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2021, Aziz Berkay Yesilyurt <abyesilyurt@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Format.h>
#include <AK/Optional.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/DateTime.h>
#include <LibCore/File.h>
#include <LibGUI/Application.h>
#include <LibGUI/Clipboard.h>
#include <LibGUI/Painter.h>
#include <LibGUI/Widget.h>
#include <LibGUI/Window.h>
#include <LibGUI/WindowServerConnection.h>
#include <LibGfx/PNGWriter.h>
#include <LibGfx/Palette.h>
#include <unistd.h>
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<Gfx::IntPoint> 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<SelectableLayover>(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<u32> 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<Gfx::Bitmap> 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;