| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  | #include "Terminal.h"
 | 
					
						
							|  |  |  | #include <AK/AKString.h>
 | 
					
						
							| 
									
										
										
										
											2019-01-30 21:04:49 +01:00
										 |  |  | #include <AK/StringBuilder.h>
 | 
					
						
							| 
									
										
										
										
											2019-01-19 23:22:46 +01:00
										 |  |  | #include <SharedGraphics/Font.h>
 | 
					
						
							|  |  |  | #include <SharedGraphics/Painter.h>
 | 
					
						
							| 
									
										
										
										
											2019-01-15 10:20:20 +01:00
										 |  |  | #include <AK/StdLibExtras.h>
 | 
					
						
							| 
									
										
										
										
											2019-01-25 01:27:02 +01:00
										 |  |  | #include <LibC/stdlib.h>
 | 
					
						
							|  |  |  | #include <LibC/unistd.h>
 | 
					
						
							|  |  |  | #include <LibC/stdio.h>
 | 
					
						
							|  |  |  | #include <LibC/gui.h>
 | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-23 08:55:59 +01:00
										 |  |  | //#define TERMINAL_DEBUG
 | 
					
						
							| 
									
										
										
										
											2019-01-15 10:20:20 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  | void Terminal::create_window() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-01-17 16:19:49 +01:00
										 |  |  |     m_pixel_width = m_columns * font().glyph_width() + m_inset * 2; | 
					
						
							|  |  |  |     m_pixel_height = (m_rows * (font().glyph_height() + m_line_spacing)) + (m_inset * 2) - m_line_spacing; | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-20 06:02:09 +01:00
										 |  |  |     GUI_WindowParameters params; | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |     params.rect = { { 300, 300 }, { m_pixel_width, m_pixel_height } }; | 
					
						
							|  |  |  |     params.background_color = 0x000000; | 
					
						
							|  |  |  |     strcpy(params.title, "Terminal"); | 
					
						
							|  |  |  |     m_window_id = gui_create_window(¶ms); | 
					
						
							|  |  |  |     ASSERT(m_window_id > 0); | 
					
						
							|  |  |  |     if (m_window_id < 0) { | 
					
						
							|  |  |  |         perror("gui_create_window"); | 
					
						
							|  |  |  |         exit(1); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-24 23:40:12 +01:00
										 |  |  |     // NOTE: We never release the backing store.
 | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |     GUI_WindowBackingStoreInfo info; | 
					
						
							|  |  |  |     int rc = gui_get_window_backing_store(m_window_id, &info); | 
					
						
							|  |  |  |     if (rc < 0) { | 
					
						
							|  |  |  |         perror("gui_get_window_backing_store"); | 
					
						
							|  |  |  |         exit(1); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     m_backing = GraphicsBitmap::create_wrapper(info.size, info.pixels); | 
					
						
							|  |  |  |     dbgprintf("(Terminal:%d) window backing %ux%u @ %p\n", getpid(), info.size.width, info.size.height, info.pixels); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Terminal::Terminal() | 
					
						
							| 
									
										
										
										
											2019-01-17 16:19:49 +01:00
										 |  |  |     : m_font(Font::default_font()) | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-01-17 16:19:49 +01:00
										 |  |  |     m_line_height = font().glyph_height() + m_line_spacing; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |     set_size(80, 25); | 
					
						
							|  |  |  |     m_horizontal_tabs = static_cast<byte*>(malloc(columns())); | 
					
						
							|  |  |  |     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-01-25 01:27:02 +01:00
										 |  |  |     m_lines = new Line*[rows()]; | 
					
						
							|  |  |  |     for (size_t i = 0; i < rows(); ++i) | 
					
						
							|  |  |  |         m_lines[i] = new Line(columns()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Terminal::Line::Line(word columns) | 
					
						
							|  |  |  |     : length(columns) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     characters = new byte[length]; | 
					
						
							|  |  |  |     attributes = new Attribute[length]; | 
					
						
							| 
									
										
										
										
											2019-01-26 05:20:32 +01:00
										 |  |  |     did_paint = false; | 
					
						
							| 
									
										
										
										
											2019-01-25 01:27:02 +01:00
										 |  |  |     memset(characters, ' ', length); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Terminal::Line::~Line() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     delete [] characters; | 
					
						
							|  |  |  |     delete [] attributes; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Terminal::Line::clear() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-01-25 02:09:29 +01:00
										 |  |  |     dirty = true; | 
					
						
							| 
									
										
										
										
											2019-01-25 01:27:02 +01:00
										 |  |  |     memset(characters, ' ', length); | 
					
						
							|  |  |  |     for (word i = 0 ; i < length; ++i) | 
					
						
							|  |  |  |         attributes[i].reset(); | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Terminal::~Terminal() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-01-25 01:27:02 +01:00
										 |  |  |     for (size_t i = 0; i < m_rows; ++i) | 
					
						
							|  |  |  |         delete m_lines[i]; | 
					
						
							|  |  |  |     delete [] m_lines; | 
					
						
							| 
									
										
										
										
											2019-01-15 07:30:24 +01:00
										 |  |  |     free(m_horizontal_tabs); | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Terminal::clear() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-01-25 01:27:02 +01:00
										 |  |  |     for (size_t i = 0; i < rows(); ++i) | 
					
						
							|  |  |  |         line(i).clear(); | 
					
						
							| 
									
										
										
										
											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-01-31 17:31:23 +01:00
										 |  |  | unsigned parse_uint(const String& str, bool& ok) | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     unsigned value = 0; | 
					
						
							|  |  |  |     for (size_t i = 0; i < str.length(); ++i) { | 
					
						
							|  |  |  |         if (str[i] < '0' || str[i] > '9') { | 
					
						
							|  |  |  |             ok = false; | 
					
						
							|  |  |  |             return 0; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         value = value * 10; | 
					
						
							|  |  |  |         value += str[i] - '0'; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     ok = true; | 
					
						
							|  |  |  |     return value; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-15 07:30:24 +01:00
										 |  |  | enum ANSIColor : byte { | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |     Black = 0, | 
					
						
							|  |  |  |     Red, | 
					
						
							|  |  |  |     Green, | 
					
						
							|  |  |  |     Brown, | 
					
						
							|  |  |  |     Blue, | 
					
						
							|  |  |  |     Magenta, | 
					
						
							|  |  |  |     Cyan, | 
					
						
							|  |  |  |     LightGray, | 
					
						
							|  |  |  |     DarkGray, | 
					
						
							|  |  |  |     BrightRed, | 
					
						
							|  |  |  |     BrightGreen, | 
					
						
							|  |  |  |     Yellow, | 
					
						
							|  |  |  |     BrightBlue, | 
					
						
							|  |  |  |     BrightMagenta, | 
					
						
							|  |  |  |     BrightCyan, | 
					
						
							|  |  |  |     White, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-15 07:30:24 +01:00
										 |  |  | static inline Color ansi_color(unsigned color) | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-01-25 02:09:29 +01:00
										 |  |  |     static const RGBA32 s_ansi_color[16] = { | 
					
						
							|  |  |  |         make_rgb(0, 0, 0),          // Black
 | 
					
						
							|  |  |  |         make_rgb(225, 56, 43),      // Red
 | 
					
						
							|  |  |  |         make_rgb(57, 181, 74),      // Green
 | 
					
						
							|  |  |  |         make_rgb(255, 199, 6),      // Brown
 | 
					
						
							| 
									
										
										
										
											2019-01-25 03:24:52 +01:00
										 |  |  |         make_rgb(0, 111, 184),      // Blue
 | 
					
						
							| 
									
										
										
										
											2019-01-25 02:09:29 +01:00
										 |  |  |         make_rgb(118, 38, 113),     // Magenta
 | 
					
						
							|  |  |  |         make_rgb(44, 181, 233),     // Cyan
 | 
					
						
							|  |  |  |         make_rgb(204, 204, 204),    // LightGray
 | 
					
						
							|  |  |  |         make_rgb(128, 128, 128),    // DarkGray
 | 
					
						
							|  |  |  |         make_rgb(255, 0, 0),        // BrightRed
 | 
					
						
							|  |  |  |         make_rgb(0, 255, 0),        // BrightGreen
 | 
					
						
							|  |  |  |         make_rgb(255, 255, 0),      // Yellow
 | 
					
						
							|  |  |  |         make_rgb(0, 0, 255),        // BrightBlue
 | 
					
						
							|  |  |  |         make_rgb(255, 0, 255),      // BrightMagenta
 | 
					
						
							|  |  |  |         make_rgb(0, 255, 255),      // BrightCyan
 | 
					
						
							|  |  |  |         make_rgb(255, 255, 255),    // White
 | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     return s_ansi_color[color]; | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Terminal::escape$m(const Vector<unsigned>& params) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     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: | 
					
						
							|  |  |  |             // Bold
 | 
					
						
							| 
									
										
										
										
											2019-01-25 02:09:29 +01:00
										 |  |  |             //m_current_attribute.bold = true;
 | 
					
						
							| 
									
										
										
										
											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-01-15 07:30:24 +01:00
										 |  |  |             m_current_attribute.foreground_color = param - 30; | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |             break; | 
					
						
							|  |  |  |         case 40: | 
					
						
							|  |  |  |         case 41: | 
					
						
							|  |  |  |         case 42: | 
					
						
							|  |  |  |         case 43: | 
					
						
							|  |  |  |         case 44: | 
					
						
							|  |  |  |         case 45: | 
					
						
							|  |  |  |         case 46: | 
					
						
							|  |  |  |         case 47: | 
					
						
							|  |  |  |             // Background color
 | 
					
						
							| 
									
										
										
										
											2019-01-15 07:30:24 +01:00
										 |  |  |             m_current_attribute.background_color = param - 30; | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Terminal::escape$s(const Vector<unsigned>&) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     m_saved_cursor_row = m_cursor_row; | 
					
						
							|  |  |  |     m_saved_cursor_column = m_cursor_column; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Terminal::escape$u(const Vector<unsigned>&) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     set_cursor(m_saved_cursor_row, m_saved_cursor_column); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Terminal::escape$H(const Vector<unsigned>& params) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     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); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Terminal::escape$A(const Vector<unsigned>& params) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     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-01-23 08:55:59 +01:00
										 |  |  | void Terminal::escape$B(const Vector<unsigned>& params) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     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); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Terminal::escape$C(const Vector<unsigned>& params) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     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-01-15 04:30:55 +01:00
										 |  |  | void Terminal::escape$D(const Vector<unsigned>& params) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     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-01-23 08:55:59 +01:00
										 |  |  | void Terminal::escape$K(const Vector<unsigned>& params) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int mode = 0; | 
					
						
							|  |  |  |     if (params.size() >= 1) | 
					
						
							|  |  |  |         mode = params[0]; | 
					
						
							|  |  |  |     switch (mode) { | 
					
						
							|  |  |  |     case 0: | 
					
						
							|  |  |  |         for (int i = m_cursor_column; i < m_columns; ++i) { | 
					
						
							|  |  |  |             put_character_at(m_cursor_row, i, ' '); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 1: | 
					
						
							|  |  |  |         // FIXME: Clear from cursor to beginning of screen.
 | 
					
						
							| 
									
										
										
										
											2019-01-30 21:04:49 +01:00
										 |  |  |         unimplemented_escape(); | 
					
						
							| 
									
										
										
										
											2019-01-23 08:55:59 +01:00
										 |  |  |         break; | 
					
						
							|  |  |  |     case 2: | 
					
						
							| 
									
										
										
										
											2019-01-30 21:04:49 +01:00
										 |  |  |         unimplemented_escape(); | 
					
						
							| 
									
										
										
										
											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-01-15 04:30:55 +01:00
										 |  |  | void Terminal::escape$J(const Vector<unsigned>& params) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     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.
 | 
					
						
							|  |  |  |         for (int i = m_cursor_column; i < m_columns; ++i) { | 
					
						
							|  |  |  |             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: | 
					
						
							|  |  |  |         // FIXME: Clear from cursor to beginning of screen.
 | 
					
						
							| 
									
										
										
										
											2019-01-30 21:04:49 +01:00
										 |  |  |         unimplemented_escape(); | 
					
						
							| 
									
										
										
										
											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-02-03 14:00:48 +01:00
										 |  |  | void Terminal::escape$M(const Vector<unsigned>& params) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int count = 1; | 
					
						
							|  |  |  |     if (params.size() >= 1) | 
					
						
							|  |  |  |         count = params[0]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (count == 1 && m_cursor_row == 0) { | 
					
						
							|  |  |  |         scroll_up(); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     int max_count = m_rows - m_cursor_row; | 
					
						
							|  |  |  |     count = min(count, max_count); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     dbgprintf("Delete %d line(s) starting from %d\n", count, m_cursor_row); | 
					
						
							|  |  |  |     // FIXME: Implement.
 | 
					
						
							|  |  |  |     ASSERT_NOT_REACHED(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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-01-31 17:31:23 +01:00
										 |  |  |     unsigned value = parse_uint(String((const char*)m_xterm_param1.data(), m_xterm_param1.size()), ok); | 
					
						
							| 
									
										
										
										
											2019-01-25 05:51:39 +01:00
										 |  |  |     if (ok) { | 
					
						
							|  |  |  |         switch (value) { | 
					
						
							|  |  |  |         case 0: | 
					
						
							|  |  |  |             set_window_title(String((const char*)m_xterm_param2.data(), m_xterm_param2.size())); | 
					
						
							|  |  |  |             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-01-30 21:04:49 +01:00
										 |  |  |     m_final = final; | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |     auto paramparts = String((const char*)m_parameters.data(), m_parameters.size()).split(';'); | 
					
						
							|  |  |  |     Vector<unsigned> params; | 
					
						
							|  |  |  |     for (auto& parampart : paramparts) { | 
					
						
							|  |  |  |         bool ok; | 
					
						
							| 
									
										
										
										
											2019-01-31 17:31:23 +01:00
										 |  |  |         unsigned value = parse_uint(parampart, ok); | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |         if (!ok) { | 
					
						
							|  |  |  |             // FIXME: Should we do something else?
 | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         params.append(value); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     switch (final) { | 
					
						
							|  |  |  |     case 'A': escape$A(params); break; | 
					
						
							| 
									
										
										
										
											2019-01-23 08:55:59 +01:00
										 |  |  |     case 'B': escape$B(params); break; | 
					
						
							|  |  |  |     case 'C': escape$C(params); break; | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |     case 'D': escape$D(params); break; | 
					
						
							|  |  |  |     case 'H': escape$H(params); break; | 
					
						
							|  |  |  |     case 'J': escape$J(params); break; | 
					
						
							| 
									
										
										
										
											2019-01-23 08:55:59 +01:00
										 |  |  |     case 'K': escape$K(params); break; | 
					
						
							| 
									
										
										
										
											2019-02-03 14:00:48 +01:00
										 |  |  |     case 'M': escape$M(params); break; | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |     case 'm': escape$m(params); break; | 
					
						
							|  |  |  |     case 's': escape$s(params); break; | 
					
						
							|  |  |  |     case 'u': escape$u(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
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     m_parameters.clear(); | 
					
						
							|  |  |  |     m_intermediates.clear(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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-01-15 04:30:55 +01:00
										 |  |  |     if (m_cursor_row == (rows() - 1)) { | 
					
						
							| 
									
										
										
										
											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(); | 
					
						
							|  |  |  |     delete m_lines[0]; | 
					
						
							|  |  |  |     for (word row = 1; row < rows(); ++row) | 
					
						
							|  |  |  |         m_lines[row - 1] = m_lines[row]; | 
					
						
							|  |  |  |     m_lines[m_rows - 1] = new Line(m_columns); | 
					
						
							|  |  |  |     ++m_rows_to_scroll_backing_store; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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-01-23 19:58:45 +01:00
										 |  |  |     if (column != columns() - 1) | 
					
						
							|  |  |  |         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); | 
					
						
							|  |  |  |     if ((line.characters[column] == ch) && (line.attributes[column] == m_current_attribute)) | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     line.characters[column] = ch; | 
					
						
							|  |  |  |     line.attributes[column] = m_current_attribute; | 
					
						
							|  |  |  |     line.dirty = true; | 
					
						
							| 
									
										
										
										
											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
 | 
					
						
							|  |  |  |     dbgprintf("Terminal::on_char: %b (%c)\n", ch, ch); | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |     switch (m_escape_state) { | 
					
						
							|  |  |  |     case ExpectBracket: | 
					
						
							|  |  |  |         if (ch == '[') | 
					
						
							|  |  |  |             m_escape_state = ExpectParameter; | 
					
						
							| 
									
										
										
										
											2019-01-25 05:51:39 +01:00
										 |  |  |         else if (ch == ']') | 
					
						
							|  |  |  |             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; | 
					
						
							|  |  |  |         // fall through
 | 
					
						
							|  |  |  |     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; | 
					
						
							|  |  |  |         // fall through
 | 
					
						
							|  |  |  |     case ExpectIntermediate: | 
					
						
							|  |  |  |         if (is_valid_intermediate_character(ch)) { | 
					
						
							|  |  |  |             m_intermediates.append(ch); | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         m_escape_state = ExpectFinal; | 
					
						
							|  |  |  |         // fall through
 | 
					
						
							|  |  |  |     case ExpectFinal: | 
					
						
							|  |  |  |         if (is_valid_final_character(ch)) { | 
					
						
							|  |  |  |             m_escape_state = Normal; | 
					
						
							|  |  |  |             execute_escape_sequence(ch); | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         m_escape_state = Normal; | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     case Normal: | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     switch (ch) { | 
					
						
							|  |  |  |     case '\0': | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     case '\033': | 
					
						
							|  |  |  |         m_escape_state = ExpectBracket; | 
					
						
							|  |  |  |         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': | 
					
						
							|  |  |  |         // FIXME: Bell!
 | 
					
						
							|  |  |  |         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) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     for (size_t i = 0; i < str.length(); ++i) | 
					
						
							|  |  |  |         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:"); | 
					
						
							|  |  |  |         for (size_t i = 0; i < m_parameters.size(); ++i) | 
					
						
							|  |  |  |             builder.append((char)m_parameters[i]); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (!m_intermediates.is_empty()) { | 
					
						
							|  |  |  |         builder.append(" intermediates:"); | 
					
						
							|  |  |  |         for (size_t i = 0; i < m_intermediates.size(); ++i) | 
					
						
							|  |  |  |             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) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     m_columns = columns; | 
					
						
							|  |  |  |     m_rows = rows; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-17 16:19:49 +01:00
										 |  |  | Rect Terminal::glyph_rect(word row, word column) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int y = row * m_line_height; | 
					
						
							|  |  |  |     int x = column * font().glyph_width(); | 
					
						
							| 
									
										
										
										
											2019-01-17 16:34:37 +01:00
										 |  |  |     return { x + m_inset, y + m_inset, font().glyph_width(), 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; | 
					
						
							|  |  |  |     return { m_inset, y + m_inset, font().glyph_width() * m_columns, font().glyph_height() }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-17 16:19:49 +01:00
										 |  |  | inline Terminal::Attribute& Terminal::attribute_at(word row, word column) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-01-23 07:45:23 +01:00
										 |  |  |     ASSERT(row < m_rows); | 
					
						
							|  |  |  |     ASSERT(column < m_columns); | 
					
						
							| 
									
										
										
										
											2019-01-25 01:27:02 +01:00
										 |  |  |     return line(row).attributes[column]; | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (!length) | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     // FIXME: Cache this result?
 | 
					
						
							|  |  |  |     auto color = attributes[0].background_color; | 
					
						
							|  |  |  |     for (size_t i = 1; i < length; ++i) { | 
					
						
							|  |  |  |         if (attributes[i].background_color != color) | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  | void Terminal::paint() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     Rect rect { 0, 0, m_pixel_width, m_pixel_height }; | 
					
						
							|  |  |  |     Painter painter(*m_backing); | 
					
						
							| 
									
										
										
										
											2019-01-18 04:37:49 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-25 01:27:02 +01:00
										 |  |  |     for (size_t i = 0; i < rows(); ++i) | 
					
						
							| 
									
										
										
										
											2019-01-26 05:20:32 +01:00
										 |  |  |         line(i).did_paint = false; | 
					
						
							| 
									
										
										
										
											2019-01-18 04:37:49 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-15 10:20:20 +01:00
										 |  |  |     if (m_rows_to_scroll_backing_store && m_rows_to_scroll_backing_store < m_rows) { | 
					
						
							|  |  |  |         int first_scanline = m_inset; | 
					
						
							| 
									
										
										
										
											2019-01-17 16:19:49 +01:00
										 |  |  |         int second_scanline = m_inset + (m_rows_to_scroll_backing_store * m_line_height); | 
					
						
							| 
									
										
										
										
											2019-01-15 10:20:20 +01:00
										 |  |  |         int num_rows_to_memcpy = m_rows - m_rows_to_scroll_backing_store; | 
					
						
							| 
									
										
										
										
											2019-01-17 16:19:49 +01:00
										 |  |  |         int scanlines_to_copy = (num_rows_to_memcpy * m_line_height) - m_line_spacing; | 
					
						
							| 
									
										
										
										
											2019-01-15 10:20:20 +01:00
										 |  |  |         fast_dword_copy( | 
					
						
							|  |  |  |             m_backing->scanline(first_scanline), | 
					
						
							|  |  |  |             m_backing->scanline(second_scanline), | 
					
						
							|  |  |  |             scanlines_to_copy * m_pixel_width | 
					
						
							|  |  |  |         ); | 
					
						
							| 
									
										
										
										
											2019-01-20 23:42:36 +01:00
										 |  |  |         m_need_full_invalidation = true; | 
					
						
							| 
									
										
										
										
											2019-01-25 02:09:29 +01:00
										 |  |  |         line(max(0, m_cursor_row - m_rows_to_scroll_backing_store)).dirty = true; | 
					
						
							| 
									
										
										
										
											2019-01-15 10:20:20 +01:00
										 |  |  |     } | 
					
						
							|  |  |  |     m_rows_to_scroll_backing_store = 0; | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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); | 
					
						
							|  |  |  |         if (!line.dirty) | 
					
						
							|  |  |  |             continue; | 
					
						
							|  |  |  |         line.dirty = false; | 
					
						
							|  |  |  |         bool has_only_one_background_color = line.has_only_one_background_color(); | 
					
						
							|  |  |  |         if (has_only_one_background_color) | 
					
						
							|  |  |  |             painter.fill_rect(row_rect(row), line.attributes[0].background_color); | 
					
						
							| 
									
										
										
										
											2019-01-15 04:44:47 +01:00
										 |  |  |         for (word column = 0; column < m_columns; ++column) { | 
					
						
							| 
									
										
										
										
											2019-02-01 05:40:27 +01:00
										 |  |  |             bool should_reverse_fill_for_cursor = 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-26 05:20:32 +01:00
										 |  |  |             line.did_paint = true; | 
					
						
							| 
									
										
										
										
											2019-01-25 02:09:29 +01:00
										 |  |  |             char ch = line.characters[column]; | 
					
						
							| 
									
										
										
										
											2019-01-17 16:19:49 +01:00
										 |  |  |             auto character_rect = glyph_rect(row, column); | 
					
						
							| 
									
										
										
										
											2019-02-01 05:40:27 +01:00
										 |  |  |             if (!has_only_one_background_color || should_reverse_fill_for_cursor) | 
					
						
							|  |  |  |                 painter.fill_rect(character_rect, ansi_color(should_reverse_fill_for_cursor ? attribute.foreground_color : attribute.background_color)); | 
					
						
							| 
									
										
										
										
											2019-01-15 04:44:47 +01:00
										 |  |  |             if (ch == ' ') | 
					
						
							|  |  |  |                 continue; | 
					
						
							| 
									
										
										
										
											2019-02-01 05:40:27 +01:00
										 |  |  |             painter.draw_glyph(character_rect.location(), ch, ansi_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) { | 
					
						
							|  |  |  |         auto cursor_rect = glyph_rect(m_cursor_row, m_cursor_column); | 
					
						
							|  |  |  |         painter.draw_rect(cursor_rect, ansi_color(line(m_cursor_row).attributes[m_cursor_column].foreground_color)); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-01-17 16:19:49 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-26 05:20:32 +01:00
										 |  |  |     line(m_cursor_row).did_paint = true; | 
					
						
							| 
									
										
										
										
											2019-01-18 04:37:49 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (m_belling) { | 
					
						
							| 
									
										
										
										
											2019-01-20 23:42:36 +01:00
										 |  |  |         m_need_full_invalidation = true; | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |         painter.draw_rect(rect, Color::Red); | 
					
						
							| 
									
										
										
										
											2019-01-18 04:37:49 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-20 23:42:36 +01:00
										 |  |  |     if (m_need_full_invalidation) { | 
					
						
							| 
									
										
										
										
											2019-01-26 05:20:32 +01:00
										 |  |  |         did_paint(); | 
					
						
							| 
									
										
										
										
											2019-01-20 23:42:36 +01:00
										 |  |  |         m_need_full_invalidation = false; | 
					
						
							| 
									
										
										
										
											2019-01-18 04:37:49 +01:00
										 |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-26 05:20:32 +01:00
										 |  |  |     Rect painted_rect; | 
					
						
							| 
									
										
										
										
											2019-01-18 04:37:49 +01:00
										 |  |  |     for (int i = 0; i < m_rows; ++i) { | 
					
						
							| 
									
										
										
										
											2019-01-26 05:20:32 +01:00
										 |  |  |         if (line(i).did_paint) | 
					
						
							|  |  |  |             painted_rect = painted_rect.united(row_rect(i)); | 
					
						
							| 
									
										
										
										
											2019-01-18 04:37:49 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-01-26 05:20:32 +01:00
										 |  |  |     did_paint(painted_rect); | 
					
						
							| 
									
										
										
										
											2019-01-18 04:37:49 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-26 05:20:32 +01:00
										 |  |  | void Terminal::did_paint(const Rect& a_rect) | 
					
						
							| 
									
										
										
										
											2019-01-18 04:37:49 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     GUI_Rect rect = a_rect; | 
					
						
							| 
									
										
										
										
											2019-01-26 05:20:32 +01:00
										 |  |  |     int rc = gui_notify_paint_finished(m_window_id, a_rect.is_null() ? nullptr : &rect); | 
					
						
							|  |  |  |     if (rc < 0) { | 
					
						
							|  |  |  |         perror("gui_notify_paint_finished"); | 
					
						
							|  |  |  |         exit(1); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Terminal::update() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     Rect rect; | 
					
						
							|  |  |  |     for (int i = 0; i < m_rows; ++i) { | 
					
						
							|  |  |  |         if (line(i).did_paint) | 
					
						
							|  |  |  |             rect = rect.united(row_rect(i)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     GUI_Rect gui_rect = rect; | 
					
						
							|  |  |  |     int rc = gui_invalidate_window(m_window_id, rect.is_null() ? nullptr : &gui_rect); | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |     if (rc < 0) { | 
					
						
							|  |  |  |         perror("gui_invalidate_window"); | 
					
						
							|  |  |  |         exit(1); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-01-17 17:38:04 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-25 05:51:39 +01:00
										 |  |  | void Terminal::set_window_title(const String& title) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int rc = gui_set_window_title(m_window_id, title.characters(), title.length()); | 
					
						
							|  |  |  |     if (rc < 0) { | 
					
						
							|  |  |  |         perror("gui_set_window_title"); | 
					
						
							|  |  |  |         exit(1); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-17 17:38:04 +01:00
										 |  |  | void Terminal::set_in_active_window(bool b) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (m_in_active_window == b) | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     m_in_active_window = b; | 
					
						
							|  |  |  |     invalidate_cursor(); | 
					
						
							| 
									
										
										
										
											2019-01-26 05:20:32 +01:00
										 |  |  |     update(); | 
					
						
							| 
									
										
										
										
											2019-01-17 17:38:04 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Terminal::invalidate_cursor() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-01-25 02:09:29 +01:00
										 |  |  |     line(m_cursor_row).dirty = true; | 
					
						
							| 
									
										
										
										
											2019-01-17 17:38:04 +01:00
										 |  |  | } |