shot.cpp 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. /*
  2. * Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2021, Aziz Berkay Yesilyurt <abyesilyurt@gmail.com>
  4. * Copyright (c) 2022, Alex Major
  5. *
  6. * SPDX-License-Identifier: BSD-2-Clause
  7. */
  8. #include <AK/Format.h>
  9. #include <AK/Optional.h>
  10. #include <AK/URL.h>
  11. #include <LibCore/ArgsParser.h>
  12. #include <LibCore/DateTime.h>
  13. #include <LibCore/File.h>
  14. #include <LibCore/Process.h>
  15. #include <LibCore/Stream.h>
  16. #include <LibGUI/Application.h>
  17. #include <LibGUI/Clipboard.h>
  18. #include <LibGUI/ConnectionToWindowServer.h>
  19. #include <LibGUI/Painter.h>
  20. #include <LibGUI/Widget.h>
  21. #include <LibGUI/Window.h>
  22. #include <LibGfx/PNGWriter.h>
  23. #include <LibGfx/Palette.h>
  24. #include <LibMain/Main.h>
  25. #include <unistd.h>
  26. class SelectableLayover final : public GUI::Widget {
  27. C_OBJECT(SelectableLayover)
  28. public:
  29. virtual ~SelectableLayover() override {};
  30. Gfx::IntRect region() const
  31. {
  32. return m_region;
  33. }
  34. private:
  35. SelectableLayover(GUI::Window* window)
  36. : m_window(window)
  37. , m_background_color(palette().threed_highlight().with_alpha(128))
  38. {
  39. set_override_cursor(Gfx::StandardCursor::Crosshair);
  40. }
  41. virtual void mousedown_event(GUI::MouseEvent& event) override
  42. {
  43. if (event.button() == GUI::MouseButton::Primary)
  44. m_anchor_point = event.position();
  45. };
  46. virtual void mousemove_event(GUI::MouseEvent& event) override
  47. {
  48. if (m_anchor_point.has_value()) {
  49. m_region = Gfx::IntRect::from_two_points(*m_anchor_point, event.position());
  50. update();
  51. }
  52. };
  53. virtual void mouseup_event(GUI::MouseEvent& event) override
  54. {
  55. if (event.button() == GUI::MouseButton::Primary)
  56. m_window->close();
  57. };
  58. virtual void paint_event(GUI::PaintEvent&) override
  59. {
  60. GUI::Painter painter(*this);
  61. painter.clear_rect(m_window->rect(), Gfx::Color::Transparent);
  62. painter.fill_rect(m_window->rect(), m_background_color);
  63. if (m_region.is_empty())
  64. return;
  65. painter.clear_rect(m_region, Gfx::Color::Transparent);
  66. }
  67. virtual void keydown_event(GUI::KeyEvent& event) override
  68. {
  69. if (event.key() == Key_Escape) {
  70. m_region = Gfx::IntRect();
  71. m_window->close();
  72. }
  73. }
  74. Optional<Gfx::IntPoint> m_anchor_point;
  75. Gfx::IntRect m_region;
  76. GUI::Window* m_window = nullptr;
  77. Gfx::Color const m_background_color;
  78. };
  79. ErrorOr<int> serenity_main(Main::Arguments arguments)
  80. {
  81. Core::ArgsParser args_parser;
  82. DeprecatedString output_path;
  83. bool output_to_clipboard = false;
  84. unsigned delay = 0;
  85. bool select_region = false;
  86. bool edit_image = false;
  87. int screen = -1;
  88. args_parser.add_positional_argument(output_path, "Output filename", "output", Core::ArgsParser::Required::No);
  89. args_parser.add_option(output_to_clipboard, "Output to clipboard", "clipboard", 'c');
  90. args_parser.add_option(delay, "Seconds to wait before taking a screenshot", "delay", 'd', "seconds");
  91. args_parser.add_option(screen, "The index of the screen (default: -1 for all screens)", "screen", 's', "index");
  92. args_parser.add_option(select_region, "Select a region to capture", "region", 'r');
  93. args_parser.add_option(edit_image, "Open in PixelPaint", "edit", 'e');
  94. args_parser.parse(arguments);
  95. if (output_path.is_empty()) {
  96. output_path = Core::DateTime::now().to_deprecated_string("screenshot-%Y-%m-%d-%H-%M-%S.png"sv);
  97. }
  98. auto app = TRY(GUI::Application::try_create(arguments));
  99. Optional<Gfx::IntRect> crop_region;
  100. if (select_region) {
  101. auto window = GUI::Window::construct();
  102. auto& container = window->set_main_widget<SelectableLayover>(window);
  103. window->set_title("shot");
  104. window->set_has_alpha_channel(true);
  105. window->set_fullscreen(true);
  106. window->show();
  107. app->exec();
  108. crop_region = container.region();
  109. if (crop_region.value().is_empty()) {
  110. dbgln("cancelled...");
  111. return 0;
  112. }
  113. }
  114. sleep(delay);
  115. Optional<u32> screen_index;
  116. if (screen >= 0)
  117. screen_index = (u32)screen;
  118. dbgln("getting screenshot...");
  119. auto shared_bitmap = GUI::ConnectionToWindowServer::the().get_screen_bitmap(crop_region, screen_index);
  120. dbgln("got screenshot");
  121. RefPtr<Gfx::Bitmap> bitmap = shared_bitmap.bitmap();
  122. if (!bitmap) {
  123. warnln("Failed to grab screenshot");
  124. return 1;
  125. }
  126. if (output_to_clipboard) {
  127. GUI::Clipboard::the().set_bitmap(*bitmap);
  128. return 0;
  129. }
  130. auto encoded_bitmap_or_error = Gfx::PNGWriter::encode(*bitmap);
  131. if (encoded_bitmap_or_error.is_error()) {
  132. warnln("Failed to encode PNG");
  133. return 1;
  134. }
  135. auto encoded_bitmap = encoded_bitmap_or_error.release_value();
  136. if (edit_image)
  137. output_path = Core::DateTime::now().to_deprecated_string("/tmp/screenshot-%Y-%m-%d-%H-%M-%S.png"sv);
  138. auto file_or_error = Core::Stream::File::open(output_path, Core::Stream::OpenMode::ReadWrite);
  139. if (file_or_error.is_error()) {
  140. warnln("Could not open '{}' for writing: {}", output_path, file_or_error.error());
  141. return 1;
  142. }
  143. auto& file = *file_or_error.value();
  144. TRY(file.write(encoded_bitmap.bytes()));
  145. if (edit_image)
  146. TRY(Core::Process::spawn("/bin/PixelPaint"sv, Array { output_path }));
  147. bool printed_hyperlink = false;
  148. if (isatty(STDOUT_FILENO)) {
  149. auto full_path = Core::File::real_path_for(output_path);
  150. if (!full_path.is_null()) {
  151. char hostname[HOST_NAME_MAX];
  152. VERIFY(gethostname(hostname, sizeof(hostname)) == 0);
  153. auto url = URL::create_with_file_scheme(full_path, {}, hostname);
  154. out("\033]8;;{}\033\\", url.serialize());
  155. printed_hyperlink = true;
  156. }
  157. }
  158. out("{}", output_path);
  159. if (printed_hyperlink) {
  160. out("\033]8;;\033\\");
  161. }
  162. outln("");
  163. return 0;
  164. }