summaryrefslogblamecommitdiff
path: root/src/kernel/ui/vt.cpp
blob: 9b0bdf03fb0d74f57ef2ee666826167bd681f84d (plain) (tree)
1
2
3
4
5


                        
                            
 








                                                                          





                                                                  
                              
                          


                              











                                                                  






                                                                     

 


















                                                          


                              
                                                            

                                          
                                                              


                                                                                    
                                                                          
                          
                                                                   

                          

                                                                               








                                        
                          












                                                                                                           











                                              
                          



                         



                          















                                                  
                              
 
                                     
                         
                                







                                                             
                                                                  








                                                                              
                                                   








                                                                                                                 
                                                          

 








                                                                 











                                                                                                             




                                                                         
















                                                                  

                                                        

                                      

































































































































                                                                                                                                   


                   







                                                       















                                                                            
                                         




                                                                               
                                 
                                           





                                                                                    
                                              




                                            


                               










                                              
#include "vt.h"
#include <dev/display.h>

vt *ke_vt = 0, *home_vt = 0;

static char *kb_cmd_esc[] = {
	/* 00 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	/* 10 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	/* 20 */ 0, 0, 0, 0, 0, 0, 0, 0, "\x1b[B", 0,
	/* 30 */ "\x1b[D", 0, "\x1b[C", 0, "\x1b[A", 0, 0, 0, 0, 0,
	/* 40 */ 0, 0, 0, 0, 0, 0, "\x1b[B", "\x1b[A", "\x1b[D", "\x1b[C",
	/* 50 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	/* 60 */ 0, 0, 0, 0 };

vt::vt(node* parent, int ww, int hh) : node(parent, FT_TERMINAL) {
	w = ww; h = hh;
	fgcolor = TC_LIGHTGRAY;
	bgcolor = TC_BLACK;
	output = 0;
	cursor_visible = true;
	keyboard_echo = false;
	csr_l = csr_c = 0;

	kbd_buffer_filled = 0;
	kbd_waiter = 0;
	
	text = 0;
	if (w != 0 && h != 0) {
		text = (vt_char*)kmalloc(w * h * sizeof(vt_char));
		clear();
	}
}

void vt::put_at(int l, int c, int ch) {
	text[l * w + c].fgcolor = fgcolor;
	text[l * w + c].bgcolor = bgcolor;
	text[l * w + c].ch = ch;
	if (output != 0) {
		if (output->connected_vt != this) {
			output = 0;
		} else {
			output->text_put(l, c, ch, fgcolor, bgcolor);
		}
	}
}

void vt::scroll(int n) {
	if (output != 0 && output->connected_vt == this) {
		output->text_scroll(n, fgcolor, bgcolor);
	}
	if (n > 0) {
		for (int j = 0; j < w * (h-n); j++) {
			text[j] = text[j + w];
		}
		for (int j = w * (h-n); j < w * h; j++) {
			text[j].fgcolor = fgcolor;
			text[j].bgcolor = bgcolor;
			text[j].ch = ' ';
		}
	} else if (n < 0) {
		//TODO
	}
	csr_l -= n;
}

void vt::put(int c) {
	if (text == 0) return;

	if (c == '\b' && csr_c != 0) {		// backspace
		csr_c--;
		put_at(csr_l, csr_c, ' ');
	} else if (c == '\t') {				// tab
		int csr_nc = (csr_c + 8) & ~(8 - 1);
		for (int i = csr_c; i < csr_nc && i < w; i++) put_at(csr_l, i, ' ');
		csr_c = csr_nc;
	} else if (c == '\r') {				// carriage return
		csr_c = 0;
	} else if (c == '\n') {				// new line
		csr_c = 0;
		csr_l++;
	} else if (c == '\f') {				// form feed (new page)
		clear();
	} else if (c >= ' ') {
		put_at(csr_l, csr_c, c);
		csr_c++;
	}
	if (csr_c >= w) {
		csr_c = 0;
		csr_l++;
	}
	if (csr_l == h) {
		scroll(1);
	}
	if (output != 0 && output->connected_vt == this) output->text_setcsr(csr_l, csr_c, cursor_visible);
}

void vt::clear() {
	for (int i = 0; i < w; i++) {
		for (int j = 0; j < h; j++) {
			put_at(j, i, ' ');
		}
	}
	csr_c = csr_l = 0;
}

void vt::writeHex(uint32_t v) {
	int i;

	put('0'); put('x');
	char hexdigits[] = "0123456789abcdef";

	for (i = 0; i < 8; i++) {
		put(hexdigits[v >> 28]);
		v = v << 4;
	}
}

void vt::writeDec(int v) {
	if (v == 0) {
		put('0');
		return;
	}
	if (v < 0) {
		put ('-');
		v = -v;
	}

	char numbers[] = "0123456789";
	while (v > 0) {
		int order = 1, no = 1;
		while (v / order > 0) order *= 10;
		order /= 10;
		put(numbers[v / order]);
		v = v - (v / order * order);
		while (v / no > 0) no *= 10;
		while (no < order) {
			put('0');
			no *= 10;
		}
	}
}

