Forráskód Böngészése

LibVT+Terminal: Support hyperlinks in the terminal :^)

We now support basic hyperlinking in the terminal with <OSC>;8;;URL<ST>
Links are opened via LaunchServer on Ctrl+LeftMouse.
Andreas Kling 5 éve
szülő
commit
f596b12a04

+ 1 - 1
Applications/Terminal/Makefile

@@ -3,6 +3,6 @@ OBJS = \
 
 
 PROGRAM = Terminal
 PROGRAM = Terminal
 
 
-LIB_DEPS = GUI Gfx VT IPC Protocol Core
+LIB_DEPS = GUI Gfx VT Desktop IPC Protocol Core
 
 
 include ../../Makefile.common
 include ../../Makefile.common

+ 6 - 1
Applications/Terminal/main.cpp

@@ -190,7 +190,7 @@ int main(int argc, char** argv)
 
 
     GUI::Application app(argc, argv);
     GUI::Application app(argc, argv);
 
 
-    if (pledge("stdio tty rpath accept cpath wpath shared_buffer proc exec", nullptr) < 0) {
+    if (pledge("stdio tty rpath accept cpath wpath shared_buffer proc exec unix", nullptr) < 0) {
         perror("pledge");
         perror("pledge");
         return 1;
         return 1;
     }
     }
@@ -313,6 +313,11 @@ int main(int argc, char** argv)
         return 1;
         return 1;
     }
     }
 
 
+    if (unveil("/tmp/portal/launch", "rw") < 0) {
+        perror("unveil");
+        return 1;
+    }
+
     if (unveil(config->file_name().characters(), "rwc")) {
     if (unveil(config->file_name().characters(), "rwc")) {
         perror("unveil");
         perror("unveil");
         return 1;
         return 1;

+ 1 - 1
DevTools/HackStudio/Makefile

@@ -19,6 +19,6 @@ OBJS = \
 
 
 PROGRAM = HackStudio
 PROGRAM = HackStudio
 
 
-LIB_DEPS = GUI Web TextCodec VT Protocol Markdown Gfx IPC Thread Pthread Core JS Debug
+LIB_DEPS = GUI Web TextCodec VT Desktop Protocol Markdown Gfx IPC Thread Pthread Core JS Debug
 
 
 include ../../Makefile.common
 include ../../Makefile.common

+ 19 - 1
Libraries/LibVT/Terminal.cpp

@@ -61,7 +61,8 @@ void Terminal::Line::set_length(u16 new_length)
     memset(new_characters, ' ', new_length);
     memset(new_characters, ' ', new_length);
     if (characters && attributes) {
     if (characters && attributes) {
         memcpy(new_characters, characters, min(m_length, new_length));
         memcpy(new_characters, characters, min(m_length, new_length));
-        memcpy(new_attributes, attributes, min(m_length, new_length) * sizeof(Attribute));
+        for (size_t i = 0; i < min(m_length, new_length); ++i)
+            new_attributes[i] = attributes[i];
     }
     }
     delete[] characters;
     delete[] characters;
     delete[] attributes;
     delete[] attributes;
@@ -605,6 +606,11 @@ void Terminal::execute_xterm_command()
     case 2:
     case 2:
         m_client.set_window_title(params[1]);
         m_client.set_window_title(params[1]);
         break;
         break;
+    case 8:
+        m_current_attribute.href = params[2];
+        // FIXME: Respect the provided ID
+        m_current_attribute.href_id = String::format("%u", m_next_href_id++);
+        break;
     default:
     default:
         unimplemented_xterm_escape();
         unimplemented_xterm_escape();
         break;
         break;
@@ -1076,4 +1082,16 @@ void Terminal::execute_hashtag(u8 hashtag)
     }
     }
 }
 }
 
 
+Attribute Terminal::attribute_at(const Position& position) const
+{
+    if (!position.is_valid())
+        return {};
+    if (position.row() >= static_cast<int>(m_lines.size()))
+        return {};
+    auto& line = this->line(position.row());
+    if (position.column() >= line.m_length)
+        return {};
+    return line.attributes[position.column()];
+}
+
 }
 }

+ 7 - 0
Libraries/LibVT/Terminal.h

