mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 15:40:19 +00:00
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:
parent
ec389adaa6
commit
1c06d77262
Notes:
sideshowbarker
2024-07-18 10:02:05 +09:00
Author: https://github.com/abyesilyurt Commit: https://github.com/SerenityOS/serenity/commit/1c06d772628 Pull-request: https://github.com/SerenityOS/serenity/pull/8515 Reviewed-by: https://github.com/MaxWipfli Reviewed-by: https://github.com/alimpfard Reviewed-by: https://github.com/gunnarbeutner
3 changed files with 105 additions and 4 deletions
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue