| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  | #include "Terminal.h"
 | 
					
						
							| 
									
										
										
										
											2019-02-03 16:11:28 +01:00
										 |  |  | #include "XtermColors.h"
 | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  | #include <AK/AKString.h>
 | 
					
						
							| 
									
										
										
										
											2019-01-15 10:20:20 +01:00
										 |  |  | #include <AK/StdLibExtras.h>
 | 
					
						
							| 
									
										
										
										
											2019-05-29 10:53:21 -07:00
										 |  |  | #include <AK/StringBuilder.h>
 | 
					
						
							|  |  |  | #include <Kernel/KeyCode.h>
 | 
					
						
							| 
									
										
										
										
											2019-02-11 15:07:05 +01:00
										 |  |  | #include <LibGUI/GApplication.h>
 | 
					
						
							| 
									
										
										
										
											2019-05-29 10:53:21 -07:00
										 |  |  | #include <LibGUI/GPainter.h>
 | 
					
						
							| 
									
										
										
										
											2019-02-10 14:28:39 +01:00
										 |  |  | #include <LibGUI/GWindow.h>
 | 
					
						
							| 
									
										
										
										
											2019-05-29 10:53:21 -07:00
										 |  |  | #include <SharedGraphics/Font.h>
 | 
					
						
							|  |  |  | #include <errno.h>
 | 
					
						
							|  |  |  | #include <stdio.h>
 | 
					
						
							|  |  |  | #include <stdlib.h>
 | 
					
						
							|  |  |  | #include <string.h>
 | 
					
						
							| 
									
										
										
										
											2019-02-20 23:32:33 +01:00
										 |  |  | #include <sys/ioctl.h>
 | 
					
						
							| 
									
										
										
										
											2019-05-29 10:53:21 -07:00
										 |  |  | #include <unistd.h>
 | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-23 08:55:59 +01:00
										 |  |  | //#define TERMINAL_DEBUG
 | 
					
						
							| 
									
										
										
										
											2019-05-29 21:10:08 +02:00
										 |  |  | byte Terminal::Attribute::default_foreground_color = 7; | 
					
						
							|  |  |  | byte Terminal::Attribute::default_background_color = 0; | 
					
						
							| 
									
										
										
										
											2019-01-15 10:20:20 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-25 16:43:15 -07:00
										 |  |  | Terminal::Terminal(int ptm_fd, RetainPtr<CConfigFile> config) | 
					
						
							| 
									
										
										
										
											2019-02-10 14:28:39 +01:00
										 |  |  |     : m_ptm_fd(ptm_fd) | 
					
						
							| 
									
										
										
										
											2019-04-10 17:35:43 +02:00
										 |  |  |     , m_notifier(ptm_fd, CNotifier::Read) | 
					
						
							| 
									
										
										
										
											2019-05-25 16:43:15 -07:00
										 |  |  |     , m_config(config) | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-05-11 02:27:06 +02:00
										 |  |  |     set_frame_shape(FrameShape::Container); | 
					
						
							|  |  |  |     set_frame_shadow(FrameShadow::Sunken); | 
					
						
							|  |  |  |     set_frame_thickness(2); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-25 16:43:15 -07:00
										 |  |  |     dbgprintf("Terminal: Load config file from %s\n", m_config->file_name().characters()); | 
					
						
							|  |  |  |     m_cursor_blink_timer.set_interval(m_config->read_num_entry("Text", | 
					
						
							| 
									
										
										
										
											2019-05-29 10:53:21 -07:00
										 |  |  |         "CursorBlinkInterval", | 
					
						
							|  |  |  |         500)); | 
					
						
							| 
									
										
										
										
											2019-03-30 21:40:27 +01:00
										 |  |  |     m_cursor_blink_timer.on_timeout = [this] { | 
					
						
							|  |  |  |         m_cursor_blink_state = !m_cursor_blink_state; | 
					
						
							|  |  |  |         update_cursor(); | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-25 16:43:15 -07:00
										 |  |  |     auto font_entry = m_config->read_entry("Text", "Font", "default"); | 
					
						
							|  |  |  |     if (font_entry == "default") | 
					
						
							|  |  |  |         set_font(Font::default_fixed_width_font()); | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |         set_font(Font::load_from_file(font_entry)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-29 10:53:21 -07:00
										 |  |  |     m_notifier.on_ready_to_read = [this] { | 
					
						
							| 
									
										
										
										
											2019-02-11 15:07:05 +01:00
										 |  |  |         byte buffer[BUFSIZ]; | 
					
						
							| 
									
										
										
										
											2019-04-10 17:35:43 +02:00
										 |  |  |         ssize_t nread = read(m_ptm_fd, buffer, sizeof(buffer)); | 
					
						
							| 
									
										
										
										
											2019-02-11 15:07:05 +01:00
										 |  |  |         if (nread < 0) { | 
					
						
							|  |  |  |             dbgprintf("Terminal read error: %s\n", strerror(errno)); | 
					
						
							|  |  |  |             perror("read(ptm)"); | 
					
						
							| 
									
										
										
										
											2019-02-17 09:58:35 +01:00
										 |  |  |             GApplication::the().quit(1); | 
					
						
							| 
									
										
										
										
											2019-02-11 15:07:05 +01:00
										 |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (nread == 0) { | 
					
						
							|  |  |  |             dbgprintf("Terminal: EOF on master pty, closing.\n"); | 
					
						
							| 
									
										
										
										
											2019-02-17 09:58:35 +01:00
										 |  |  |             GApplication::the().quit(0); | 
					
						
							| 
									
										
										
										
											2019-02-11 15:07:05 +01:00
										 |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         for (ssize_t i = 0; i < nread; ++i) | 
					
						
							|  |  |  |             on_char(buffer[i]); | 
					
						
							|  |  |  |         flush_dirty_lines(); | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-17 16:19:49 +01:00
										 |  |  |     m_line_height = font().glyph_height() + m_line_spacing; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-25 16:43:15 -07:00
										 |  |  |     set_size(m_config->read_num_entry("Window", "Width", 80), | 
					
						
							| 
									
										
										
										
											2019-05-29 10:53:21 -07:00
										 |  |  |         m_config->read_num_entry("Window", "Height", 25)); | 
					
						
							| 
									
										
										
										
											2019-01-25 01:27:02 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-06 14:59:18 +02:00
										 |  |  | Terminal::Line::Line(word length) | 
					
						
							| 
									
										
										
										
											2019-01-25 01:27:02 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-06-06 14:59:18 +02:00
										 |  |  |     set_length(length); | 
					
						
							| 
									
										
										
										
											2019-01-25 01:27:02 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Terminal::Line::~Line() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-05-29 10:53:21 -07:00
										 |  |  |     delete[] characters; | 
					
						
							|  |  |  |     delete[] attributes; | 
					
						
							| 
									
										
										
										
											2019-01-25 01:27:02 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-06 14:59:18 +02:00
										 |  |  | void Terminal::Line::set_length(word new_length) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (m_length == new_length) | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     auto* new_characters = new byte[new_length]; | 
					
						
							|  |  |  |     auto* new_attributes = new Attribute[new_length]; | 
					
						
							|  |  |  |     memset(new_characters, ' ', new_length); | 
					
						
							|  |  |  |     delete[] characters; | 
					
						
							|  |  |  |     delete[] attributes; | 
					
						
							|  |  |  |     characters = new_characters; | 
					
						
							|  |  |  |     attributes = new_attributes; | 
					
						
							|  |  |  |     m_length = new_length; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-03 16:11:28 +01:00
										 |  |  | void Terminal::Line::clear(Attribute attribute) | 
					
						
							| 
									
										
										
										
											2019-01-25 01:27:02 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-02-04 08:53:53 +01:00
										 |  |  |     if (dirty) { | 
					
						
							| 
									
										
										
										
											2019-06-06 14:59:18 +02:00
										 |  |  |         memset(characters, ' ', m_length); | 
					
						
							|  |  |  |         for (word i = 0; i < m_length; ++i) | 
					
						
							| 
									
										
										
										
											2019-02-04 08:53:53 +01:00
										 |  |  |             attributes[i] = attribute; | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-06-06 14:59:18 +02:00
										 |  |  |     for (unsigned i = 0; i < m_length; ++i) { | 
					
						
							| 
									
										
										
										
											2019-02-04 08:53:53 +01:00
										 |  |  |         if (characters[i] != ' ') | 
					
						
							|  |  |  |             dirty = true; | 
					
						
							|  |  |  |         characters[i] = ' '; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-06-06 14:59:18 +02:00
										 |  |  |     for (unsigned i = 0; i < m_length; ++i) { | 
					
						
							| 
									
										
										
										
											2019-02-04 08:53:53 +01:00
										 |  |  |         if (attributes[i] != attribute) | 
					
						
							|  |  |  |             dirty = true; | 
					
						
							| 
									
										
										
										
											2019-02-03 16:11:28 +01:00
										 |  |  |         attributes[i] = attribute; | 
					
						
							| 
									
										
										
										
											2019-02-04 08:53:53 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Terminal::~Terminal() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Terminal::clear() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-01-25 01:27:02 +01:00
										 |  |  |     for (size_t i = 0; i < rows(); ++i) | 
					
						
							| 
									
										
										
										
											2019-02-03 16:11:28 +01:00
										 |  |  |         line(i).clear(m_current_attribute); | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |     set_cursor(0, 0); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | inline bool is_valid_parameter_character(byte ch) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return ch >= 0x30 && ch <= 0x3f; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | inline bool is_valid_intermediate_character(byte ch) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return ch >= 0x20 && ch <= 0x2f; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | inline bool is_valid_final_character(byte ch) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return ch >= 0x40 && ch <= 0x7e; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-03 16:11:28 +01:00
										 |  |  | static inline Color lookup_color(unsigned color) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-02-19 01:42:53 +01:00
										 |  |  |     return Color::from_rgb(xterm_colors[color]); | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-29 10:53:21 -07:00
										 |  |  | void Terminal::escape$h_l(bool should_set, bool question_param, const ParamVector& params) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int mode = 2; | 
					
						
							|  |  |  |     if (params.size() > 0) { | 
					
						
							|  |  |  |         mode = params[0]; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (!question_param) { | 
					
						
							|  |  |  |         switch (mode) { | 
					
						
							|  |  |  |             // FIXME: implement *something* for this
 | 
					
						
							|  |  |  |         default: | 
					
						
							|  |  |  |             unimplemented_escape(); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         switch (mode) { | 
					
						
							|  |  |  |         case 25: | 
					
						
							|  |  |  |             // Hide cursor command, but doesn't need to be run (for now, because
 | 
					
						
							|  |  |  |             // we don't do inverse control codes anyways)
 | 
					
						
							|  |  |  |             if (should_set) | 
					
						
							|  |  |  |                 dbgprintf("Terminal: Hide Cursor escapecode recieved. Not needed: ignored.\n"); | 
					
						
							|  |  |  |             else | 
					
						
							|  |  |  |                 dbgprintf("Terminal: Show Cursor escapecode recieved. Not needed: ignored.\n"); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         default: | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-20 14:02:19 +02:00
										 |  |  | void Terminal::escape$m(const ParamVector& params) | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-30 01:23:20 +02:00
										 |  |  |     if (params.is_empty()) { | 
					
						
							|  |  |  |         m_current_attribute.reset(); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-02-03 16:11:28 +01:00
										 |  |  |     if (params.size() == 3 && params[1] == 5) { | 
					
						
							|  |  |  |         if (params[0] == 38) { | 
					
						
							|  |  |  |             m_current_attribute.foreground_color = params[2]; | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } else if (params[0] == 48) { | 
					
						
							|  |  |  |             m_current_attribute.background_color = params[2]; | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |     for (auto param : params) { | 
					
						
							|  |  |  |         switch (param) { | 
					
						
							|  |  |  |         case 0: | 
					
						
							|  |  |  |             // Reset
 | 
					
						
							| 
									
										
										
										
											2019-01-15 07:30:24 +01:00
										 |  |  |             m_current_attribute.reset(); | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |             break; | 
					
						
							|  |  |  |         case 1: | 
					
						
							| 
									
										
										
										
											2019-05-29 21:10:08 +02:00
										 |  |  |             m_current_attribute.flags |= Attribute::Bold; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case 3: | 
					
						
							|  |  |  |             m_current_attribute.flags |= Attribute::Italic; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case 4: | 
					
						
							|  |  |  |             m_current_attribute.flags |= Attribute::Underline; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case 5: | 
					
						
							|  |  |  |             m_current_attribute.flags |= Attribute::Blink; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case 7: | 
					
						
							|  |  |  |             m_current_attribute.flags |= Attribute::Negative; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case 22: | 
					
						
							|  |  |  |             m_current_attribute.flags &= ~Attribute::Bold; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case 23: | 
					
						
							|  |  |  |             m_current_attribute.flags &= ~Attribute::Italic; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case 24: | 
					
						
							|  |  |  |             m_current_attribute.flags &= ~Attribute::Underline; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case 25: | 
					
						
							|  |  |  |             m_current_attribute.flags &= ~Attribute::Blink; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case 27: | 
					
						
							|  |  |  |             m_current_attribute.flags &= ~Attribute::Negative; | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |             break; | 
					
						
							|  |  |  |         case 30: | 
					
						
							|  |  |  |         case 31: | 
					
						
							|  |  |  |         case 32: | 
					
						
							|  |  |  |         case 33: | 
					
						
							|  |  |  |         case 34: | 
					
						
							|  |  |  |         case 35: | 
					
						
							|  |  |  |         case 36: | 
					
						
							|  |  |  |         case 37: | 
					
						
							|  |  |  |             // Foreground color
 | 
					
						
							| 
									
										
										
										
											2019-05-29 21:10:08 +02:00
										 |  |  |             if (m_current_attribute.flags & Attribute::Bold) | 
					
						
							|  |  |  |                 param += 8; | 
					
						
							| 
									
										
										
										
											2019-01-15 07:30:24 +01:00
										 |  |  |             m_current_attribute.foreground_color = param - 30; | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |             break; | 
					
						
							| 
									
										
										
										
											2019-05-29 21:10:08 +02:00
										 |  |  |         case 39: | 
					
						
							|  |  |  |             // reset foreground
 | 
					
						
							|  |  |  |             m_current_attribute.foreground_color = Attribute::default_foreground_color; | 
					
						
							|  |  |  |             break; | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |         case 40: | 
					
						
							|  |  |  |         case 41: | 
					
						
							|  |  |  |         case 42: | 
					
						
							|  |  |  |         case 43: | 
					
						
							|  |  |  |         case 44: | 
					
						
							|  |  |  |         case 45: | 
					
						
							|  |  |  |         case 46: | 
					
						
							|  |  |  |         case 47: | 
					
						
							|  |  |  |             // Background color
 | 
					
						
							| 
									
										
										
										
											2019-05-29 21:10:08 +02:00
										 |  |  |             if (m_current_attribute.flags & Attribute::Bold) | 
					
						
							|  |  |  |                 param += 8; | 
					
						
							| 
									
										
										
										
											2019-02-04 10:22:47 +01:00
										 |  |  |             m_current_attribute.background_color = param - 40; | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |             break; | 
					
						
							| 
									
										
										
										
											2019-05-29 21:10:08 +02:00
										 |  |  |         case 49: | 
					
						
							|  |  |  |             // reset background
 | 
					
						
							|  |  |  |             m_current_attribute.background_color = Attribute::default_background_color; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         default: | 
					
						
							|  |  |  |             dbgprintf("FIXME: escape$m: p: %u\n", param); | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-20 14:02:19 +02:00
										 |  |  | void Terminal::escape$s(const ParamVector&) | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     m_saved_cursor_row = m_cursor_row; | 
					
						
							|  |  |  |     m_saved_cursor_column = m_cursor_column; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-20 14:02:19 +02:00
										 |  |  | void Terminal::escape$u(const ParamVector&) | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     set_cursor(m_saved_cursor_row, m_saved_cursor_column); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-20 14:02:19 +02:00
										 |  |  | void Terminal::escape$t(const ParamVector& params) | 
					
						
							| 
									
										
										
										
											2019-02-27 00:02:01 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     if (params.size() < 1) | 
					
						
							|  |  |  |         return; | 
					
						
							| 
									
										
										
										
											2019-05-29 10:53:21 -07:00
										 |  |  |     dbgprintf("FIXME: escape$t: Ps: %u (param count: %d)\n", params[0], params.size()); | 
					
						
							| 
									
										
										
										
											2019-03-14 16:25:19 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-20 14:02:19 +02:00
										 |  |  | void Terminal::escape$r(const ParamVector& params) | 
					
						
							| 
									
										
										
										
											2019-03-14 16:25:19 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     unsigned top = 1; | 
					
						
							|  |  |  |     unsigned bottom = m_rows; | 
					
						
							|  |  |  |     if (params.size() >= 1) | 
					
						
							|  |  |  |         top = params[0]; | 
					
						
							|  |  |  |     if (params.size() >= 2) | 
					
						
							|  |  |  |         bottom = params[1]; | 
					
						
							| 
									
										
										
										
											2019-05-29 10:53:21 -07:00
										 |  |  |     if ((bottom - top) < 2 || bottom > m_rows || top < 0) { | 
					
						
							|  |  |  |         dbgprintf("Error: escape$r: scrolling region invalid: %u-%u\n", top, bottom); | 
					
						
							| 
									
										
										
										
											2019-05-28 15:40:44 -07:00
										 |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-05-29 10:53:21 -07:00
										 |  |  |     m_scroll_region_top = top - 1; | 
					
						
							|  |  |  |     m_scroll_region_bottom = bottom - 1; | 
					
						
							| 
									
										
										
										
											2019-05-28 15:40:44 -07:00
										 |  |  |     set_cursor(0, 0); | 
					
						
							| 
									
										
										
										
											2019-02-27 00:02:01 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-20 14:02:19 +02:00
										 |  |  | void Terminal::escape$H(const ParamVector& params) | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     unsigned row = 1; | 
					
						
							|  |  |  |     unsigned col = 1; | 
					
						
							|  |  |  |     if (params.size() >= 1) | 
					
						
							|  |  |  |         row = params[0]; | 
					
						
							|  |  |  |     if (params.size() >= 2) | 
					
						
							|  |  |  |         col = params[1]; | 
					
						
							|  |  |  |     set_cursor(row - 1, col - 1); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-20 14:02:19 +02:00
										 |  |  | void Terminal::escape$A(const ParamVector& params) | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     int num = 1; | 
					
						
							|  |  |  |     if (params.size() >= 1) | 
					
						
							|  |  |  |         num = params[0]; | 
					
						
							| 
									
										
										
										
											2019-01-23 08:55:59 +01:00
										 |  |  |     if (num == 0) | 
					
						
							|  |  |  |         num = 1; | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |     int new_row = (int)m_cursor_row - num; | 
					
						
							|  |  |  |     if (new_row < 0) | 
					
						
							|  |  |  |         new_row = 0; | 
					
						
							|  |  |  |     set_cursor(new_row, m_cursor_column); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-20 14:02:19 +02:00
										 |  |  | void Terminal::escape$B(const ParamVector& params) | 
					
						
							| 
									
										
										
										
											2019-01-23 08:55:59 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     int num = 1; | 
					
						
							|  |  |  |     if (params.size() >= 1) | 
					
						
							|  |  |  |         num = params[0]; | 
					
						
							|  |  |  |     if (num == 0) | 
					
						
							|  |  |  |         num = 1; | 
					
						
							|  |  |  |     int new_row = (int)m_cursor_row + num; | 
					
						
							|  |  |  |     if (new_row >= m_rows) | 
					
						
							|  |  |  |         new_row = m_rows - 1; | 
					
						
							|  |  |  |     set_cursor(new_row, m_cursor_column); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-20 14:02:19 +02:00
										 |  |  | void Terminal::escape$C(const ParamVector& params) | 
					
						
							| 
									
										
										
										
											2019-01-23 08:55:59 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     int num = 1; | 
					
						
							|  |  |  |     if (params.size() >= 1) | 
					
						
							|  |  |  |         num = params[0]; | 
					
						
							|  |  |  |     if (num == 0) | 
					
						
							|  |  |  |         num = 1; | 
					
						
							|  |  |  |     int new_column = (int)m_cursor_column + num; | 
					
						
							|  |  |  |     if (new_column >= m_columns) | 
					
						
							|  |  |  |         new_column = m_columns - 1; | 
					
						
							|  |  |  |     set_cursor(m_cursor_row, new_column); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-20 14:02:19 +02:00
										 |  |  | void Terminal::escape$D(const ParamVector& params) | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     int num = 1; | 
					
						
							|  |  |  |     if (params.size() >= 1) | 
					
						
							|  |  |  |         num = params[0]; | 
					
						
							| 
									
										
										
										
											2019-01-23 08:55:59 +01:00
										 |  |  |     if (num == 0) | 
					
						
							|  |  |  |         num = 1; | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |     int new_column = (int)m_cursor_column - num; | 
					
						
							|  |  |  |     if (new_column < 0) | 
					
						
							|  |  |  |         new_column = 0; | 
					
						
							|  |  |  |     set_cursor(m_cursor_row, new_column); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-20 14:02:19 +02:00
										 |  |  | void Terminal::escape$G(const ParamVector& params) | 
					
						
							| 
									
										
										
										
											2019-03-14 17:39:50 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     int new_column = 1; | 
					
						
							|  |  |  |     if (params.size() >= 1) | 
					
						
							|  |  |  |         new_column = params[0] - 1; | 
					
						
							|  |  |  |     if (new_column < 0) | 
					
						
							|  |  |  |         new_column = 0; | 
					
						
							|  |  |  |     set_cursor(m_cursor_row, new_column); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-11 07:31:47 +02:00
										 |  |  | void Terminal::escape$b(const ParamVector& params) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (params.size() < 1) | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (unsigned i = 0; i < params[0]; ++i) | 
					
						
							|  |  |  |         put_character_at(m_cursor_row, m_cursor_column++, m_last_char); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-20 14:02:19 +02:00
										 |  |  | void Terminal::escape$d(const ParamVector& params) | 
					
						
							| 
									
										
										
										
											2019-03-14 17:39:50 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     int new_row = 1; | 
					
						
							|  |  |  |     if (params.size() >= 1) | 
					
						
							|  |  |  |         new_row = params[0] - 1; | 
					
						
							|  |  |  |     if (new_row < 0) | 
					
						
							|  |  |  |         new_row = 0; | 
					
						
							|  |  |  |     set_cursor(new_row, m_cursor_column); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-20 14:02:19 +02:00
										 |  |  | void Terminal::escape$X(const ParamVector& params) | 
					
						
							| 
									
										
										
										
											2019-03-14 17:39:50 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     // Erase characters (without moving cursor)
 | 
					
						
							|  |  |  |     int num = 1; | 
					
						
							|  |  |  |     if (params.size() >= 1) | 
					
						
							|  |  |  |         num = params[0]; | 
					
						
							|  |  |  |     if (num == 0) | 
					
						
							|  |  |  |         num = 1; | 
					
						
							|  |  |  |     // Clear from cursor to end of line.
 | 
					
						
							|  |  |  |     for (int i = m_cursor_column; i < num; ++i) { | 
					
						
							|  |  |  |         put_character_at(m_cursor_row, i, ' '); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-20 14:02:19 +02:00
										 |  |  | void Terminal::escape$K(const ParamVector& params) | 
					
						
							| 
									
										
										
										
											2019-01-23 08:55:59 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     int mode = 0; | 
					
						
							|  |  |  |     if (params.size() >= 1) | 
					
						
							|  |  |  |         mode = params[0]; | 
					
						
							|  |  |  |     switch (mode) { | 
					
						
							|  |  |  |     case 0: | 
					
						
							| 
									
										
										
										
											2019-02-04 10:22:47 +01:00
										 |  |  |         // Clear from cursor to end of line.
 | 
					
						
							| 
									
										
										
										
											2019-01-23 08:55:59 +01:00
										 |  |  |         for (int i = m_cursor_column; i < m_columns; ++i) { | 
					
						
							|  |  |  |             put_character_at(m_cursor_row, i, ' '); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 1: | 
					
						
							| 
									
										
										
										
											2019-03-14 18:33:21 +01:00
										 |  |  |         // Clear from cursor to beginning of line.
 | 
					
						
							|  |  |  |         for (int i = 0; i < m_cursor_column; ++i) { | 
					
						
							|  |  |  |             put_character_at(m_cursor_row, i, ' '); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-01-23 08:55:59 +01:00
										 |  |  |         break; | 
					
						
							|  |  |  |     case 2: | 
					
						
							| 
									
										
										
										
											2019-05-29 10:53:21 -07:00
										 |  |  |         // Clear the complete line
 | 
					
						
							|  |  |  |         for (int i = 0; i < m_columns; ++i) { | 
					
						
							|  |  |  |             put_character_at(m_cursor_row, i, ' '); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-01-23 08:55:59 +01:00
										 |  |  |         break; | 
					
						
							|  |  |  |     default: | 
					
						
							| 
									
										
										
										
											2019-01-30 21:04:49 +01:00
										 |  |  |         unimplemented_escape(); | 
					
						
							| 
									
										
										
										
											2019-01-23 08:55:59 +01:00
										 |  |  |         break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-20 14:02:19 +02:00
										 |  |  | void Terminal::escape$J(const ParamVector& params) | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     int mode = 0; | 
					
						
							|  |  |  |     if (params.size() >= 1) | 
					
						
							|  |  |  |         mode = params[0]; | 
					
						
							|  |  |  |     switch (mode) { | 
					
						
							|  |  |  |     case 0: | 
					
						
							| 
									
										
										
										
											2019-01-30 21:04:49 +01:00
										 |  |  |         // Clear from cursor to end of screen.
 | 
					
						
							| 
									
										
										
										
											2019-05-29 10:53:21 -07:00
										 |  |  |         for (int i = m_cursor_column; i < m_columns; ++i) | 
					
						
							| 
									
										
										
										
											2019-01-30 21:04:49 +01:00
										 |  |  |             put_character_at(m_cursor_row, i, ' '); | 
					
						
							|  |  |  |         for (int row = m_cursor_row + 1; row < m_rows; ++row) { | 
					
						
							|  |  |  |             for (int column = 0; column < m_columns; ++column) { | 
					
						
							|  |  |  |                 put_character_at(row, column, ' '); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |         break; | 
					
						
							|  |  |  |     case 1: | 
					
						
							| 
									
										
										
										
											2019-05-29 10:53:21 -07:00
										 |  |  |         /// Clear from cursor to beginning of screen
 | 
					
						
							|  |  |  |         for (int i = m_cursor_column - 1; i >= 0; --i) | 
					
						
							|  |  |  |             put_character_at(m_cursor_row, i, ' '); | 
					
						
							|  |  |  |         for (int row = m_cursor_row - 1; row >= 0; --row) { | 
					
						
							|  |  |  |             for (int column = 0; column < m_columns; ++column) { | 
					
						
							|  |  |  |                 put_character_at(row, column, ' '); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |         break; | 
					
						
							|  |  |  |     case 2: | 
					
						
							|  |  |  |         clear(); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 3: | 
					
						
							|  |  |  |         // FIXME: <esc>[3J should also clear the scrollback buffer.
 | 
					
						
							|  |  |  |         clear(); | 
					
						
							|  |  |  |         break; | 
					
						
							| 
									
										
										
										
											2019-01-23 08:55:59 +01:00
										 |  |  |     default: | 
					
						
							| 
									
										
										
										
											2019-01-30 21:04:49 +01:00
										 |  |  |         unimplemented_escape(); | 
					
						
							| 
									
										
										
										
											2019-01-23 08:55:59 +01:00
										 |  |  |         break; | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-28 15:40:44 -07:00
										 |  |  | void Terminal::escape$S(const ParamVector& params) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int count = 1; | 
					
						
							|  |  |  |     if (params.size() >= 1) | 
					
						
							|  |  |  |         count = params[0]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (word i = 0; i < count; i++) | 
					
						
							|  |  |  |         scroll_up(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Terminal::escape$T(const ParamVector& params) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int count = 1; | 
					
						
							|  |  |  |     if (params.size() >= 1) | 
					
						
							|  |  |  |         count = params[0]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (word i = 0; i < count; i++) | 
					
						
							|  |  |  |         scroll_down(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Terminal::escape$L(const ParamVector& params) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int count = 1; | 
					
						
							|  |  |  |     if (params.size() >= 1) | 
					
						
							|  |  |  |         count = params[0]; | 
					
						
							|  |  |  |     invalidate_cursor(); | 
					
						
							| 
									
										
										
										
											2019-05-29 10:53:21 -07:00
										 |  |  |     for (; count > 0; --count) { | 
					
						
							|  |  |  |         m_lines.insert(m_cursor_row + m_scroll_region_top, make<Line>(m_columns)); | 
					
						
							|  |  |  |         if (m_scroll_region_bottom + 1 < m_lines.size()) | 
					
						
							|  |  |  |             m_lines.remove(m_scroll_region_bottom + 1); | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |             m_lines.remove(m_lines.size() - 1); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-05-28 15:40:44 -07:00
										 |  |  |     m_need_full_flush = true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-20 14:02:19 +02:00
										 |  |  | void Terminal::escape$M(const ParamVector& params) | 
					
						
							| 
									
										
										
										
											2019-02-03 14:00:48 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     int count = 1; | 
					
						
							|  |  |  |     if (params.size() >= 1) | 
					
						
							|  |  |  |         count = params[0]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (count == 1 && m_cursor_row == 0) { | 
					
						
							|  |  |  |         scroll_up(); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-29 10:53:21 -07:00
										 |  |  |     int max_count = m_rows - (m_scroll_region_top + m_cursor_row); | 
					
						
							| 
									
										
										
										
											2019-02-03 14:00:48 +01:00
										 |  |  |     count = min(count, max_count); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-29 10:53:21 -07:00
										 |  |  |     for (int c = count; c > 0; --c) { | 
					
						
							|  |  |  |         m_lines.remove(m_cursor_row + m_scroll_region_top); | 
					
						
							|  |  |  |         if (m_scroll_region_bottom < m_lines.size()) | 
					
						
							|  |  |  |             m_lines.insert(m_scroll_region_bottom, make<Line>(m_columns)); | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |             m_lines.append(make<Line>(m_columns)); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-02-03 14:00:48 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-12 20:53:50 +02:00
										 |  |  | void Terminal::escape$P(const ParamVector& params) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int num = 1; | 
					
						
							|  |  |  |     if (params.size() >= 1) | 
					
						
							|  |  |  |         num = params[0]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (num == 0) | 
					
						
							|  |  |  |         num = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto& line = this->line(m_cursor_row); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Move n characters of line to the left
 | 
					
						
							|  |  |  |     for (int i = m_cursor_column; i < line.m_length - num; i++) | 
					
						
							|  |  |  |         line.characters[i] = line.characters[i + num]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Fill remainder of line with blanks
 | 
					
						
							|  |  |  |     for (int i = line.m_length - num; i < line.m_length; i++) | 
					
						
							|  |  |  |         line.characters[i] = ' '; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     line.dirty = true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-25 05:51:39 +01:00
										 |  |  | void Terminal::execute_xterm_command() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-01-30 21:04:49 +01:00
										 |  |  |     m_final = '@'; | 
					
						
							| 
									
										
										
										
											2019-01-25 05:51:39 +01:00
										 |  |  |     bool ok; | 
					
						
							| 
									
										
										
										
											2019-05-08 19:21:51 +02:00
										 |  |  |     unsigned value = String::copy(m_xterm_param1).to_uint(ok); | 
					
						
							| 
									
										
										
										
											2019-01-25 05:51:39 +01:00
										 |  |  |     if (ok) { | 
					
						
							|  |  |  |         switch (value) { | 
					
						
							|  |  |  |         case 0: | 
					
						
							| 
									
										
										
										
											2019-02-03 16:11:28 +01:00
										 |  |  |         case 1: | 
					
						
							|  |  |  |         case 2: | 
					
						
							| 
									
										
										
										
											2019-04-20 14:13:40 +02:00
										 |  |  |             set_window_title(String::copy(m_xterm_param2)); | 
					
						
							| 
									
										
										
										
											2019-01-25 05:51:39 +01:00
										 |  |  |             break; | 
					
						
							|  |  |  |         default: | 
					
						
							| 
									
										
										
										
											2019-01-30 21:04:49 +01:00
										 |  |  |             unimplemented_xterm_escape(); | 
					
						
							| 
									
										
										
										
											2019-01-25 05:51:39 +01:00
										 |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     m_xterm_param1.clear_with_capacity(); | 
					
						
							|  |  |  |     m_xterm_param2.clear_with_capacity(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  | void Terminal::execute_escape_sequence(byte final) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-05-29 10:53:21 -07:00
										 |  |  |     bool question_param = false; | 
					
						
							| 
									
										
										
										
											2019-01-30 21:04:49 +01:00
										 |  |  |     m_final = final; | 
					
						
							| 
									
										
										
										
											2019-04-20 14:02:19 +02:00
										 |  |  |     ParamVector params; | 
					
						
							| 
									
										
										
										
											2019-05-29 10:53:21 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (m_parameters.size() > 0 && m_parameters[0] == '?') { | 
					
						
							|  |  |  |         question_param = true; | 
					
						
							|  |  |  |         m_parameters.remove(0); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     auto paramparts = String::copy(m_parameters).split(';'); | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |     for (auto& parampart : paramparts) { | 
					
						
							|  |  |  |         bool ok; | 
					
						
							| 
									
										
										
										
											2019-05-08 19:21:51 +02:00
										 |  |  |         unsigned value = parampart.to_uint(ok); | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |         if (!ok) { | 
					
						
							| 
									
										
										
										
											2019-05-29 10:53:21 -07:00
										 |  |  |             // FIXME: Should we do something else?
 | 
					
						
							| 
									
										
										
										
											2019-02-03 16:11:28 +01:00
										 |  |  |             m_parameters.clear_with_capacity(); | 
					
						
							|  |  |  |             m_intermediates.clear_with_capacity(); | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         params.append(value); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-05-29 10:53:21 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | #if defined(TERMINAL_DEBUG)
 | 
					
						
							|  |  |  |     dbgprintf("Terminal::execute_escape_sequence: Handled final '%c'\n", final); | 
					
						
							|  |  |  |     dbgprintf("Params: "); | 
					
						
							|  |  |  |     for (auto& p : params) { | 
					
						
							|  |  |  |         dbgprintf("%d ", p); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     dbgprintf("\b\n"); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |     switch (final) { | 
					
						
							| 
									
										
										
										
											2019-05-29 10:53:21 -07:00
										 |  |  |     case 'A': | 
					
						
							|  |  |  |         escape$A(params); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 'B': | 
					
						
							|  |  |  |         escape$B(params); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 'C': | 
					
						
							|  |  |  |         escape$C(params); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 'D': | 
					
						
							|  |  |  |         escape$D(params); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 'H': | 
					
						
							|  |  |  |         escape$H(params); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 'J': | 
					
						
							|  |  |  |         escape$J(params); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 'K': | 
					
						
							|  |  |  |         escape$K(params); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 'M': | 
					
						
							|  |  |  |         escape$M(params); | 
					
						
							|  |  |  |         break; | 
					
						
							| 
									
										
										
										
											2019-06-12 20:53:50 +02:00
										 |  |  |     case 'P': | 
					
						
							|  |  |  |         escape$P(params); | 
					
						
							|  |  |  |         break; | 
					
						
							| 
									
										
										
										
											2019-05-29 10:53:21 -07:00
										 |  |  |     case 'S': | 
					
						
							|  |  |  |         escape$S(params); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 'T': | 
					
						
							|  |  |  |         escape$T(params); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 'L': | 
					
						
							|  |  |  |         escape$L(params); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 'G': | 
					
						
							|  |  |  |         escape$G(params); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 'X': | 
					
						
							|  |  |  |         escape$X(params); | 
					
						
							|  |  |  |         break; | 
					
						
							| 
									
										
										
										
											2019-06-11 07:31:47 +02:00
										 |  |  |     case 'b': | 
					
						
							|  |  |  |         escape$b(params); | 
					
						
							|  |  |  |         break; | 
					
						
							| 
									
										
										
										
											2019-05-29 10:53:21 -07:00
										 |  |  |     case 'd': | 
					
						
							|  |  |  |         escape$d(params); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 'm': | 
					
						
							|  |  |  |         escape$m(params); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 's': | 
					
						
							|  |  |  |         escape$s(params); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 'u': | 
					
						
							|  |  |  |         escape$u(params); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 't': | 
					
						
							|  |  |  |         escape$t(params); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 'r': | 
					
						
							|  |  |  |         escape$r(params); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 'l': | 
					
						
							|  |  |  |         escape$h_l(true, question_param, params); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 'h': | 
					
						
							|  |  |  |         escape$h_l(false, question_param, params); | 
					
						
							|  |  |  |         break; | 
					
						
							| 
									
										
										
										
											2019-01-23 08:55:59 +01:00
										 |  |  |     default: | 
					
						
							|  |  |  |         dbgprintf("Terminal::execute_escape_sequence: Unhandled final '%c'\n", final); | 
					
						
							|  |  |  |         break; | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-29 10:53:21 -07:00
										 |  |  | #if defined(TERMINAL_DEBUG)
 | 
					
						
							|  |  |  |     dbgprintf("\n"); | 
					
						
							|  |  |  |     for (auto& line : m_lines) { | 
					
						
							|  |  |  |         dbgprintf("Terminal: Line: "); | 
					
						
							|  |  |  |         for (int i = 0; i < line->length; i++) { | 
					
						
							|  |  |  |             dbgprintf("%c", line->characters[i]); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         dbgprintf("\n"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-03 16:11:28 +01:00
										 |  |  |     m_parameters.clear_with_capacity(); | 
					
						
							|  |  |  |     m_intermediates.clear_with_capacity(); | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-03 14:00:48 +01:00
										 |  |  | void Terminal::newline() | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-01-17 16:19:49 +01:00
										 |  |  |     word new_row = m_cursor_row; | 
					
						
							| 
									
										
										
										
											2019-05-29 10:53:21 -07:00
										 |  |  |     if (m_cursor_row == m_scroll_region_bottom) { | 
					
						
							| 
									
										
										
										
											2019-02-03 14:00:48 +01:00
										 |  |  |         scroll_up(); | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2019-01-17 16:19:49 +01:00
										 |  |  |         ++new_row; | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-01-17 16:19:49 +01:00
										 |  |  |     set_cursor(new_row, 0); | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-03 14:00:48 +01:00
										 |  |  | void Terminal::scroll_up() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // NOTE: We have to invalidate the cursor first.
 | 
					
						
							|  |  |  |     invalidate_cursor(); | 
					
						
							| 
									
										
										
										
											2019-05-29 10:53:21 -07:00
										 |  |  |     m_lines.remove(m_scroll_region_top); | 
					
						
							|  |  |  |     m_lines.insert(m_scroll_region_bottom, make<Line>(m_columns)); | 
					
						
							| 
									
										
										
										
											2019-02-10 14:47:09 +01:00
										 |  |  |     m_need_full_flush = true; | 
					
						
							| 
									
										
										
										
											2019-02-03 14:00:48 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-28 15:40:44 -07:00
										 |  |  | void Terminal::scroll_down() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // NOTE: We have to invalidate the cursor first.
 | 
					
						
							|  |  |  |     invalidate_cursor(); | 
					
						
							| 
									
										
										
										
											2019-05-29 10:53:21 -07:00
										 |  |  |     m_lines.remove(m_scroll_region_bottom); | 
					
						
							|  |  |  |     m_lines.insert(m_scroll_region_top, make<Line>(m_columns)); | 
					
						
							| 
									
										
										
										
											2019-05-28 15:40:44 -07:00
										 |  |  |     m_need_full_flush = true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-03 04:04:42 +01:00
										 |  |  | void Terminal::set_cursor(unsigned a_row, unsigned a_column) | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-02-03 04:04:42 +01:00
										 |  |  |     unsigned row = min(a_row, m_rows - 1u); | 
					
						
							|  |  |  |     unsigned column = min(a_column, m_columns - 1u); | 
					
						
							| 
									
										
										
										
											2019-01-23 08:55:59 +01:00
										 |  |  |     if (row == m_cursor_row && column == m_cursor_column) | 
					
						
							|  |  |  |         return; | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |     ASSERT(row < rows()); | 
					
						
							|  |  |  |     ASSERT(column < columns()); | 
					
						
							| 
									
										
										
										
											2019-01-17 17:38:04 +01:00
										 |  |  |     invalidate_cursor(); | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |     m_cursor_row = row; | 
					
						
							|  |  |  |     m_cursor_column = column; | 
					
						
							| 
									
										
										
										
											2019-05-29 10:53:21 -07:00
										 |  |  |     if (column != columns() - 1u) | 
					
						
							| 
									
										
										
										
											2019-01-23 19:58:45 +01:00
										 |  |  |         m_stomp = false; | 
					
						
							| 
									
										
										
										
											2019-01-17 17:38:04 +01:00
										 |  |  |     invalidate_cursor(); | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Terminal::put_character_at(unsigned row, unsigned column, byte ch) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     ASSERT(row < rows()); | 
					
						
							|  |  |  |     ASSERT(column < columns()); | 
					
						
							| 
									
										
										
										
											2019-02-03 14:00:48 +01:00
										 |  |  |     auto& line = this->line(row); | 
					
						
							|  |  |  |     line.characters[column] = ch; | 
					
						
							|  |  |  |     line.attributes[column] = m_current_attribute; | 
					
						
							|  |  |  |     line.dirty = true; | 
					
						
							| 
									
										
										
										
											2019-06-11 07:31:47 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     m_last_char = ch; | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Terminal::on_char(byte ch) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-01-23 08:55:59 +01:00
										 |  |  | #ifdef TERMINAL_DEBUG
 | 
					
						
							| 
									
										
										
										
											2019-02-03 16:11:28 +01:00
										 |  |  |     dbgprintf("Terminal::on_char: %b (%c), fg=%u, bg=%u\n", ch, ch, m_current_attribute.foreground_color, m_current_attribute.background_color); | 
					
						
							| 
									
										
										
										
											2019-01-23 08:55:59 +01:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |     switch (m_escape_state) { | 
					
						
							|  |  |  |     case ExpectBracket: | 
					
						
							|  |  |  |         if (ch == '[') | 
					
						
							|  |  |  |             m_escape_state = ExpectParameter; | 
					
						
							| 
									
										
										
										
											2019-03-14 18:33:21 +01:00
										 |  |  |         else if (ch == '(') { | 
					
						
							|  |  |  |             m_swallow_current = true; | 
					
						
							|  |  |  |             m_escape_state = ExpectParameter; | 
					
						
							|  |  |  |         } else if (ch == ']') | 
					
						
							| 
									
										
										
										
											2019-01-25 05:51:39 +01:00
										 |  |  |             m_escape_state = ExpectXtermParameter1; | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |         else | 
					
						
							|  |  |  |             m_escape_state = Normal; | 
					
						
							|  |  |  |         return; | 
					
						
							| 
									
										
										
										
											2019-01-25 05:51:39 +01:00
										 |  |  |     case ExpectXtermParameter1: | 
					
						
							|  |  |  |         if (ch != ';') { | 
					
						
							|  |  |  |             m_xterm_param1.append(ch); | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         m_escape_state = ExpectXtermParameter2; | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     case ExpectXtermParameter2: | 
					
						
							|  |  |  |         if (ch != '\007') { | 
					
						
							|  |  |  |             m_xterm_param2.append(ch); | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         m_escape_state = ExpectXtermFinal; | 
					
						
							| 
									
										
										
										
											2019-02-15 12:30:48 +01:00
										 |  |  |         [[fallthrough]]; | 
					
						
							| 
									
										
										
										
											2019-01-25 05:51:39 +01:00
										 |  |  |     case ExpectXtermFinal: | 
					
						
							|  |  |  |         m_escape_state = Normal; | 
					
						
							|  |  |  |         if (ch == '\007') | 
					
						
							|  |  |  |             execute_xterm_command(); | 
					
						
							|  |  |  |         return; | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |     case ExpectParameter: | 
					
						
							|  |  |  |         if (is_valid_parameter_character(ch)) { | 
					
						
							|  |  |  |             m_parameters.append(ch); | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         m_escape_state = ExpectIntermediate; | 
					
						
							| 
									
										
										
										
											2019-02-15 12:30:48 +01:00
										 |  |  |         [[fallthrough]]; | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |     case ExpectIntermediate: | 
					
						
							|  |  |  |         if (is_valid_intermediate_character(ch)) { | 
					
						
							|  |  |  |             m_intermediates.append(ch); | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         m_escape_state = ExpectFinal; | 
					
						
							| 
									
										
										
										
											2019-02-15 12:30:48 +01:00
										 |  |  |         [[fallthrough]]; | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |     case ExpectFinal: | 
					
						
							|  |  |  |         if (is_valid_final_character(ch)) { | 
					
						
							|  |  |  |             m_escape_state = Normal; | 
					
						
							| 
									
										
										
										
											2019-03-14 18:33:21 +01:00
										 |  |  |             if (!m_swallow_current) | 
					
						
							|  |  |  |                 execute_escape_sequence(ch); | 
					
						
							|  |  |  |             m_swallow_current = false; | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         m_escape_state = Normal; | 
					
						
							| 
									
										
										
										
											2019-03-14 18:33:21 +01:00
										 |  |  |         m_swallow_current = false; | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |         return; | 
					
						
							|  |  |  |     case Normal: | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     switch (ch) { | 
					
						
							|  |  |  |     case '\0': | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     case '\033': | 
					
						
							|  |  |  |         m_escape_state = ExpectBracket; | 
					
						
							| 
									
										
										
										
											2019-03-14 18:33:21 +01:00
										 |  |  |         m_swallow_current = false; | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |         return; | 
					
						
							|  |  |  |     case 8: // Backspace
 | 
					
						
							|  |  |  |         if (m_cursor_column) { | 
					
						
							|  |  |  |             set_cursor(m_cursor_row, m_cursor_column - 1); | 
					
						
							|  |  |  |             put_character_at(m_cursor_row, m_cursor_column, ' '); | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-01-23 19:58:45 +01:00
										 |  |  |         return; | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |     case '\a': | 
					
						
							| 
									
										
										
										
											2019-05-31 12:43:58 -07:00
										 |  |  |         if (m_should_beep) | 
					
						
							|  |  |  |             sysbeep(); | 
					
						
							| 
									
										
										
										
											2019-05-31 14:51:06 -07:00
										 |  |  |         else { | 
					
						
							| 
									
										
										
										
											2019-06-01 00:36:56 +02:00
										 |  |  |             m_visual_beep_timer.restart(200); | 
					
						
							| 
									
										
										
										
											2019-05-31 14:51:06 -07:00
										 |  |  |             m_visual_beep_timer.set_single_shot(true); | 
					
						
							|  |  |  |             m_visual_beep_timer.on_timeout = [this] { | 
					
						
							| 
									
										
										
										
											2019-05-29 10:53:21 -07:00
										 |  |  |                 force_repaint(); | 
					
						
							|  |  |  |             }; | 
					
						
							| 
									
										
										
										
											2019-06-01 00:36:56 +02:00
										 |  |  |             force_repaint(); | 
					
						
							| 
									
										
										
										
											2019-05-31 14:51:06 -07:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |         return; | 
					
						
							|  |  |  |     case '\t': { | 
					
						
							|  |  |  |         for (unsigned i = m_cursor_column; i < columns(); ++i) { | 
					
						
							|  |  |  |             if (m_horizontal_tabs[i]) { | 
					
						
							|  |  |  |                 set_cursor(m_cursor_row, i); | 
					
						
							|  |  |  |                 return; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-01-23 08:55:59 +01:00
										 |  |  |     case '\r': | 
					
						
							|  |  |  |         set_cursor(m_cursor_row, 0); | 
					
						
							|  |  |  |         return; | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |     case '\n': | 
					
						
							| 
									
										
										
										
											2019-02-03 14:00:48 +01:00
										 |  |  |         newline(); | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-17 16:19:49 +01:00
										 |  |  |     auto new_column = m_cursor_column + 1; | 
					
						
							| 
									
										
										
										
											2019-01-23 19:58:45 +01:00
										 |  |  |     if (new_column < columns()) { | 
					
						
							|  |  |  |         put_character_at(m_cursor_row, m_cursor_column, ch); | 
					
						
							| 
									
										
										
										
											2019-01-17 16:19:49 +01:00
										 |  |  |         set_cursor(m_cursor_row, new_column); | 
					
						
							| 
									
										
										
										
											2019-01-23 19:58:45 +01:00
										 |  |  |     } else { | 
					
						
							|  |  |  |         if (m_stomp) { | 
					
						
							|  |  |  |             m_stomp = false; | 
					
						
							| 
									
										
										
										
											2019-02-03 14:00:48 +01:00
										 |  |  |             newline(); | 
					
						
							| 
									
										
										
										
											2019-01-23 19:58:45 +01:00
										 |  |  |             put_character_at(m_cursor_row, m_cursor_column, ch); | 
					
						
							|  |  |  |             set_cursor(m_cursor_row, 1); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             // Curious: We wait once on the right-hand side
 | 
					
						
							|  |  |  |             m_stomp = true; | 
					
						
							|  |  |  |             put_character_at(m_cursor_row, m_cursor_column, ch); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-30 21:04:49 +01:00
										 |  |  | void Terminal::inject_string(const String& str) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-02-25 22:06:55 +01:00
										 |  |  |     for (int i = 0; i < str.length(); ++i) | 
					
						
							| 
									
										
										
										
											2019-01-30 21:04:49 +01:00
										 |  |  |         on_char(str[i]); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Terminal::unimplemented_escape() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     StringBuilder builder; | 
					
						
							|  |  |  |     builder.appendf("((Unimplemented escape: %c", m_final); | 
					
						
							|  |  |  |     if (!m_parameters.is_empty()) { | 
					
						
							|  |  |  |         builder.append(" parameters:"); | 
					
						
							| 
									
										
										
										
											2019-02-25 22:06:55 +01:00
										 |  |  |         for (int i = 0; i < m_parameters.size(); ++i) | 
					
						
							| 
									
										
										
										
											2019-01-30 21:04:49 +01:00
										 |  |  |             builder.append((char)m_parameters[i]); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (!m_intermediates.is_empty()) { | 
					
						
							|  |  |  |         builder.append(" intermediates:"); | 
					
						
							| 
									
										
										
										
											2019-02-25 22:06:55 +01:00
										 |  |  |         for (int i = 0; i < m_intermediates.size(); ++i) | 
					
						
							| 
									
										
										
										
											2019-01-30 21:04:49 +01:00
										 |  |  |             builder.append((char)m_intermediates[i]); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     builder.append("))"); | 
					
						
							| 
									
										
										
										
											2019-01-31 17:31:23 +01:00
										 |  |  |     inject_string(builder.to_string()); | 
					
						
							| 
									
										
										
										
											2019-01-30 21:04:49 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Terminal::unimplemented_xterm_escape() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     auto message = String::format("((Unimplemented xterm escape: %c))\n", m_final); | 
					
						
							|  |  |  |     inject_string(message); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  | void Terminal::set_size(word columns, word rows) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-02-20 23:32:33 +01:00
										 |  |  |     if (columns == m_columns && rows == m_rows) | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-29 10:53:21 -07:00
										 |  |  | #if defined(TERMINAL_DEBUG)
 | 
					
						
							|  |  |  |     dbgprintf("Terminal: RESIZE to: %d rows\n", rows); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (rows > m_rows) { | 
					
						
							|  |  |  |         while (m_lines.size() < rows) | 
					
						
							|  |  |  |             m_lines.append(make<Line>(columns)); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         m_lines.resize(rows); | 
					
						
							| 
									
										
										
										
											2019-02-20 23:32:33 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-06 14:59:18 +02:00
										 |  |  |     for (int i = 0; i < rows; ++i) | 
					
						
							|  |  |  |         m_lines[i]->set_length(columns); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |     m_columns = columns; | 
					
						
							|  |  |  |     m_rows = rows; | 
					
						
							| 
									
										
										
										
											2019-02-20 23:32:33 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-28 15:40:44 -07:00
										 |  |  |     m_scroll_region_top = 0; | 
					
						
							| 
									
										
										
										
											2019-05-29 10:53:21 -07:00
										 |  |  |     m_scroll_region_bottom = rows - 1; | 
					
						
							| 
									
										
										
										
											2019-05-28 15:40:44 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-20 23:32:33 +01:00
										 |  |  |     m_cursor_row = 0; | 
					
						
							|  |  |  |     m_cursor_column = 0; | 
					
						
							|  |  |  |     m_saved_cursor_row = 0; | 
					
						
							|  |  |  |     m_saved_cursor_column = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-06 11:42:05 +02:00
										 |  |  |     m_horizontal_tabs.resize(columns); | 
					
						
							| 
									
										
										
										
											2019-02-20 23:32:33 +01:00
										 |  |  |     for (unsigned i = 0; i < columns; ++i) | 
					
						
							|  |  |  |         m_horizontal_tabs[i] = (i % 8) == 0; | 
					
						
							|  |  |  |     // Rightmost column is always last tab on line.
 | 
					
						
							|  |  |  |     m_horizontal_tabs[columns - 1] = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-11 02:27:06 +02:00
										 |  |  |     m_pixel_width = (frame_thickness() * 2) + (m_inset * 2) + (m_columns * font().glyph_width('x')); | 
					
						
							|  |  |  |     m_pixel_height = (frame_thickness() * 2) + (m_inset * 2) + (m_rows * (font().glyph_height() + m_line_spacing)) - m_line_spacing; | 
					
						
							| 
									
										
										
										
											2019-02-20 23:32:33 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     set_size_policy(SizePolicy::Fixed, SizePolicy::Fixed); | 
					
						
							|  |  |  |     set_preferred_size({ m_pixel_width, m_pixel_height }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     m_needs_background_fill = true; | 
					
						
							|  |  |  |     force_repaint(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     winsize ws; | 
					
						
							|  |  |  |     ws.ws_row = rows; | 
					
						
							|  |  |  |     ws.ws_col = columns; | 
					
						
							|  |  |  |     int rc = ioctl(m_ptm_fd, TIOCSWINSZ, &ws); | 
					
						
							|  |  |  |     ASSERT(rc == 0); | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-17 16:19:49 +01:00
										 |  |  | Rect Terminal::glyph_rect(word row, word column) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int y = row * m_line_height; | 
					
						
							| 
									
										
										
										
											2019-03-06 11:03:10 +01:00
										 |  |  |     int x = column * font().glyph_width('x'); | 
					
						
							| 
									
										
										
										
											2019-05-11 02:27:06 +02:00
										 |  |  |     return { x + frame_thickness() + m_inset, y + frame_thickness() + m_inset, font().glyph_width('x'), font().glyph_height() }; | 
					
						
							| 
									
										
										
										
											2019-01-17 16:19:49 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-18 04:37:49 +01:00
										 |  |  | Rect Terminal::row_rect(word row) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int y = row * m_line_height; | 
					
						
							| 
									
										
										
										
											2019-05-11 02:27:06 +02:00
										 |  |  |     Rect rect = { frame_thickness() + m_inset, y + frame_thickness() + m_inset, font().glyph_width('x') * m_columns, font().glyph_height() }; | 
					
						
							| 
									
										
										
										
											2019-02-03 16:11:28 +01:00
										 |  |  |     rect.inflate(0, m_line_spacing); | 
					
						
							|  |  |  |     return rect; | 
					
						
							| 
									
										
										
										
											2019-01-17 16:19:49 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-25 02:09:29 +01:00
										 |  |  | bool Terminal::Line::has_only_one_background_color() const | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-06-06 14:59:18 +02:00
										 |  |  |     if (!m_length) | 
					
						
							| 
									
										
										
										
											2019-01-25 02:09:29 +01:00
										 |  |  |         return true; | 
					
						
							|  |  |  |     // FIXME: Cache this result?
 | 
					
						
							|  |  |  |     auto color = attributes[0].background_color; | 
					
						
							| 
									
										
										
										
											2019-06-06 14:59:18 +02:00
										 |  |  |     for (size_t i = 1; i < m_length; ++i) { | 
					
						
							| 
									
										
										
										
											2019-01-25 02:09:29 +01:00
										 |  |  |         if (attributes[i].background_color != color) | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-10 16:56:55 +02:00
										 |  |  | void Terminal::event(CEvent& event) | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-02-10 14:28:39 +01:00
										 |  |  |     if (event.type() == GEvent::WindowBecameActive || event.type() == GEvent::WindowBecameInactive) { | 
					
						
							|  |  |  |         m_in_active_window = event.type() == GEvent::WindowBecameActive; | 
					
						
							| 
									
										
										
										
											2019-03-30 21:40:27 +01:00
										 |  |  |         if (!m_in_active_window) { | 
					
						
							|  |  |  |             m_cursor_blink_timer.stop(); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             m_cursor_blink_state = true; | 
					
						
							|  |  |  |             m_cursor_blink_timer.start(); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-02-10 14:28:39 +01:00
										 |  |  |         invalidate_cursor(); | 
					
						
							|  |  |  |         update(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return GWidget::event(event); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-01-18 04:37:49 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-10 14:28:39 +01:00
										 |  |  | void Terminal::keydown_event(GKeyEvent& event) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     char ch = !event.text().is_empty() ? event.text()[0] : 0; | 
					
						
							|  |  |  |     if (event.ctrl()) { | 
					
						
							|  |  |  |         if (ch >= 'a' && ch <= 'z') { | 
					
						
							|  |  |  |             ch = ch - 'a' + 1; | 
					
						
							|  |  |  |         } else if (ch == '\\') { | 
					
						
							|  |  |  |             ch = 0x1c; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     switch (event.key()) { | 
					
						
							|  |  |  |     case KeyCode::Key_Up: | 
					
						
							|  |  |  |         write(m_ptm_fd, "\033[A", 3); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case KeyCode::Key_Down: | 
					
						
							|  |  |  |         write(m_ptm_fd, "\033[B", 3); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case KeyCode::Key_Right: | 
					
						
							|  |  |  |         write(m_ptm_fd, "\033[C", 3); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case KeyCode::Key_Left: | 
					
						
							|  |  |  |         write(m_ptm_fd, "\033[D", 3); | 
					
						
							|  |  |  |         break; | 
					
						
							| 
									
										
										
										
											2019-05-07 05:03:21 +02:00
										 |  |  |     case KeyCode::Key_Home: | 
					
						
							|  |  |  |         write(m_ptm_fd, "\033[H", 3); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case KeyCode::Key_End: | 
					
						
							|  |  |  |         write(m_ptm_fd, "\033[F", 3); | 
					
						
							|  |  |  |         break; | 
					
						
							| 
									
										
										
										
											2019-05-28 15:40:44 -07:00
										 |  |  |     case KeyCode::Key_RightShift: | 
					
						
							| 
									
										
										
										
											2019-05-29 10:53:21 -07:00
										 |  |  |         // Prevent RightShift from being sent to whatever's running in the
 | 
					
						
							|  |  |  |         // terminal. Prevents `~@` (null) character from being sent after every
 | 
					
						
							|  |  |  |         // character entered with right shift.
 | 
					
						
							| 
									
										
										
										
											2019-05-28 15:40:44 -07:00
										 |  |  |         break; | 
					
						
							| 
									
										
										
										
											2019-02-10 14:28:39 +01:00
										 |  |  |     default: | 
					
						
							|  |  |  |         write(m_ptm_fd, &ch, 1); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-11 02:27:06 +02:00
										 |  |  | void Terminal::paint_event(GPaintEvent& event) | 
					
						
							| 
									
										
										
										
											2019-05-31 12:43:58 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-05-11 02:27:06 +02:00
										 |  |  |     GFrame::paint_event(event); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-28 17:19:56 +01:00
										 |  |  |     GPainter painter(*this); | 
					
						
							| 
									
										
										
										
											2019-01-18 04:37:49 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-29 10:53:21 -07:00
										 |  |  |     if (m_visual_beep_timer.is_active()) | 
					
						
							|  |  |  |         painter.fill_rect(frame_inner_rect(), Color::Red); | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |         painter.fill_rect(frame_inner_rect(), Color(Color::Black).with_alpha(255 * m_opacity)); | 
					
						
							| 
									
										
										
										
											2019-02-03 14:00:48 +01:00
										 |  |  |     invalidate_cursor(); | 
					
						
							| 
									
										
										
										
											2019-02-01 05:40:27 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-15 04:44:47 +01:00
										 |  |  |     for (word row = 0; row < m_rows; ++row) { | 
					
						
							| 
									
										
										
										
											2019-01-25 02:09:29 +01:00
										 |  |  |         auto& line = this->line(row); | 
					
						
							|  |  |  |         bool has_only_one_background_color = line.has_only_one_background_color(); | 
					
						
							| 
									
										
										
										
											2019-05-31 14:51:06 -07:00
										 |  |  |         if (m_visual_beep_timer.is_active()) | 
					
						
							| 
									
										
										
										
											2019-05-31 12:43:58 -07:00
										 |  |  |             painter.fill_rect(row_rect(row), Color::Red); | 
					
						
							|  |  |  |         else if (has_only_one_background_color) | 
					
						
							| 
									
										
										
										
											2019-02-19 01:42:53 +01:00
										 |  |  |             painter.fill_rect(row_rect(row), lookup_color(line.attributes[0].background_color).with_alpha(255 * m_opacity)); | 
					
						
							| 
									
										
										
										
											2019-01-15 04:44:47 +01:00
										 |  |  |         for (word column = 0; column < m_columns; ++column) { | 
					
						
							| 
									
										
										
										
											2019-05-29 10:53:21 -07:00
										 |  |  |             char ch = line.characters[column]; | 
					
						
							| 
									
										
										
										
											2019-03-30 21:40:27 +01:00
										 |  |  |             bool should_reverse_fill_for_cursor = m_cursor_blink_state && m_in_active_window && row == m_cursor_row && column == m_cursor_column; | 
					
						
							| 
									
										
										
										
											2019-01-25 02:09:29 +01:00
										 |  |  |             auto& attribute = line.attributes[column]; | 
					
						
							| 
									
										
										
										
											2019-01-17 16:19:49 +01:00
										 |  |  |             auto character_rect = glyph_rect(row, column); | 
					
						
							| 
									
										
										
										
											2019-02-03 16:11:28 +01:00
										 |  |  |             if (!has_only_one_background_color || should_reverse_fill_for_cursor) { | 
					
						
							| 
									
										
										
										
											2019-02-08 00:21:02 +01:00
										 |  |  |                 auto cell_rect = character_rect.inflated(0, m_line_spacing); | 
					
						
							| 
									
										
										
										
											2019-02-19 01:42:53 +01:00
										 |  |  |                 painter.fill_rect(cell_rect, lookup_color(should_reverse_fill_for_cursor ? attribute.foreground_color : attribute.background_color).with_alpha(255 * m_opacity)); | 
					
						
							| 
									
										
										
										
											2019-02-03 16:11:28 +01:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2019-01-15 04:44:47 +01:00
										 |  |  |             if (ch == ' ') | 
					
						
							|  |  |  |                 continue; | 
					
						
							| 
									
										
										
										
											2019-02-03 16:11:28 +01:00
										 |  |  |             painter.draw_glyph(character_rect.location(), ch, lookup_color(should_reverse_fill_for_cursor ? attribute.background_color : attribute.foreground_color)); | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-01 05:40:27 +01:00
										 |  |  |     if (!m_in_active_window) { | 
					
						
							| 
									
										
										
										
											2019-02-08 00:21:02 +01:00
										 |  |  |         auto cell_rect = glyph_rect(m_cursor_row, m_cursor_column).inflated(0, m_line_spacing); | 
					
						
							|  |  |  |         painter.draw_rect(cell_rect, lookup_color(line(m_cursor_row).attributes[m_cursor_column].foreground_color)); | 
					
						
							| 
									
										
										
										
											2019-02-01 05:40:27 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-02-10 14:28:39 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-12 14:57:15 +02:00
										 |  |  | void Terminal::set_window_title(const String& title) | 
					
						
							| 
									
										
										
										
											2019-02-10 14:28:39 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     auto* w = window(); | 
					
						
							|  |  |  |     if (!w) | 
					
						
							| 
									
										
										
										
											2019-01-18 04:37:49 +01:00
										 |  |  |         return; | 
					
						
							| 
									
										
										
										
											2019-05-12 14:57:15 +02:00
										 |  |  |     w->set_title(title); | 
					
						
							| 
									
										
										
										
											2019-01-18 04:37:49 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-10 14:28:39 +01:00
										 |  |  | void Terminal::invalidate_cursor() | 
					
						
							| 
									
										
										
										
											2019-01-18 04:37:49 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-02-10 14:28:39 +01:00
										 |  |  |     line(m_cursor_row).dirty = true; | 
					
						
							| 
									
										
										
										
											2019-01-26 05:20:32 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-10 14:28:39 +01:00
										 |  |  | void Terminal::flush_dirty_lines() | 
					
						
							| 
									
										
										
										
											2019-01-26 05:20:32 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-02-10 14:28:39 +01:00
										 |  |  |     if (m_need_full_flush) { | 
					
						
							|  |  |  |         update(); | 
					
						
							|  |  |  |         m_need_full_flush = false; | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-01-26 05:20:32 +01:00
										 |  |  |     Rect rect; | 
					
						
							|  |  |  |     for (int i = 0; i < m_rows; ++i) { | 
					
						
							| 
									
										
										
										
											2019-02-10 14:28:39 +01:00
										 |  |  |         if (line(i).dirty) | 
					
						
							| 
									
										
										
										
											2019-01-26 05:20:32 +01:00
										 |  |  |             rect = rect.united(row_rect(i)); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-02-10 14:28:39 +01:00
										 |  |  |     update(rect); | 
					
						
							| 
									
										
										
										
											2019-01-17 17:38:04 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2019-02-12 10:08:35 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | void Terminal::force_repaint() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-05-11 02:27:06 +02:00
										 |  |  |     m_needs_background_fill = true; | 
					
						
							| 
									
										
										
										
											2019-02-12 10:08:35 +01:00
										 |  |  |     for (int i = 0; i < m_rows; ++i) | 
					
						
							|  |  |  |         line(i).dirty = true; | 
					
						
							|  |  |  |     update(); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-02-20 21:59:13 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-20 23:32:33 +01:00
										 |  |  | void Terminal::resize_event(GResizeEvent& event) | 
					
						
							| 
									
										
										
										
											2019-02-20 21:59:13 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-05-11 02:27:06 +02:00
										 |  |  |     int new_columns = (event.size().width() - frame_thickness() * 2 - m_inset * 2) / font().glyph_width('x'); | 
					
						
							|  |  |  |     int new_rows = (event.size().height() - frame_thickness() * 2 - m_inset * 2) / m_line_height; | 
					
						
							| 
									
										
										
										
											2019-02-20 23:32:33 +01:00
										 |  |  |     set_size(new_columns, new_rows); | 
					
						
							| 
									
										
										
										
											2019-02-20 21:59:13 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2019-02-21 00:21:23 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | void Terminal::apply_size_increments_to_window(GWindow& window) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-03-06 11:03:10 +01:00
										 |  |  |     window.set_size_increment({ font().glyph_width('x'), m_line_height }); | 
					
						
							| 
									
										
										
										
											2019-05-11 02:27:06 +02:00
										 |  |  |     window.set_base_size({ frame_thickness() * 2 + m_inset * 2, frame_thickness() * 2 + m_inset * 2 }); | 
					
						
							| 
									
										
										
										
											2019-02-21 00:21:23 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2019-03-30 21:40:27 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | void Terminal::update_cursor() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     invalidate_cursor(); | 
					
						
							|  |  |  |     flush_dirty_lines(); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-04-29 19:24:18 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | void Terminal::set_opacity(float opacity) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (m_opacity == opacity) | 
					
						
							|  |  |  |         return; | 
					
						
							| 
									
										
										
										
											2019-05-03 21:07:16 +02:00
										 |  |  |     window()->set_has_alpha_channel(opacity < 1); | 
					
						
							| 
									
										
										
										
											2019-04-29 19:24:18 +02:00
										 |  |  |     m_opacity = opacity; | 
					
						
							|  |  |  |     force_repaint(); | 
					
						
							|  |  |  | } |