// *************** INTERACTION

void vt::outputTo(display *display) {
	output = display;
	if (output == 0) return;
	output->connected_vt = this;
	
	if (output->text_w() != w || output->text_h() != h) {
		int ow = w, oh = h;
		vt_char *old_text = text;

		w = output->text_w();
		h = output->text_h();
		text = (vt_char*)kmalloc(w * h * sizeof(vt_char));
		for (int c = 0; c < w; c++) {
			for (int l = 0; l < h; l++) {
				if (c < ow && l < oh) {
					put_at(l, c, old_text[l * ow + c].ch);
				} else {
					put_at(l, c, ' ');
				}
			}
		}
		if (old_text != 0) kfree(old_text);
		if (csr_c >= w) csr_c = w;
		if (csr_l >= h) csr_l = h;
	} else {
		for (int c = 0; c < w; c++) {
			for (int l = 0; l < h; l++) {
				output->text_put(l, c, text[l*w+c].ch, text[l*w+c].fgcolor, text[l*w+c].bgcolor);
			}
		}
	}
	output->text_setcsr(csr_l, csr_c, cursor_visible);
}

void vt::keyboardInput(keypress kp, keyboard* from) {
	if (kp.command == KB_RSUPER || kp.command == KB_LSUPER) {
		if (this == home_vt) return;
		// go to home terminal
		home_vt->outputTo(output);
		from->outputTo(home_vt);
		output = 0;
		return;
	}
	// convert to sequence of chars
	int n = 0;
	char b[8];

	if (kp.character >= 0x80) kp.character = '?';	// todo some day : unicode

	if ((kp.command == 0 || kp.command == KB_ENTER || kp.command == KB_TAB || kp.command == KB_BACKSPACE)
			&& kp.character != 0 && kp.pressed) {
		b[n++] = (char)kp.character;
	} else if (kp.command == KB_CMD_ALT && kp.character >= 64 && kp.character <= 126 && kp.pressed) {
		b[n++] = 27;
		b[n++] = kp.character;
	} else if (kb_cmd_esc[kp.command & 0x3F] != 0 && kp.pressed) {
		int i;
		for (i = 0; kb_cmd_esc[kp.command & 0x3F][i] != 0; i++) {
			b[n++] = kb_cmd_esc[kp.command & 0x3F][i];
		}
	}
	if (n == 0) return;
	if (kbd_buffer_filled + n > KBD_BUFFER_SIZE) goto wake_up;
	for (int i = 0; i < n; i++) {
		kbd_buffer[kbd_buffer_filled++] = b[i];
	}

	wake_up:
	if (kbd_waiter != 0) {
		thread *w = kbd_waiter;
		kbd_waiter = 0;
		w->wakeUp();
	}
}

// ********************* SYSCALLS