@@ -60,6 +60,9 @@ struct Attribute {
     u8 foreground_color;
     u8 foreground_color;
     u8 background_color;
     u8 background_color;
 
 
+    String href;
+    String href_id;
+
     enum Flags : u8 {
     enum Flags : u8 {
         NoAttributes = 0x00,
         NoAttributes = 0x00,
         Bold = 0x01,
         Bold = 0x01,
@@ -137,6 +140,8 @@ public:
 
 
     void inject_string(const StringView&);
     void inject_string(const StringView&);
 
 
+    Attribute attribute_at(const Position&) const;
+
 private:
 private:
     typedef Vector<unsigned, 4> ParamVector;
     typedef Vector<unsigned, 4> ParamVector;
 
 
@@ -204,6 +209,8 @@ private:
 
 
     Attribute m_current_attribute;
     Attribute m_current_attribute;
 
 
+    u32 m_next_href_id { 0 };
+
     void execute_escape_sequence(u8 final);
     void execute_escape_sequence(u8 final);
     void execute_xterm_command();
     void execute_xterm_command();
     void execute_hashtag(u8);
     void execute_hashtag(u8);

+ 25 - 2
Libraries/LibVT/TerminalWidget.cpp

@@ -32,6 +32,7 @@
 #include <AK/Utf8View.h>
 #include <AK/Utf8View.h>
 #include <Kernel/KeyCode.h>
 #include <Kernel/KeyCode.h>
 #include <LibCore/MimeData.h>
 #include <LibCore/MimeData.h>
+#include <LibDesktop/Launcher.h>
 #include <LibGUI/Action.h>
 #include <LibGUI/Action.h>
 #include <LibGUI/Application.h>
 #include <LibGUI/Application.h>
 #include <LibGUI/Clipboard.h>
 #include <LibGUI/Clipboard.h>
@@ -371,7 +372,11 @@ void TerminalWidget::paint_event(GUI::PaintEvent& event)
                 if (!has_only_one_background_color || should_reverse_fill_for_cursor_or_selection) {
                 if (!has_only_one_background_color || should_reverse_fill_for_cursor_or_selection) {
                     painter.clear_rect(cell_rect, lookup_color(should_reverse_fill_for_cursor_or_selection ? attribute.foreground_color : attribute.background_color).with_alpha(m_opacity));
                     painter.clear_rect(cell_rect, lookup_color(should_reverse_fill_for_cursor_or_selection ? attribute.foreground_color : attribute.background_color).with_alpha(m_opacity));
                 }
                 }
-                if (attribute.flags & VT::Attribute::Underline)
+
+                bool should_paint_underline = attribute.flags & VT::Attribute::Underline
+                    || (!m_hovered_href.is_empty() && m_hovered_href_id == attribute.href_id);
+
+                if (should_paint_underline)
                     painter.draw_line(cell_rect.bottom_left(), cell_rect.bottom_right(), lookup_color(should_reverse_fill_for_cursor_or_selection ? attribute.background_color : attribute.foreground_color));
                     painter.draw_line(cell_rect.bottom_left(), cell_rect.bottom_right(), lookup_color(should_reverse_fill_for_cursor_or_selection ? attribute.background_color : attribute.foreground_color));
             }
             }
 
 
@@ -586,6 +591,15 @@ void TerminalWidget::copy()
 
 
 void TerminalWidget::mousedown_event(GUI::MouseEvent& event)
 void TerminalWidget::mousedown_event(GUI::MouseEvent& event)
 {
 {
+    if (event.modifiers() == Mod_Ctrl && event.button() == GUI::MouseButton::Left) {
+        auto attribute = m_terminal.attribute_at(buffer_position_at(event.position()));
+        if (!attribute.href.is_empty()) {
+            dbg() << "Open URL: _" << attribute.href << "_";
+            Desktop::Launcher::open(attribute.href);
+        }
+        return;
+    }
+
     if (event.button() == GUI::MouseButton::Left) {
     if (event.button() == GUI::MouseButton::Left) {
         if (m_triple_click_timer.is_valid() && m_triple_click_timer.elapsed() < 250) {
         if (m_triple_click_timer.is_valid() && m_triple_click_timer.elapsed() < 250) {
             int start_column = 0;
             int start_column = 0;
@@ -609,11 +623,20 @@ void TerminalWidget::mousedown_event(GUI::MouseEvent& event)
 
 
 void TerminalWidget::mousemove_event(GUI::MouseEvent& event)
 void TerminalWidget::mousemove_event(GUI::MouseEvent& event)
 {
 {
+    auto position = buffer_position_at(event.position());
+
+    auto attribute = m_terminal.attribute_at(position);
+    if (attribute.href_id != m_hovered_href_id) {
+        m_hovered_href_id = attribute.href_id;
+        m_hovered_href = attribute.href;
+        update();
+    }
+
     if (!(event.buttons() & GUI::MouseButton::Left))
     if (!(event.buttons() & GUI::MouseButton::Left))
         return;
         return;
 
 
     auto old_selection_end = m_selection_end;
     auto old_selection_end = m_selection_end;
-    m_selection_end = buffer_position_at(event.position());
+    m_selection_end = position;
     if (old_selection_end != m_selection_end)
     if (old_selection_end != m_selection_end)
         update();
         update();
 }
 }

+ 3 - 0
Libraries/LibVT/TerminalWidget.h

@@ -129,6 +129,9 @@ private:
     VT::Position m_selection_start;
     VT::Position m_selection_start;
     VT::Position m_selection_end;
     VT::Position m_selection_end;
 
 
+    String m_hovered_href;
+    String m_hovered_href_id;
+
     bool m_should_beep { false };
     bool m_should_beep { false };
     bool m_belling { false };
     bool m_belling { false };
     bool m_alt_key_held { false };
     bool m_alt_key_held { false };