| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  | #include "Terminal.h"
 | 
					
						
							| 
									
										
										
										
											2019-02-03 16:11:28 +01:00
										 |  |  | #include "XtermColors.h"
 | 
					
						
							| 
									
										
										
										
											2019-02-11 15:07:05 +01:00
										 |  |  | #include <string.h>
 | 
					
						
							|  |  |  | #include <errno.h>
 | 
					
						
							|  |  |  | #include <stdlib.h>
 | 
					
						
							|  |  |  | #include <unistd.h>
 | 
					
						
							|  |  |  | #include <stdio.h>
 | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  | #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>
 | 
					
						
							| 
									
										
										
										
											2019-03-28 17:19:56 +01:00
										 |  |  | #include <LibGUI/GPainter.h>
 | 
					
						
							| 
									
										
										
										
											2019-01-15 10:20:20 +01:00
										 |  |  | #include <AK/StdLibExtras.h>
 | 
					
						
							| 
									
										
										
										
											2019-02-11 15:07:05 +01:00
										 |  |  | #include <LibGUI/GApplication.h>
 | 
					
						
							| 
									
										
										
										
											2019-02-10 14:28:39 +01:00
										 |  |  | #include <LibGUI/GWindow.h>
 | 
					
						
							|  |  |  | #include <Kernel/KeyCode.h>
 | 
					
						
							| 
									
										
										
										
											2019-02-20 23:32:33 +01:00
										 |  |  | #include <sys/ioctl.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-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", | 
					
						
							|  |  |  |                                                                "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-04-10 17:35:43 +02: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), | 
					
						
							|  |  |  |              m_config->read_num_entry("Window", "Height", 25)); | 
					
						
							| 
									
										
										
										
											2019-01-25 01:27:02 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Terminal::Line::Line(word columns) | 
					
						
							|  |  |  |     : length(columns) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     characters = new byte[length]; | 
					
						
							|  |  |  |     attributes = new Attribute[length]; | 
					
						
							|  |  |  |     memset(characters, ' ', length); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Terminal::Line::~Line() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     delete [] characters; | 
					
						
							|  |  |  |     delete [] attributes; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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) { | 
					
						
							|  |  |  |         memset(characters, ' ', length); | 
					
						
							|  |  |  |         for (word i = 0 ; i < length; ++i) | 
					
						
							|  |  |  |             attributes[i] = attribute; | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     for (unsigned i = 0 ; i < length; ++i) { | 
					
						
							|  |  |  |         if (characters[i] != ' ') | 
					
						
							|  |  |  |             dirty = true; | 
					
						
							|  |  |  |         characters[i] = ' '; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     for (unsigned i = 0 ; i < length; ++i) { | 
					
						
							|  |  |  |         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() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-02-25 22:06:55 +01:00
										 |  |  |     for (int i = 0; i < m_rows; ++i) | 
					
						
							| 
									
										
										
										
											2019-01-25 01:27:02 +01:00
										 |  |  |         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) | 
					
						
							| 
									
										
										
										
											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-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: | 
					
						
							|  |  |  |             // 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-02-04 10:22:47 +01:00
										 |  |  |             m_current_attribute.background_color = param - 40; | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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-03-14 16:25:19 +01:00
										 |  |  |     dbgprintf("FIXME: escape$t: Ps: %u\n", params[0]); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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]; | 
					
						
							|  |  |  |     dbgprintf("FIXME: escape$r: Set scrolling region: %u-%u\n", top, bottom); | 
					
						
							| 
									
										
										
										
											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-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-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-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.
 | 
					
						
							|  |  |  |         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-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; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     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-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-01-30 21:04:49 +01:00
										 |  |  |     m_final = final; | 
					
						
							| 
									
										
										
										
											2019-04-20 14:13:40 +02:00
										 |  |  |     auto paramparts = String::copy(m_parameters).split(';'); | 
					
						
							| 
									
										
										
										
											2019-04-20 14:02:19 +02:00
										 |  |  |     ParamVector params; | 
					
						
							| 
									
										
										
										
											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-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
										 |  |  |             // 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-03-14 17:39:50 +01:00
										 |  |  |     case 'G': escape$G(params); break; | 
					
						
							|  |  |  |     case 'X': escape$X(params); break; | 
					
						
							|  |  |  |     case 'd': escape$d(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-02-27 00:02:01 +01:00
										 |  |  |     case 't': escape$t(params); break; | 
					
						
							| 
									
										
										
										
											2019-03-14 16:25:19 +01:00
										 |  |  |     case 'r': escape$r(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-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-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-10 14:47:09 +01:00
										 |  |  |     m_need_full_flush = true; | 
					
						
							| 
									
										
										
										
											2019-02-03 14:00:48 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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
 | 
					
						
							| 
									
										
										
										
											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-17 14:34:07 +02:00
										 |  |  |         sysbeep(); | 
					
						
							| 
									
										
										
										
											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; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (m_lines) { | 
					
						
							|  |  |  |         for (size_t i = 0; i < m_rows; ++i) | 
					
						
							|  |  |  |             delete m_lines[i]; | 
					
						
							|  |  |  |         delete m_lines; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-15 04:30:55 +01:00
										 |  |  |     m_columns = columns; | 
					
						
							|  |  |  |     m_rows = rows; | 
					
						
							| 
									
										
										
										
											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; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (m_horizontal_tabs) | 
					
						
							|  |  |  |         free(m_horizontal_tabs); | 
					
						
							|  |  |  |     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; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     m_lines = new Line*[rows]; | 
					
						
							|  |  |  |     for (size_t i = 0; i < rows; ++i) | 
					
						
							|  |  |  |         m_lines[i] = new Line(columns); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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_rows_to_scroll_backing_store = 0; | 
					
						
							|  |  |  |     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 | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     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-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-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-02-19 01:42:53 +01: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-02-19 01:42:53 +01:00
										 |  |  |     if (m_needs_background_fill) { | 
					
						
							|  |  |  |         m_needs_background_fill = false; | 
					
						
							| 
									
										
										
										
											2019-05-11 02:27:06 +02:00
										 |  |  |         painter.fill_rect(frame_inner_rect(), Color(Color::Black).with_alpha(255 * m_opacity)); | 
					
						
							| 
									
										
										
										
											2019-02-19 01:42:53 +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-05-11 02:27:06 +02:00
										 |  |  |         memcpy( | 
					
						
							| 
									
										
										
										
											2019-02-10 14:28:39 +01:00
										 |  |  |             painter.target()->scanline(first_scanline), | 
					
						
							|  |  |  |             painter.target()->scanline(second_scanline), | 
					
						
							| 
									
										
										
										
											2019-05-11 02:27:06 +02:00
										 |  |  |             scanlines_to_copy * painter.target()->pitch() | 
					
						
							| 
									
										
										
										
											2019-01-15 10:20:20 +01:00
										 |  |  |         ); | 
					
						
							| 
									
										
										
										
											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(); | 
					
						
							| 
									
										
										
										
											2019-02-03 16:11:28 +01:00
										 |  |  |         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-02-03 16:11:28 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-01-15 04:44:47 +01:00
										 |  |  |         for (word column = 0; column < m_columns; ++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]; | 
					
						
							|  |  |  |             char ch = line.characters[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-01-17 16:19:49 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-10 14:28:39 +01:00
										 |  |  |     if (m_belling) | 
					
						
							| 
									
										
										
										
											2019-05-11 02:27:06 +02:00
										 |  |  |         painter.draw_rect(frame_inner_rect(), Color::Red); | 
					
						
							| 
									
										
										
										
											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(); | 
					
						
							|  |  |  | } |