int vt::write(size_t offset, size_t len, char* buffer) {
	// ignore offset
	// TODO: ANSI escape sequences
	// todo some day : unicode
	for (unsigned i = 0; i < len; i++) {
		if (buffer[i] == 27) {
			i++;
			if (buffer[i++] == '[') {
				int n = -1, m = -1;
				while (buffer[i] >= '0' && buffer[i] <= '9') {
					if (n == -1) n = 0;
					n *= 10;
					n += buffer[i] - '0';
					i++;
				}
				if (buffer[i] == ';') {
					i++;
					while (buffer[i] >= '0' && buffer[i] <= '9') {
						if (m == -1) m = 0;
						m *= 10;
						m += buffer[i] - '0';
						i++;
					}
				}
				if (buffer[i] == 'A') { 			// move cursor up
					if (n == -1) n = 1;
					csr_l -= n;
				} else if (buffer[i] == 'B') {		// move cursor down
					if (n == -1) n = 1;
					csr_l += n;
				} else if (buffer[i] == 'C') {		// move cursor forward
					if (n == -1) n = 1;
					csr_c += n;
				} else if (buffer[i] == 'D') {		// move cursor back
					if (n == -1) n = 1;
					csr_c -= n;
				} else if (buffer[i] == 'E') {		// move cursor n lines down, to beginning of line
					if (n == -1) n = 1;
					csr_l += n;
					csr_c = 0;
				} else if (buffer[i] == 'F') {		// move cursor to beginning of line, n lines up
					if (n == -1) n = 1;
					csr_l -= n;
					csr_c = 0;
				} else if (buffer[i] == 'H' || buffer[i] == 'f') {		// set cursor position
					if (n == -1) n = 1;
					if (m == -1) m = 1;
					csr_l = n - 1;
					csr_c = m - 1;
				} else if (buffer[i] == 'J' || buffer[i] == 'K') {		// erase data / erase line
					if (n == -1) n = 0;
					int start = 0, end = 0;
					if (n == 0) {
						start = csr_c; end = w;
					} else if (n == 1) {
						start = 0; end = csr_c;
					} else if (n == 2) {
						start = 0; end = w;
					}
					for (int p = start; p < end; p++) put_at(csr_l, p, ' ');
					if (buffer[i] == 'J') {		// erase data
						if (n == 0) {
							start = csr_l + 1; end = h;
						} else if (n == 1) {
							start = 0; end = csr_l;
						} else if (n == 2) {
							start = 0; end = h;
						}
						for (int l = start; l < end; l++) {
							for (int c = 0; c < w; c++) put_at(l, c, ' ');
						}
					}
				} else if (buffer[i] == 'S') {		// scroll up
					if (n == -1) n = 1;
					scroll(n);
				} else if (buffer[i] == 'T') {		// scroll down
					if (n == -1) n = 1;
					scroll(-n);
				} else if (buffer[i] == 'm') {		// select graphic rendition
					if (n == -1) n = 0;
					if (n == 0) {
						fgcolor = 7;
						bgcolor = 0;
					} else if (n == 1) {
						fgcolor |= TC_MAKE_LIGHT;
					} else if (n == 22) {
						fgcolor &= TC_MAKE_DARK;
					} else if (n >= 30 && n <= 37) {
						fgcolor = (fgcolor & TC_MAKE_LIGHT) | (n - 30);
					} else if (n >= 40 && n <= 47) {
						bgcolor = (n - 40);
					} else {
						// others to do.
					}
				} else if (buffer[i] == 'n') {		// device status report
					kbd_buffer[kbd_buffer_filled++] = 27;
					kbd_buffer[kbd_buffer_filled++] = '[';
					if (csr_l >= 100) kbd_buffer[kbd_buffer_filled++] = ((csr_l / 100) % 10) + '0';
					if (csr_l >= 10) kbd_buffer[kbd_buffer_filled++] = ((csr_l / 10) % 10) + '0';
					kbd_buffer[kbd_buffer_filled++] = (csr_l % 10) + '0';
					kbd_buffer[kbd_buffer_filled++] = ';';
					if (csr_c >= 100) kbd_buffer[kbd_buffer_filled++] = ((csr_c / 100) % 10) + '0';
					if (csr_c >= 10) kbd_buffer[kbd_buffer_filled++] = ((csr_c / 10) % 10) + '0';
					kbd_buffer[kbd_buffer_filled++] = (csr_c % 10) + '0';
					kbd_buffer[kbd_buffer_filled++] = 'R';
				} else if (buffer[i] == 's') {		// save cursor position
					save_csr_l = csr_l;
					save_csr_c = csr_c;
				} else if (buffer[i] == 'p') {		// restore cursor position
					csr_l = save_csr_l;
					csr_c = save_csr_c;
				} else if (buffer[i] == 'e') {		// CUSTOM : enable keyboard echo
					keyboard_echo = true;
				} else if (buffer[i] == 'h') {		// CUSTOM : disable keyboard echo
					keyboard_echo = false;
				} else if (buffer[i] == '?') {
					if (buffer[++i] == '2' && buffer[++i] == '5') {
						if (buffer[++i] == 'l') cursor_visible = false;
						else					cursor_visible = true;
					}
				} else {
				}
				if (csr_l < 0) csr_l = 0;
				if (csr_c < 0) csr_c = 0;
				if (csr_l >= h) csr_l = h - 1;
				if (csr_c >= w) csr_c = w - 1;
				if (output != 0 && output->connected_vt == this) output->text_setcsr(csr_l, csr_c, cursor_visible);
			} else {
					// ignore it.
			}
		} else {
			put(buffer[i]);
		}
	}
	return len;
}

int vt::read(size_t offset, size_t len, char* buffer) {
	// ignore offset

	unsigned c = 0;
	while (true) {
		bool end = false;
		if (kbd_buffer_filled > 0) {
			int r = 0;
			if (keyboard_echo) {
				for (; r < kbd_buffer_filled; r++) {
					if (kbd_buffer[r] == '\b') {
						if (c > 0) {
							c--;
							put('\b');
						}
					} else if (kbd_buffer[r] == '\n') {
						buffer[c++] = '\n';
						c++;
						put('\n');
						end = true;
						break;
					} else if (kbd_buffer[r] >= ' ') {
						buffer[c++] = kbd_buffer[r];
						put(buffer[c]);
					}
					if (c == len) break;
				}
			} else {
				for (; r < kbd_buffer_filled && c < len; r++) {
					buffer[c++] = kbd_buffer[r];
				}
				end = true;
			}
			memcpy(kbd_buffer + r, kbd_buffer, (kbd_buffer_filled - r));
			kbd_buffer_filled -= r;
		}
		if (end || c == len) return c;

		if (kbd_waiter != 0) return c;
		kbd_waiter = current_thread;
		thread_goInactive();
	}
}

size_t vt::get_size() {
	return ((w << 16) + h);
}

int vt::link(node* to, int mode) {
	if (mode == LM_OUTPUT_TO) {
		display *d = to->as_display();
		if (d != 0) {
			outputTo(d);
			return 0;
		}
	}
	return E_NOT_IMPLEMENTED;
}