shot.cpp 5.4 KB

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