2012-02-08 12:40:44 +04:00
/* vim: tabstop=4 shiftwidth=4 noexpandtab
*
2012-01-23 10:13:50 +04:00
* Terminal Emulator
2012-07-07 08:08:28 +04:00
*
* Graphical terminal emulator .
*
* Provides a number of features :
* - Windowed and full screen modes
* - Antialiased fonts
* - Built - in fallback bitmap font
* - ANSI escape support
* - 256 colors
2012-01-23 09:36:49 +04:00
*/
# include <stdio.h>
# include <stdint.h>
# include <syscall.h>
# include <string.h>
# include <stdlib.h>
2013-05-06 02:00:24 +04:00
# include <signal.h>
2012-01-23 09:36:49 +04:00
# include <time.h>
# include <unistd.h>
2012-01-31 10:16:09 +04:00
# include <sys/stat.h>
2013-03-19 10:57:40 +04:00
# include <sys/ioctl.h>
2014-04-27 12:37:33 +04:00
# include <sys/wait.h>
2012-03-17 02:09:00 +04:00
# include <getopt.h>
2014-04-20 06:10:44 +04:00
# include <errno.h>
2012-01-23 10:13:50 +04:00
# include <ft2build.h>
# include FT_FREETYPE_H
2012-01-26 02:12:56 +04:00
# include FT_CACHE_H
2012-01-23 10:13:50 +04:00
2012-09-09 03:47:43 +04:00
# include <wchar.h>
2012-09-06 07:19:52 +04:00
# include "lib/utf8decode.h"
2012-01-29 08:27:37 +04:00
2012-02-23 10:36:49 +04:00
# include "lib/graphics.h"
2014-04-16 06:45:56 +04:00
# include "lib/yutani.h"
2012-03-15 00:04:12 +04:00
# include "lib/decorations.h"
2012-04-13 07:42:24 +04:00
# include "lib/pthread.h"
2012-10-15 06:53:16 +04:00
# include "lib/kbd.h"
2012-02-23 10:36:49 +04:00
2012-09-07 06:46:36 +04:00
# include "terminal-palette.h"
# include "terminal-font.h"
2012-09-02 13:24:25 +04:00
2014-03-25 08:08:07 +04:00
# include "gui/terminal/lib/termemu.h"
2012-01-23 09:36:49 +04:00
2014-03-25 08:32:19 +04:00
# define USE_BELL 0
2013-03-19 10:57:40 +04:00
/* master and slave pty descriptors */
2013-03-18 03:34:23 +04:00
static int fd_master , fd_slave ;
2013-04-14 07:21:40 +04:00
static FILE * terminal ;
2012-09-08 07:17:00 +04:00
int scale_fonts = 0 ; /* Whether fonts should be scaled */
float font_scaling = 1.0 ; /* How much they should be scaled by */
uint16_t term_width = 0 ; /* Width of the terminal (in cells) */
uint16_t term_height = 0 ; /* Height of the terminal (in cells) */
uint16_t font_size = 13 ; /* Font size according to Freetype */
uint16_t char_width = 8 ; /* Width of a cell in pixels */
uint16_t char_height = 12 ; /* Height of a cell in pixels */
uint16_t char_offset = 0 ; /* Offset of the font within the cell */
uint16_t csr_x = 0 ; /* Cursor X */
uint16_t csr_y = 0 ; /* Cursor Y */
2014-03-25 08:08:07 +04:00
term_cell_t * term_buffer = NULL ; /* The terminal cell buffer */
2013-03-28 04:42:47 +04:00
uint32_t current_fg = 7 ; /* Current foreground color */
uint32_t current_bg = 0 ; /* Current background color */
2012-09-08 07:17:00 +04:00
uint8_t cursor_on = 1 ; /* Whether or not the cursor should be rendered */
2013-06-28 11:42:40 +04:00
uint8_t _fullscreen = 0 ; /* Whether or not we are running in fullscreen mode (GUI only) */
2012-09-08 10:54:23 +04:00
uint8_t _login_shell = 0 ; /* Whether we're going to display a login shell or not */
2012-09-08 07:17:00 +04:00
uint8_t _use_freetype = 0 ; /* Whether we should use freetype or not XXX seriously, how about some flags */
2012-09-13 09:10:10 +04:00
uint8_t _force_kernel = 0 ;
2012-12-14 07:26:45 +04:00
uint8_t _hold_out = 0 ; /* state indicator on last cell ignore \n */
2012-09-08 07:17:00 +04:00
2014-04-19 06:46:05 +04:00
static volatile int display_lock = 0 ;
2014-04-16 06:45:56 +04:00
yutani_window_t * window = NULL ; /* GUI window */
yutani_t * yctx = NULL ;
2014-03-25 08:32:19 +04:00
term_state_t * ansi_state = NULL ;
2014-04-16 22:18:40 +04:00
int32_t l_x = INT32_MAX ;
int32_t l_y = INT32_MAX ;
2014-04-16 11:59:58 +04:00
int32_t r_x = - 1 ;
int32_t r_y = - 1 ;
2012-09-08 07:17:00 +04:00
void reinit ( ) ; /* Defined way further down */
2013-05-23 07:56:52 +04:00
void term_redraw_cursor ( ) ;
2012-09-08 07:17:00 +04:00
/* Cursor bink timer */
2012-02-03 06:28:26 +04:00
static unsigned int timer_tick = 0 ;
2012-09-08 07:17:00 +04:00
/* Some GUI-only options */
2014-04-16 06:45:56 +04:00
uint32_t window_width = 640 ;
uint32_t window_height = 408 ;
2012-03-17 02:09:00 +04:00
# define TERMINAL_TITLE_SIZE 512
char terminal_title [ TERMINAL_TITLE_SIZE ] ;
size_t terminal_title_length = 0 ;
Context-based graphics library.
All graphics library commands now take a gfx_context_t pointer, which
points to a simple datastructure describing a rendering context (width,
height, depth, total size, front buffer, backbuffer; where backbuffer =
front buffer when not in double-buffering mode, thus we always render to
backbuffer except on a flip). This may have caused a minor speed
reduction, but I don't really care as it's far more important that we
support multiple graphics contexts.
TODO:
- Shared Memory Fonts library (there are a couple of apps that use these
so-called "shmem fonts" on their own; we need a dedicated library for
them)
- Break off "TTK" GUI toolkit into its own library. Since it's just a
callback-based button framework, this shouldn't be too hard right now.
Also, with the previous tick, I'll be able to put labels on controls
and start using text in more places.
2012-04-17 22:21:34 +04:00
gfx_context_t * ctx ;
2012-03-17 02:09:00 +04:00
static void render_decors ( ) ;
2012-12-01 11:28:49 +04:00
void term_clear ( ) ;
2012-03-02 07:13:52 +04:00
2013-04-27 12:14:21 +04:00
void dump_buffer ( ) ;
2013-05-12 00:20:20 +04:00
wchar_t box_chars [ ] = L " ▒␉␌␍␊°±␋┘┐┌└┼⎺⎻─⎼⎽├┤┴┬│≤≥ " ;
2012-09-08 07:17:00 +04:00
/* Trigger to exit the terminal when the child process dies or
* we otherwise receive an exit signal */
2012-04-11 06:55:41 +04:00
volatile int exit_application = 0 ;
2014-04-16 11:59:58 +04:00
static void display_flip ( void ) {
2014-04-16 22:18:40 +04:00
if ( l_x ! = INT32_MAX & & l_y ! = INT32_MAX ) {
2014-04-16 11:59:58 +04:00
yutani_flip_region ( yctx , window , l_x , l_y , r_x - l_x , r_y - l_y ) ;
2014-04-16 22:18:40 +04:00
l_x = INT32_MAX ;
2014-05-11 01:02:49 +04:00
l_y = INT32_MAX ;
r_x = - 1 ;
2014-04-16 11:59:58 +04:00
r_y = - 1 ;
}
}
2014-03-25 08:08:07 +04:00
static void set_term_font_size ( float s ) {
scale_fonts = 1 ;
font_scaling = s ;
reinit ( 1 ) ;
}
2012-12-01 11:28:49 +04:00
2014-04-19 06:46:05 +04:00
static void spin_lock ( int volatile * lock ) {
while ( __sync_lock_test_and_set ( lock , 0x01 ) ) {
syscall_yield ( ) ;
}
}
static void spin_unlock ( int volatile * lock ) {
__sync_lock_release ( lock ) ;
}
2012-09-08 07:17:00 +04:00
/* Returns the lower of two shorts */
2014-05-11 01:02:49 +04:00
int32_t min ( int32_t a , int32_t b ) {
2012-01-23 09:36:49 +04:00
return ( a < b ) ? a : b ;
}
2012-09-08 07:17:00 +04:00
/* Returns the higher of two shorts */
2014-05-11 01:02:49 +04:00
int32_t max ( int32_t a , int32_t b ) {
2012-01-23 09:36:49 +04:00
return ( a > b ) ? a : b ;
}
2012-09-08 07:17:00 +04:00
2014-03-25 08:08:07 +04:00
void set_title ( char * c ) {
int len = min ( TERMINAL_TITLE_SIZE , strlen ( c ) + 1 ) ;
memcpy ( terminal_title , c , len ) ;
terminal_title [ len - 1 ] = ' \0 ' ;
terminal_title_length = len - 1 ;
render_decors ( ) ;
}
2012-01-23 09:36:49 +04:00
2012-09-08 07:17:00 +04:00
/* Stuffs a string into the stdin of the terminal's child process
* Useful for things like the ANSI DSR command . */
void input_buffer_stuff ( char * str ) {
2013-03-21 08:24:55 +04:00
size_t s = strlen ( str ) + 1 ;
2013-03-18 03:34:23 +04:00
write ( fd_master , str , s ) ;
2012-09-08 07:17:00 +04:00
}
2012-02-03 02:16:29 +04:00
2012-03-15 00:04:12 +04:00
static void render_decors ( ) {
2014-04-16 06:45:56 +04:00
/* XXX Make the decorations library support Yutani windows */
2013-06-29 05:51:30 +04:00
if ( ! _fullscreen ) {
2012-03-24 02:44:37 +04:00
if ( terminal_title_length ) {
2014-04-16 08:16:46 +04:00
render_decorations ( window , ctx , terminal_title ) ;
2014-05-04 00:20:16 +04:00
yutani_window_advertise_icon ( yctx , window , terminal_title , " utilities-terminal " ) ;
2012-03-24 02:44:37 +04:00
} else {
2014-04-16 08:16:46 +04:00
render_decorations ( window , ctx , " Terminal " ) ;
2014-05-04 00:20:16 +04:00
yutani_window_advertise_icon ( yctx , window , " Terminal " , " utilities-terminal " ) ;
2012-03-24 02:44:37 +04:00
}
2014-04-16 11:59:58 +04:00
l_x = 0 ; l_y = 0 ;
r_x = window - > width ;
r_y = window - > height ;
display_flip ( ) ;
2012-03-17 02:09:00 +04:00
}
2012-03-15 00:04:12 +04:00
}
2012-09-08 07:17:00 +04:00
static inline void term_set_point ( uint16_t x , uint16_t y , uint32_t color ) {
2013-06-29 05:51:30 +04:00
if ( ! _fullscreen ) {
2012-09-08 07:17:00 +04:00
GFX ( ctx , ( x + decor_left_width ) , ( y + decor_top_height ) ) = color ;
} else {
2013-11-29 04:14:41 +04:00
GFX ( ctx , x , y ) = alpha_blend_rgba ( premultiply ( rgba ( 0 , 0 , 0 , 0xFF ) ) , color ) ;
2012-09-08 07:17:00 +04:00
}
2012-01-23 09:36:49 +04:00
}
2012-09-08 07:17:00 +04:00
/* FreeType text rendering */
2012-01-23 10:13:50 +04:00
FT_Library library ;
FT_Face face ;
2012-01-24 21:01:31 +04:00
FT_Face face_bold ;
2012-01-24 21:53:03 +04:00
FT_Face face_italic ;
FT_Face face_bold_italic ;
2012-01-29 08:27:37 +04:00
FT_Face face_extra ;
2014-04-29 11:29:19 +04:00
FT_Face face_symbol ;
FT_Face * fallbacks [ ] = { & face_symbol , & face_extra , & face_symbol , NULL } ;
2012-01-23 10:13:50 +04:00
2013-03-29 11:34:12 +04:00
2012-01-23 10:13:50 +04:00
void drawChar ( FT_Bitmap * bitmap , int x , int y , uint32_t fg , uint32_t bg ) {
int i , j , p , q ;
int x_max = x + bitmap - > width ;
int y_max = y + bitmap - > rows ;
for ( j = y , q = 0 ; j < y_max ; j + + , q + + ) {
for ( i = x , p = 0 ; i < x_max ; i + + , p + + ) {
2013-03-29 11:39:23 +04:00
uint32_t a = _ALP ( fg ) ;
a = ( a * bitmap - > buffer [ q * bitmap - > width + p ] ) / 255 ;
uint32_t tmp = rgba ( _RED ( fg ) , _GRE ( fg ) , _BLU ( fg ) , a ) ;
2013-03-29 11:34:12 +04:00
term_set_point ( i , j , alpha_blend_rgba ( premultiply ( bg ) , premultiply ( tmp ) ) ) ;
2012-01-23 10:13:50 +04:00
}
}
}
2013-09-02 05:32:46 +04:00
void draw_semi_block ( int c , int x , int y , uint32_t fg , uint32_t bg ) {
int height ;
bg = premultiply ( bg ) ;
fg = premultiply ( fg ) ;
if ( c = = 0x2580 ) {
uint32_t t = bg ;
bg = fg ;
fg = t ;
c = 0x2584 ;
for ( uint8_t i = 0 ; i < char_height ; + + i ) {
for ( uint8_t j = 0 ; j < char_width ; + + j ) {
term_set_point ( x + j , y + i , bg ) ;
}
}
}
c - = 0x2580 ;
height = char_height - ( ( c * char_height ) / 8 ) ;
for ( uint8_t i = height ; i < char_height ; + + i ) {
for ( uint8_t j = 0 ; j < char_width ; + + j ) {
term_set_point ( x + j , y + i , fg ) ;
}
}
}
2014-04-16 06:45:56 +04:00
void focus_callback ( yutani_window_t * yutani_window ) {
2012-11-20 09:24:21 +04:00
render_decors ( ) ;
2013-05-23 07:56:52 +04:00
term_redraw_cursor ( ) ;
2012-11-20 09:24:21 +04:00
}
2012-01-23 09:36:49 +04:00
void
term_write_char (
2012-01-29 08:27:37 +04:00
uint32_t val ,
2012-01-23 09:36:49 +04:00
uint16_t x ,
uint16_t y ,
uint32_t fg ,
2012-01-24 21:01:31 +04:00
uint32_t bg ,
uint8_t flags
2012-01-23 09:36:49 +04:00
) {
2012-01-23 10:13:50 +04:00
2012-12-01 03:50:06 +04:00
uint32_t _fg , _bg ;
2013-06-29 05:51:30 +04:00
if ( fg < PALETTE_COLORS ) {
_fg = term_colors [ fg ] ;
_fg | = 0xFF < < 24 ;
2013-03-28 04:42:47 +04:00
} else {
2013-06-29 05:51:30 +04:00
_fg = fg ;
}
if ( bg < PALETTE_COLORS ) {
_bg = term_colors [ bg ] ;
if ( flags & ANSI_SPECBG ) {
_bg | = 0xFF < < 24 ;
2013-03-27 11:22:15 +04:00
} else {
2014-03-25 08:08:07 +04:00
_bg | = TERM_DEFAULT_OPAC < < 24 ;
2012-09-14 09:12:47 +04:00
}
2013-06-29 05:51:30 +04:00
} else {
_bg = bg ;
}
if ( _use_freetype ) {
if ( val = = 0xFFFF ) { return ; } /* Unicode, do not redraw here */
for ( uint8_t i = 0 ; i < char_height ; + + i ) {
for ( uint8_t j = 0 ; j < char_width ; + + j ) {
term_set_point ( x + j , y + i , premultiply ( _bg ) ) ;
2012-01-23 22:36:59 +04:00
}
}
2013-06-29 05:51:30 +04:00
if ( flags & ANSI_WIDE ) {
2012-09-06 07:42:54 +04:00
for ( uint8_t i = 0 ; i < char_height ; + + i ) {
2013-06-29 05:51:30 +04:00
for ( uint8_t j = char_width ; j < 2 * char_width ; + + j ) {
2013-03-29 11:34:12 +04:00
term_set_point ( x + j , y + i , premultiply ( _bg ) ) ;
2012-09-06 07:42:54 +04:00
}
}
2013-06-29 05:51:30 +04:00
}
if ( val < 32 | | val = = ' ' ) {
goto _extra_stuff ;
}
2013-09-02 05:32:46 +04:00
if ( val > = 0x2580 & & val < = 0x2588 ) {
draw_semi_block ( val , x , y , _fg , _bg ) ;
goto _extra_stuff ;
}
2014-04-29 11:29:19 +04:00
2013-06-29 05:51:30 +04:00
int pen_x = x ;
int pen_y = y + char_offset ;
int error ;
2014-04-29 11:29:19 +04:00
2013-06-29 05:51:30 +04:00
FT_Face * _font = NULL ;
2014-04-29 11:29:19 +04:00
FT_GlyphSlot slot ;
FT_UInt glyph_index ;
2014-03-25 08:08:07 +04:00
if ( flags & ANSI_ALTFONT ) {
2013-06-29 05:51:30 +04:00
_font = & face_extra ;
} else if ( flags & ANSI_BOLD & & flags & ANSI_ITALIC ) {
_font = & face_bold_italic ;
} else if ( flags & ANSI_ITALIC ) {
_font = & face_italic ;
} else if ( flags & ANSI_BOLD ) {
_font = & face_bold ;
} else {
_font = & face ;
}
glyph_index = FT_Get_Char_Index ( * _font , val ) ;
2014-04-29 11:29:19 +04:00
if ( ! glyph_index ) {
int i = 0 ;
while ( ! glyph_index & & fallbacks [ i ] ) {
_font = fallbacks [ i ] ;
glyph_index = FT_Get_Char_Index ( * _font , val ) ;
i + + ;
}
2013-06-29 05:51:30 +04:00
}
error = FT_Load_Glyph ( * _font , glyph_index , FT_LOAD_DEFAULT ) ;
if ( error ) {
fprintf ( terminal , " Error loading glyph: %d \n " , val ) ;
2014-04-29 11:29:19 +04:00
fprintf ( stderr , " Error loading glyph: %d \n " , val ) ;
2013-06-29 05:51:30 +04:00
} ;
slot = ( * _font ) - > glyph ;
if ( slot - > format = = FT_GLYPH_FORMAT_OUTLINE ) {
error = FT_Render_Glyph ( ( * _font ) - > glyph , FT_RENDER_MODE_NORMAL ) ;
2013-03-28 04:42:47 +04:00
if ( error ) {
2014-04-29 11:29:19 +04:00
fprintf ( stderr , " Error rendering glyph: %d \n " , val ) ;
2013-06-29 05:51:30 +04:00
goto _extra_stuff ;
2013-03-28 04:42:47 +04:00
}
2013-06-29 05:51:30 +04:00
}
drawChar ( & slot - > bitmap , pen_x + slot - > bitmap_left , pen_y - slot - > bitmap_top , _fg , _bg ) ;
2012-01-24 21:53:03 +04:00
2013-06-29 05:51:30 +04:00
} else {
if ( val > 128 ) {
val = 4 ;
}
uint8_t * c = number_font [ val ] ;
for ( uint8_t i = 0 ; i < char_height ; + + i ) {
for ( uint8_t j = 0 ; j < char_width ; + + j ) {
if ( c [ i ] & ( 1 < < ( 8 - j ) ) ) {
term_set_point ( x + j , y + i , _fg ) ;
} else {
term_set_point ( x + j , y + i , _bg ) ;
2012-01-23 22:36:59 +04:00
}
}
}
2013-06-29 05:51:30 +04:00
}
2013-05-05 10:22:54 +04:00
_extra_stuff :
2013-06-29 05:51:30 +04:00
if ( flags & ANSI_UNDERLINE ) {
for ( uint8_t i = 0 ; i < char_width ; + + i ) {
term_set_point ( x + i , y + char_offset + 2 , _fg ) ;
2013-05-05 10:22:54 +04:00
}
2013-06-29 05:51:30 +04:00
}
if ( flags & ANSI_CROSS ) {
for ( uint8_t i = 0 ; i < char_width ; + + i ) {
term_set_point ( x + i , y + char_offset - 5 , _fg ) ;
2013-05-05 10:22:54 +04:00
}
2013-06-29 05:51:30 +04:00
}
if ( flags & ANSI_BORDER ) {
for ( uint8_t i = 0 ; i < char_height ; + + i ) {
term_set_point ( x , y + i , _fg ) ;
term_set_point ( x + ( char_width - 1 ) , y + i , _fg ) ;
}
for ( uint8_t j = 0 ; j < char_width ; + + j ) {
term_set_point ( x + j , y , _fg ) ;
term_set_point ( x + j , y + ( char_height - 1 ) , _fg ) ;
2013-05-05 10:22:54 +04:00
}
2012-01-23 09:36:49 +04:00
}
2014-04-16 11:59:58 +04:00
2014-05-11 01:02:49 +04:00
if ( ! _fullscreen ) {
l_x = min ( l_x , decor_left_width + x ) ;
l_y = min ( l_y , decor_top_height + y ) ;
2014-04-16 22:18:40 +04:00
2014-05-11 01:02:49 +04:00
if ( flags & ANSI_WIDE ) {
r_x = max ( r_x , decor_left_width + x + char_width * 2 ) ;
r_y = max ( r_y , decor_top_height + y + char_height * 2 ) ;
} else {
r_x = max ( r_x , decor_left_width + x + char_width ) ;
r_y = max ( r_y , decor_top_height + y + char_height ) ;
}
2014-04-16 11:59:58 +04:00
} else {
2014-05-11 01:02:49 +04:00
l_x = min ( l_x , x ) ;
l_y = min ( l_y , y ) ;
2014-04-16 11:59:58 +04:00
2014-05-11 01:02:49 +04:00
if ( flags & ANSI_WIDE ) {
r_x = max ( r_x , x + char_width * 2 ) ;
r_y = max ( r_y , y + char_height * 2 ) ;
} else {
r_x = max ( r_x , x + char_width ) ;
r_y = max ( r_y , y + char_height ) ;
}
}
2012-01-23 09:36:49 +04:00
}
2014-04-29 11:29:19 +04:00
static void cell_set ( uint16_t x , uint16_t y , uint32_t c , uint32_t fg , uint32_t bg , uint8_t flags ) {
2012-01-29 08:27:37 +04:00
if ( x > = term_width | | y > = term_height ) return ;
2014-03-25 08:08:07 +04:00
term_cell_t * cell = ( term_cell_t * ) ( ( uintptr_t ) term_buffer + ( y * term_width + x ) * sizeof ( term_cell_t ) ) ;
2012-09-06 07:19:52 +04:00
cell - > c = c ;
cell - > fg = fg ;
cell - > bg = bg ;
cell - > flags = flags ;
2012-01-23 09:36:49 +04:00
}
static void cell_redraw ( uint16_t x , uint16_t y ) {
2012-01-29 08:27:37 +04:00
if ( x > = term_width | | y > = term_height ) return ;
2014-03-25 08:08:07 +04:00
term_cell_t * cell = ( term_cell_t * ) ( ( uintptr_t ) term_buffer + ( y * term_width + x ) * sizeof ( term_cell_t ) ) ;
2012-01-25 10:19:52 +04:00
if ( ( ( uint32_t * ) cell ) [ 0 ] = = 0x00000000 ) {
2014-03-25 08:08:07 +04:00
term_write_char ( ' ' , x * char_width , y * char_height , TERM_DEFAULT_FG , TERM_DEFAULT_BG , TERM_DEFAULT_FLAGS ) ;
2012-01-25 10:19:52 +04:00
} else {
2012-09-06 07:19:52 +04:00
term_write_char ( cell - > c , x * char_width , y * char_height , cell - > fg , cell - > bg , cell - > flags ) ;
2012-01-25 10:19:52 +04:00
}
2012-01-23 09:36:49 +04:00
}
2012-01-24 21:15:15 +04:00
static void cell_redraw_inverted ( uint16_t x , uint16_t y ) {
2012-01-29 08:27:37 +04:00
if ( x > = term_width | | y > = term_height ) return ;
2014-03-25 08:08:07 +04:00
term_cell_t * cell = ( term_cell_t * ) ( ( uintptr_t ) term_buffer + ( y * term_width + x ) * sizeof ( term_cell_t ) ) ;
2012-01-24 21:15:15 +04:00
if ( ( ( uint32_t * ) cell ) [ 0 ] = = 0x00000000 ) {
2014-03-25 08:08:07 +04:00
term_write_char ( ' ' , x * char_width , y * char_height , TERM_DEFAULT_BG , TERM_DEFAULT_FG , TERM_DEFAULT_FLAGS | ANSI_SPECBG ) ;
2012-01-24 21:15:15 +04:00
} else {
2013-04-02 08:25:53 +04:00
term_write_char ( cell - > c , x * char_width , y * char_height , cell - > bg , cell - > fg , cell - > flags | ANSI_SPECBG ) ;
2012-01-24 21:15:15 +04:00
}
}
2013-05-05 10:22:54 +04:00
static void cell_redraw_box ( uint16_t x , uint16_t y ) {
if ( x > = term_width | | y > = term_height ) return ;
2014-03-25 08:08:07 +04:00
term_cell_t * cell = ( term_cell_t * ) ( ( uintptr_t ) term_buffer + ( y * term_width + x ) * sizeof ( term_cell_t ) ) ;
2013-05-05 10:22:54 +04:00
if ( ( ( uint32_t * ) cell ) [ 0 ] = = 0x00000000 ) {
2014-03-25 08:08:07 +04:00
term_write_char ( ' ' , x * char_width , y * char_height , TERM_DEFAULT_FG , TERM_DEFAULT_BG , TERM_DEFAULT_FLAGS | ANSI_BORDER ) ;
2013-05-05 10:22:54 +04:00
} else {
term_write_char ( cell - > c , x * char_width , y * char_height , cell - > fg , cell - > bg , cell - > flags | ANSI_BORDER ) ;
}
}
void render_cursor ( ) {
2013-06-29 05:51:30 +04:00
if ( ! window - > focused ) {
2013-05-05 10:22:54 +04:00
cell_redraw_box ( csr_x , csr_y ) ;
} else {
cell_redraw_inverted ( csr_x , csr_y ) ;
}
}
2012-01-23 09:36:49 +04:00
void draw_cursor ( ) {
if ( ! cursor_on ) return ;
2012-02-03 06:28:26 +04:00
timer_tick = 0 ;
2013-05-05 10:22:54 +04:00
render_cursor ( ) ;
2012-01-23 09:36:49 +04:00
}
void term_redraw_all ( ) {
for ( uint16_t y = 0 ; y < term_height ; + + y ) {
for ( uint16_t x = 0 ; x < term_width ; + + x ) {
cell_redraw ( x , y ) ;
}
}
}
2012-12-01 11:28:49 +04:00
void term_scroll ( int how_much ) {
if ( how_much > = term_height | | - how_much > = term_height ) {
term_clear ( ) ;
return ;
}
if ( how_much = = 0 ) {
return ;
}
if ( how_much > 0 ) {
/* Shift terminal cells one row up */
2014-03-25 08:08:07 +04:00
memmove ( term_buffer , ( void * ) ( ( uintptr_t ) term_buffer + sizeof ( term_cell_t ) * term_width ) , sizeof ( term_cell_t ) * term_width * ( term_height - how_much ) ) ;
2012-12-01 11:28:49 +04:00
/* Reset the "new" row to clean cells */
2014-03-25 08:08:07 +04:00
memset ( ( void * ) ( ( uintptr_t ) term_buffer + sizeof ( term_cell_t ) * term_width * ( term_height - how_much ) ) , 0x0 , sizeof ( term_cell_t ) * term_width * how_much ) ;
2013-06-29 05:51:30 +04:00
/* In graphical modes, we will shift the graphics buffer up as necessary */
uintptr_t dst , src ;
size_t siz = char_height * ( term_height - how_much ) * GFX_W ( ctx ) * GFX_B ( ctx ) ;
if ( ! _fullscreen ) {
/* Windowed mode must take borders into account */
dst = ( uintptr_t ) ctx - > backbuffer + ( GFX_W ( ctx ) * decor_top_height ) * GFX_B ( ctx ) ;
src = ( uintptr_t ) ctx - > backbuffer + ( GFX_W ( ctx ) * ( decor_top_height + char_height * how_much ) ) * GFX_B ( ctx ) ;
2012-09-11 10:42:45 +04:00
} else {
2013-06-29 05:51:30 +04:00
/* While fullscreen mode does not */
dst = ( uintptr_t ) ctx - > backbuffer ;
src = ( uintptr_t ) ctx - > backbuffer + ( GFX_W ( ctx ) * char_height * how_much ) * GFX_B ( ctx ) ;
}
/* Perform the shift */
memmove ( ( void * ) dst , ( void * ) src , siz ) ;
/* And redraw the new rows */
for ( int i = 0 ; i < how_much ; + + i ) {
for ( uint16_t x = 0 ; x < term_width ; + + x ) {
cell_redraw ( x , term_height - how_much ) ;
2012-12-01 11:28:49 +04:00
}
2012-09-11 10:42:45 +04:00
}
2012-12-01 11:28:49 +04:00
} else {
how_much = - how_much ;
/* Shift terminal cells one row up */
2014-03-25 08:08:07 +04:00
memmove ( ( void * ) ( ( uintptr_t ) term_buffer + sizeof ( term_cell_t ) * term_width ) , term_buffer , sizeof ( term_cell_t ) * term_width * ( term_height - how_much ) ) ;
2012-12-01 11:28:49 +04:00
/* Reset the "new" row to clean cells */
2014-03-25 08:08:07 +04:00
memset ( term_buffer , 0x0 , sizeof ( term_cell_t ) * term_width * how_much ) ;
2013-06-29 05:51:30 +04:00
uintptr_t dst , src ;
size_t siz = char_height * ( term_height - how_much ) * GFX_W ( ctx ) * GFX_B ( ctx ) ;
if ( ! _fullscreen ) {
src = ( uintptr_t ) ctx - > backbuffer + ( GFX_W ( ctx ) * decor_top_height ) * GFX_B ( ctx ) ;
dst = ( uintptr_t ) ctx - > backbuffer + ( GFX_W ( ctx ) * ( decor_top_height + char_height * how_much ) ) * GFX_B ( ctx ) ;
2012-12-01 11:28:49 +04:00
} else {
2013-06-29 05:51:30 +04:00
/* While fullscreen mode does not */
src = ( uintptr_t ) ctx - > backbuffer ;
dst = ( uintptr_t ) ctx - > backbuffer + ( GFX_W ( ctx ) * char_height * how_much ) * GFX_B ( ctx ) ;
}
/* Perform the shift */
memmove ( ( void * ) dst , ( void * ) src , siz ) ;
/* And redraw the new rows */
for ( int i = 0 ; i < how_much ; + + i ) {
for ( uint16_t x = 0 ; x < term_width ; + + x ) {
cell_redraw ( x , i ) ;
2012-12-01 11:28:49 +04:00
}
2012-01-23 09:36:49 +04:00
}
}
2014-04-20 02:00:26 +04:00
yutani_flip ( yctx , window ) ;
2012-01-23 09:36:49 +04:00
}
2012-09-06 07:19:52 +04:00
int is_wide ( uint32_t codepoint ) {
2012-12-01 11:28:49 +04:00
if ( codepoint < 256 ) return 0 ;
2013-05-12 00:20:20 +04:00
return wcwidth ( codepoint ) = = 2 ;
2012-09-06 07:19:52 +04:00
}
2012-12-10 07:57:04 +04:00
struct scrollback_row {
unsigned short width ;
2014-03-25 08:08:07 +04:00
term_cell_t cells [ ] ;
2012-12-10 07:57:04 +04:00
} ;
# define MAX_SCROLLBACK 10240
list_t * scrollback_list = NULL ;
uint32_t scrollback_offset = 0 ;
void save_scrollback ( ) {
/* Save the current top row for scrollback */
if ( ! scrollback_list ) {
scrollback_list = list_create ( ) ;
}
if ( scrollback_list - > length = = MAX_SCROLLBACK ) {
free ( list_dequeue ( scrollback_list ) ) ;
}
2014-04-21 00:19:52 +04:00
struct scrollback_row * row = malloc ( sizeof ( struct scrollback_row ) + sizeof ( term_cell_t ) * term_width + 20 ) ;
2012-12-10 07:57:04 +04:00
row - > width = term_width ;
for ( int i = 0 ; i < term_width ; + + i ) {
2014-03-25 08:08:07 +04:00
term_cell_t * cell = ( term_cell_t * ) ( ( uintptr_t ) term_buffer + ( i ) * sizeof ( term_cell_t ) ) ;
memcpy ( & row - > cells [ i ] , cell , sizeof ( term_cell_t ) ) ;
2012-12-10 07:57:04 +04:00
}
list_insert ( scrollback_list , row ) ;
}
void redraw_scrollback ( ) {
if ( ! scrollback_offset ) {
term_redraw_all ( ) ;
return ;
}
if ( scrollback_offset < term_height ) {
for ( int i = scrollback_offset ; i < term_height ; i + + ) {
int y = i - scrollback_offset ;
for ( int x = 0 ; x < term_width ; + + x ) {
2014-03-25 08:08:07 +04:00
term_cell_t * cell = ( term_cell_t * ) ( ( uintptr_t ) term_buffer + ( y * term_width + x ) * sizeof ( term_cell_t ) ) ;
2012-12-10 07:57:04 +04:00
if ( ( ( uint32_t * ) cell ) [ 0 ] = = 0x00000000 ) {
2014-03-25 08:08:07 +04:00
term_write_char ( ' ' , x * char_width , i * char_height , TERM_DEFAULT_FG , TERM_DEFAULT_BG , TERM_DEFAULT_FLAGS ) ;
2012-12-10 07:57:04 +04:00
} else {
term_write_char ( cell - > c , x * char_width , i * char_height , cell - > fg , cell - > bg , cell - > flags ) ;
}
}
}
node_t * node = scrollback_list - > tail ;
for ( int i = 0 ; i < scrollback_offset ; + + i ) {
struct scrollback_row * row = ( struct scrollback_row * ) node - > value ;
int y = scrollback_offset - 1 - i ;
int width = row - > width ;
if ( width > term_width ) {
width = term_width ;
} else {
for ( int x = row - > width ; x < term_width ; + + x ) {
2014-03-25 08:08:07 +04:00
term_write_char ( ' ' , x * char_width , y * char_height , TERM_DEFAULT_FG , TERM_DEFAULT_BG , TERM_DEFAULT_FLAGS ) ;
2012-12-10 07:57:04 +04:00
}
}
for ( int x = 0 ; x < width ; + + x ) {
2014-03-25 08:08:07 +04:00
term_cell_t * cell = & row - > cells [ x ] ;
2012-12-10 07:57:04 +04:00
if ( ( ( uint32_t * ) cell ) [ 0 ] = = 0x00000000 ) {
2014-03-25 08:08:07 +04:00
term_write_char ( ' ' , x * char_width , y * char_height , TERM_DEFAULT_FG , TERM_DEFAULT_BG , TERM_DEFAULT_FLAGS ) ;
2012-12-10 07:57:04 +04:00
} else {
term_write_char ( cell - > c , x * char_width , y * char_height , cell - > fg , cell - > bg , cell - > flags ) ;
}
}
node = node - > prev ;
}
} else {
node_t * node = scrollback_list - > tail ;
for ( int i = 0 ; i < scrollback_offset - term_height ; + + i ) {
node = node - > prev ;
}
for ( int i = scrollback_offset - term_height ; i < scrollback_offset ; + + i ) {
struct scrollback_row * row = ( struct scrollback_row * ) node - > value ;
int y = scrollback_offset - 1 - i ;
int width = row - > width ;
if ( width > term_width ) {
width = term_width ;
} else {
for ( int x = row - > width ; x < term_width ; + + x ) {
2014-03-25 08:08:07 +04:00
term_write_char ( ' ' , x * char_width , y * char_height , TERM_DEFAULT_FG , TERM_DEFAULT_BG , TERM_DEFAULT_FLAGS ) ;
2012-12-10 07:57:04 +04:00
}
}
for ( int x = 0 ; x < width ; + + x ) {
2014-03-25 08:08:07 +04:00
term_cell_t * cell = & row - > cells [ x ] ;
2012-12-10 07:57:04 +04:00
if ( ( ( uint32_t * ) cell ) [ 0 ] = = 0x00000000 ) {
2014-03-25 08:08:07 +04:00
term_write_char ( ' ' , x * char_width , y * char_height , TERM_DEFAULT_FG , TERM_DEFAULT_BG , TERM_DEFAULT_FLAGS ) ;
2012-12-10 07:57:04 +04:00
} else {
term_write_char ( cell - > c , x * char_width , y * char_height , cell - > fg , cell - > bg , cell - > flags ) ;
}
}
node = node - > prev ;
}
}
2014-04-20 06:51:11 +04:00
display_flip ( ) ;
2012-12-10 07:57:04 +04:00
}
2012-09-06 07:19:52 +04:00
void term_write ( char c ) {
2014-04-29 11:29:19 +04:00
static uint32_t unicode_state = 0 ;
static uint32_t codepoint = 0 ;
2012-09-06 07:19:52 +04:00
cell_redraw ( csr_x , csr_y ) ;
2014-04-29 11:29:19 +04:00
2012-09-06 07:19:52 +04:00
if ( ! decode ( & unicode_state , & codepoint , ( uint8_t ) c ) ) {
2014-04-29 11:29:19 +04:00
uint32_t o = codepoint ;
codepoint = 0 ;
2013-08-22 05:30:20 +04:00
if ( c = = ' \r ' ) {
csr_x = 0 ;
return ;
}
if ( csr_x = = term_width ) {
csr_x = 0 ;
+ + csr_y ;
}
if ( csr_y = = term_height ) {
save_scrollback ( ) ;
term_scroll ( 1 ) ;
csr_y = term_height - 1 ;
}
2012-09-06 07:19:52 +04:00
if ( c = = ' \n ' ) {
2013-08-22 05:30:20 +04:00
if ( csr_x = = 0 & & _hold_out ) {
_hold_out = 0 ;
return ;
}
2012-09-06 07:19:52 +04:00
+ + csr_y ;
2014-04-29 11:29:19 +04:00
if ( csr_y = = term_height ) {
save_scrollback ( ) ;
term_scroll ( 1 ) ;
csr_y = term_height - 1 ;
}
2012-12-01 11:28:49 +04:00
draw_cursor ( ) ;
} else if ( c = = ' \007 ' ) {
/* bell */
2013-04-03 10:02:54 +04:00
# if USE_BELL
2012-12-01 11:28:49 +04:00
for ( int i = 0 ; i < term_height ; + + i ) {
for ( int j = 0 ; j < term_width ; + + j ) {
cell_redraw_inverted ( j , i ) ;
}
}
2012-12-11 09:04:18 +04:00
syscall_nanosleep ( 0 , 10 ) ;
2012-12-01 11:28:49 +04:00
term_redraw_all ( ) ;
2013-04-03 10:02:54 +04:00
# endif
2012-09-06 07:19:52 +04:00
} else if ( c = = ' \b ' ) {
2012-10-07 23:22:39 +04:00
if ( csr_x > 0 ) {
- - csr_x ;
}
2012-09-06 07:19:52 +04:00
cell_redraw ( csr_x , csr_y ) ;
2012-12-01 11:28:49 +04:00
draw_cursor ( ) ;
2012-09-06 07:19:52 +04:00
} else if ( c = = ' \t ' ) {
2012-12-01 11:28:49 +04:00
csr_x + = ( 8 - csr_x % 8 ) ;
draw_cursor ( ) ;
2012-09-06 07:19:52 +04:00
} else {
2014-04-29 11:29:19 +04:00
int wide = is_wide ( o ) ;
2014-03-25 08:32:19 +04:00
uint8_t flags = ansi_state - > flags ;
2012-09-06 07:19:52 +04:00
if ( wide & & csr_x = = term_width - 1 ) {
csr_x = 0 ;
+ + csr_y ;
}
2012-09-06 07:42:54 +04:00
if ( wide ) {
flags = flags | ANSI_WIDE ;
}
2014-04-29 11:29:19 +04:00
cell_set ( csr_x , csr_y , o , current_fg , current_bg , flags ) ;
2012-09-06 07:19:52 +04:00
cell_redraw ( csr_x , csr_y ) ;
csr_x + + ;
if ( wide & & csr_x ! = term_width ) {
2014-03-25 08:32:19 +04:00
cell_set ( csr_x , csr_y , 0xFFFF , current_fg , current_bg , ansi_state - > flags ) ;
2012-09-06 07:19:52 +04:00
cell_redraw ( csr_x , csr_y ) ;
cell_redraw ( csr_x - 1 , csr_y ) ;
csr_x + + ;
}
}
} else if ( unicode_state = = UTF8_REJECT ) {
unicode_state = 0 ;
2014-04-29 11:29:19 +04:00
codepoint = 0 ;
2012-01-23 09:36:49 +04:00
}
draw_cursor ( ) ;
}
void
term_set_csr ( int x , int y ) {
cell_redraw ( csr_x , csr_y ) ;
csr_x = x ;
csr_y = y ;
2012-12-01 11:28:49 +04:00
draw_cursor ( ) ;
2012-01-23 09:36:49 +04:00
}
int
term_get_csr_x ( ) {
return csr_x ;
}
int
term_get_csr_y ( ) {
return csr_y ;
}
void
term_set_csr_show ( uint8_t on ) {
cursor_on = on ;
}
2013-03-28 04:42:47 +04:00
void term_set_colors ( uint32_t fg , uint32_t bg ) {
2012-01-23 09:36:49 +04:00
current_fg = fg ;
current_bg = bg ;
}
void term_redraw_cursor ( ) {
if ( term_buffer ) {
draw_cursor ( ) ;
}
}
2012-02-03 06:28:26 +04:00
void flip_cursor ( ) {
static uint8_t cursor_flipped = 0 ;
2014-04-20 06:51:11 +04:00
if ( scrollback_offset ! = 0 ) {
return ; /* Don't flip cursor while drawing scrollback */
}
2012-02-03 06:28:26 +04:00
if ( cursor_flipped ) {
cell_redraw ( csr_x , csr_y ) ;
} else {
2013-05-05 10:22:54 +04:00
render_cursor ( ) ;
2012-02-03 06:28:26 +04:00
}
2014-04-16 11:59:58 +04:00
display_flip ( ) ;
2012-02-03 06:28:26 +04:00
cursor_flipped = 1 - cursor_flipped ;
}
2014-04-29 11:29:19 +04:00
void term_set_cell ( int x , int y , uint32_t c ) {
2014-03-25 08:32:19 +04:00
cell_set ( x , y , c , current_fg , current_bg , ansi_state - > flags ) ;
2012-01-23 09:36:49 +04:00
cell_redraw ( x , y ) ;
}
void term_redraw_cell ( int x , int y ) {
if ( x < 0 | | y < 0 | | x > = term_width | | y > = term_height ) return ;
cell_redraw ( x , y ) ;
}
2012-12-01 11:28:49 +04:00
void term_clear ( int i ) {
2012-11-29 11:05:19 +04:00
if ( i = = 2 ) {
/* Oh dear */
csr_x = 0 ;
csr_y = 0 ;
2014-03-25 08:08:07 +04:00
memset ( ( void * ) term_buffer , 0x00 , term_width * term_height * sizeof ( term_cell_t ) ) ;
2013-06-29 05:51:30 +04:00
if ( ! _fullscreen ) {
2012-11-29 11:05:19 +04:00
render_decors ( ) ;
}
term_redraw_all ( ) ;
} else if ( i = = 0 ) {
for ( int x = csr_x ; x < term_width ; + + x ) {
term_set_cell ( x , csr_y , ' ' ) ;
}
for ( int y = csr_y + 1 ; y < term_height ; + + y ) {
for ( int x = 0 ; x < term_width ; + + x ) {
term_set_cell ( x , y , ' ' ) ;
}
}
} else if ( i = = 1 ) {
for ( int y = 0 ; y < csr_y ; + + y ) {
for ( int x = 0 ; x < term_width ; + + x ) {
term_set_cell ( x , y , ' ' ) ;
}
}
for ( int x = 0 ; x < csr_x ; + + x ) {
term_set_cell ( x , csr_y , ' ' ) ;
}
2012-03-15 00:04:12 +04:00
}
2012-01-23 09:36:49 +04:00
}
2012-02-26 07:28:33 +04:00
char * loadMemFont ( char * name , char * ident , size_t * size ) {
2013-06-28 11:42:40 +04:00
size_t s = 0 ;
int error ;
char * font = ( char * ) syscall_shm_obtain ( ident , & s ) ;
* size = s ;
return font ;
2012-01-26 02:12:56 +04:00
}
2012-01-31 11:25:17 +04:00
# define INPUT_SIZE 1024
char input_buffer [ INPUT_SIZE ] ;
int input_collected = 0 ;
void clear_input ( ) {
memset ( input_buffer , 0x0 , INPUT_SIZE ) ;
input_collected = 0 ;
}
2012-02-08 12:40:44 +04:00
uint32_t child_pid = 0 ;
2012-09-08 10:54:23 +04:00
void handle_input ( char c ) {
2014-04-19 06:46:05 +04:00
spin_lock ( & display_lock ) ;
2013-07-27 11:18:29 +04:00
write ( fd_master , & c , 1 ) ;
2014-04-16 11:59:58 +04:00
display_flip ( ) ;
2014-04-19 06:46:05 +04:00
spin_unlock ( & display_lock ) ;
2013-04-27 12:14:21 +04:00
}
2012-10-15 06:53:16 +04:00
void handle_input_s ( char * c ) {
2014-04-19 06:46:05 +04:00
spin_lock ( & display_lock ) ;
2013-07-27 11:18:29 +04:00
write ( fd_master , c , strlen ( c ) ) ;
2014-04-16 11:59:58 +04:00
display_flip ( ) ;
2014-04-19 06:46:05 +04:00
spin_unlock ( & display_lock ) ;
2012-10-15 06:53:16 +04:00
}
void key_event ( int ret , key_event_t * event ) {
if ( ret ) {
2013-06-16 02:23:10 +04:00
if ( event - > modifiers & KEY_MOD_LEFT_ALT | | event - > modifiers & KEY_MOD_RIGHT_ALT ) {
handle_input ( ' \033 ' ) ;
}
2012-10-15 06:53:16 +04:00
handle_input ( event - > key ) ;
} else {
if ( event - > action = = KEY_ACTION_UP ) return ;
switch ( event - > keycode ) {
2013-04-14 07:21:40 +04:00
case KEY_F1 :
handle_input_s ( " \033 OP " ) ;
break ;
case KEY_F2 :
handle_input_s ( " \033 OQ " ) ;
break ;
case KEY_F3 :
handle_input_s ( " \033 OR " ) ;
break ;
case KEY_F4 :
handle_input_s ( " \033 OS " ) ;
break ;
case KEY_F5 :
handle_input_s ( " \033 [15~ " ) ;
break ;
case KEY_F6 :
handle_input_s ( " \033 [17~ " ) ;
break ;
case KEY_F7 :
handle_input_s ( " \033 [18~ " ) ;
break ;
case KEY_F8 :
handle_input_s ( " \033 [19~ " ) ;
break ;
case KEY_F9 :
handle_input_s ( " \033 [20~ " ) ;
break ;
case KEY_F10 :
handle_input_s ( " \033 [21~ " ) ;
break ;
case KEY_F11 :
handle_input_s ( " \033 [23~ " ) ;
break ;
case KEY_F12 :
2013-06-28 11:42:40 +04:00
/* XXX This is for testing only */
handle_input_s ( " テスト " ) ;
//handle_input_s("\033[24~");
2013-04-14 07:21:40 +04:00
break ;
2012-10-15 06:53:16 +04:00
case KEY_ARROW_UP :
handle_input_s ( " \033 [A " ) ;
break ;
case KEY_ARROW_DOWN :
handle_input_s ( " \033 [B " ) ;
break ;
case KEY_ARROW_RIGHT :
handle_input_s ( " \033 [C " ) ;
break ;
case KEY_ARROW_LEFT :
handle_input_s ( " \033 [D " ) ;
break ;
2012-12-10 07:57:04 +04:00
case KEY_PAGE_UP :
if ( event - > modifiers & KEY_MOD_LEFT_SHIFT ) {
int i = 0 ;
while ( i < 5 & & scrollback_list & & scrollback_offset < scrollback_list - > length ) {
scrollback_offset + + ;
i + + ;
}
redraw_scrollback ( ) ;
2013-04-14 07:21:40 +04:00
} else {
handle_input_s ( " \033 [5~ " ) ;
2012-12-10 07:57:04 +04:00
}
break ;
case KEY_PAGE_DOWN :
if ( event - > modifiers & KEY_MOD_LEFT_SHIFT ) {
int i = 0 ;
while ( i < 5 & & scrollback_list & & scrollback_offset ! = 0 ) {
scrollback_offset - = 1 ;
i + + ;
}
redraw_scrollback ( ) ;
2013-04-14 07:21:40 +04:00
} else {
handle_input_s ( " \033 [6~ " ) ;
2012-12-10 07:57:04 +04:00
}
break ;
2012-10-15 06:53:16 +04:00
}
}
}
2012-04-11 06:55:41 +04:00
void * wait_for_exit ( void * garbage ) {
2014-04-29 11:28:41 +04:00
int pid ;
do {
pid = waitpid ( - 1 , NULL , 0 ) ;
} while ( pid = = - 1 & & errno = = EINTR ) ;
2012-04-11 06:55:41 +04:00
/* Clean up */
exit_application = 1 ;
/* Exit */
2013-12-03 09:05:30 +04:00
char exit_message [ ] = " [Process terminated] \n " ;
write ( fd_slave , exit_message , sizeof ( exit_message ) ) ;
2012-04-11 06:55:41 +04:00
}
2012-03-17 02:09:00 +04:00
void usage ( char * argv [ ] ) {
printf (
" Terminal Emulator \n "
" \n "
" usage: %s [-b] [-F] [-h] \n "
" \n "
2013-06-28 11:42:40 +04:00
" -F --fullscreen \033 [3mRun in fullscreen (background) mode. \033 [0m \n "
2012-03-17 02:09:00 +04:00
" -b --bitmap \033 [3mUse the integrated bitmap font. \033 [0m \n "
" -h --help \033 [3mShow this help message. \033 [0m \n "
2012-09-08 07:17:00 +04:00
" -s --scale \033 [3mScale the font in FreeType mode by a given amount. \033 [0m \n "
2012-03-17 02:09:00 +04:00
" \n "
" This terminal emulator provides basic support for VT220 escapes and \n "
" XTerm extensions, including 256 color support and font effects. \n " ,
argv [ 0 ] ) ;
}
2014-03-25 08:32:19 +04:00
term_callbacks_t term_callbacks = {
/* writer*/
& term_write ,
/* set_color*/
term_set_colors ,
/* set_csr*/
term_set_csr ,
/* get_csr_x*/
term_get_csr_x ,
/* get_csr_y*/
term_get_csr_y ,
/* set_cell*/
term_set_cell ,
/* cls*/
term_clear ,
/* scroll*/
term_scroll ,
/* redraw_cursor*/
term_redraw_cursor ,
/* input_buffer_stuff*/
input_buffer_stuff ,
/* set_font_size*/
set_term_font_size ,
/* set_title*/
set_title ,
} ;
2013-05-06 02:00:24 +04:00
void reinit ( int send_sig ) {
2012-09-08 07:17:00 +04:00
if ( _use_freetype ) {
/* Reset font sizes */
font_size = 13 ;
char_height = 17 ;
char_width = 8 ;
char_offset = 13 ;
if ( scale_fonts ) {
/* Recalculate scaling */
font_size * = font_scaling ;
char_height * = font_scaling ;
char_width * = font_scaling ;
char_offset * = font_scaling ;
}
/* Initialize the freetype font pixel sizes */
FT_Set_Pixel_Sizes ( face , font_size , font_size ) ;
FT_Set_Pixel_Sizes ( face_bold , font_size , font_size ) ;
FT_Set_Pixel_Sizes ( face_italic , font_size , font_size ) ;
FT_Set_Pixel_Sizes ( face_bold_italic , font_size , font_size ) ;
FT_Set_Pixel_Sizes ( face_extra , font_size , font_size ) ;
2014-04-29 11:29:19 +04:00
FT_Set_Pixel_Sizes ( face_symbol , font_size , font_size ) ;
2012-09-08 07:17:00 +04:00
}
2014-04-19 06:46:05 +04:00
int i = 0 ;
2012-09-08 07:17:00 +04:00
2013-03-23 09:08:21 +04:00
int old_width = term_width ;
int old_height = term_height ;
2013-06-29 05:51:30 +04:00
term_width = window_width / char_width ;
term_height = window_height / char_height ;
2012-09-08 07:17:00 +04:00
if ( term_buffer ) {
2014-04-20 06:10:44 +04:00
fprintf ( stderr , " reinitalizing... \n " ) ;
2014-04-21 00:19:52 +04:00
term_cell_t * new_term_buffer = malloc ( sizeof ( term_cell_t ) * term_width * term_height ) ;
2014-04-20 06:10:44 +04:00
fprintf ( stderr , " new buffer is %x \n " , new_term_buffer ) ;
2014-04-19 06:46:05 +04:00
2014-03-25 08:08:07 +04:00
memset ( new_term_buffer , 0x0 , sizeof ( term_cell_t ) * term_width * term_height ) ;
2013-03-23 09:08:21 +04:00
for ( int row = 0 ; row < min ( old_height , term_height ) ; + + row ) {
for ( int col = 0 ; col < min ( old_width , term_width ) ; + + col ) {
2014-03-25 08:08:07 +04:00
term_cell_t * old_cell = ( term_cell_t * ) ( ( uintptr_t ) term_buffer + ( row * old_width + col ) * sizeof ( term_cell_t ) ) ;
term_cell_t * new_cell = ( term_cell_t * ) ( ( uintptr_t ) new_term_buffer + ( row * term_width + col ) * sizeof ( term_cell_t ) ) ;
2013-03-23 09:08:21 +04:00
* new_cell = * old_cell ;
}
}
free ( term_buffer ) ;
term_buffer = new_term_buffer ;
2012-12-14 07:45:39 +04:00
} else {
2014-04-21 00:19:52 +04:00
term_buffer = malloc ( sizeof ( term_cell_t ) * term_width * term_height ) ;
2014-03-25 08:08:07 +04:00
memset ( term_buffer , 0x0 , sizeof ( term_cell_t ) * term_width * term_height ) ;
2012-09-08 07:17:00 +04:00
}
2014-03-25 08:32:19 +04:00
ansi_state = ansi_init ( ansi_state , term_width , term_height , & term_callbacks ) ;
2012-09-08 07:17:00 +04:00
2014-03-25 08:08:07 +04:00
draw_fill ( ctx , rgba ( 0 , 0 , 0 , TERM_DEFAULT_OPAC ) ) ;
2013-06-29 05:51:30 +04:00
render_decors ( ) ;
2013-05-23 08:59:28 +04:00
term_redraw_all ( ) ;
2012-10-14 00:02:58 +04:00
2013-03-19 10:57:40 +04:00
struct winsize w ;
w . ws_row = term_height ;
w . ws_col = term_width ;
ioctl ( fd_master , TIOCSWINSZ , & w ) ;
2013-05-06 02:00:24 +04:00
if ( send_sig ) {
kill ( child_pid , SIGWINCH ) ;
}
2012-09-08 07:17:00 +04:00
}
2012-02-23 10:36:49 +04:00
2014-04-19 06:46:05 +04:00
static void resize_finish ( int width , int height ) {
2014-04-19 07:07:46 +04:00
static int resize_attempts = 0 ;
int t_window_width = width - decor_left_width - decor_right_width ;
int t_window_height = height - decor_top_height - decor_bottom_height ;
if ( t_window_width < char_width * 20 | | t_window_height < char_height * 10 ) {
resize_attempts + + ;
int n_width = decor_left_width + decor_right_width + max ( char_width * 20 , t_window_width ) ;
int n_height = decor_top_height + decor_bottom_height + max ( char_height * 10 , t_window_height ) ;
yutani_window_resize_offer ( yctx , window , n_width , n_height ) ;
return ;
}
if ( t_window_width % char_width ! = 0 | | t_window_height % char_height ! = 0 & & resize_attempts < 3 ) {
resize_attempts + + ;
fprintf ( stderr , " Rejecting dimensions, rounding to %d x %d (+decors) \n " , t_window_width - ( char_width - ( t_window_width % char_width ) ) , t_window_height - ( char_height - ( t_window_height % char_height ) ) ) ;
int n_width = decor_left_width + decor_right_width + t_window_width - ( t_window_width % char_width ) ;
int n_height = decor_top_height + decor_bottom_height + t_window_height - ( t_window_height % char_height ) ;
yutani_window_resize_offer ( yctx , window , n_width , n_height ) ;
return ;
}
resize_attempts = 0 ;
2014-04-19 06:46:05 +04:00
yutani_window_resize_accept ( yctx , window , width , height ) ;
window_width = window - > width - decor_left_width - decor_right_width ;
window_height = window - > height - decor_top_height - decor_bottom_height ;
spin_lock ( & display_lock ) ;
reinit_graphics_yutani ( ctx , window ) ;
reinit ( 1 ) ;
spin_unlock ( & display_lock ) ;
yutani_window_resize_done ( yctx , window ) ;
yutani_flip ( yctx , window ) ;
}
2012-12-10 11:07:04 +04:00
void * handle_incoming ( void * garbage ) {
while ( ! exit_application ) {
2014-04-16 06:45:56 +04:00
yutani_msg_t * m = yutani_poll ( yctx ) ;
if ( m ) {
switch ( m - > type ) {
case YUTANI_MSG_KEY_EVENT :
{
struct yutani_msg_key_event * ke = ( void * ) m - > data ;
int ret = ( ke - > event . action = = KEY_ACTION_DOWN ) & & ( ke - > event . key ) ;
key_event ( ret , & ke - > event ) ;
}
break ;
case YUTANI_MSG_WINDOW_FOCUS_CHANGE :
{
struct yutani_msg_window_focus_change * wf = ( void * ) m - > data ;
yutani_window_t * win = hashmap_get ( yctx - > windows , ( void * ) wf - > wid ) ;
if ( win ) {
win - > focused = wf - > focused ;
render_decors ( ) ;
}
}
break ;
2014-04-19 06:23:45 +04:00
case YUTANI_MSG_SESSION_END :
{
kill ( child_pid , SIGKILL ) ;
2014-04-27 12:37:33 +04:00
exit_application = 1 ;
2014-04-19 06:23:45 +04:00
}
break ;
2014-04-19 06:46:05 +04:00
case YUTANI_MSG_RESIZE_OFFER :
{
struct yutani_msg_window_resize * wr = ( void * ) m - > data ;
resize_finish ( wr - > width , wr - > height ) ;
}
break ;
2014-05-18 22:54:20 +04:00
case YUTANI_MSG_WINDOW_MOUSE_EVENT :
{
struct yutani_msg_window_mouse_event * me = ( void * ) m - > data ;
if ( me - > command = = YUTANI_MOUSE_EVENT_DOWN & & me - > buttons & YUTANI_MOUSE_BUTTON_LEFT ) {
if ( ! _fullscreen ) {
if ( me - > new_y < decor_top_height ) {
yutani_window_drag_start ( yctx , window ) ;
}
}
}
}
break ;
2014-04-16 06:45:56 +04:00
default :
break ;
}
free ( m ) ;
2012-12-10 11:07:04 +04:00
}
}
pthread_exit ( 0 ) ;
}
2013-07-20 13:04:58 +04:00
void * blink_cursor ( void * garbage ) {
while ( ! exit_application ) {
timer_tick + + ;
if ( timer_tick = = 3 ) {
timer_tick = 0 ;
2014-04-19 06:46:05 +04:00
spin_lock ( & display_lock ) ;
2013-07-20 13:04:58 +04:00
flip_cursor ( ) ;
2014-04-19 06:46:05 +04:00
spin_unlock ( & display_lock ) ;
2013-07-20 13:04:58 +04:00
}
usleep ( 90000 ) ;
}
pthread_exit ( 0 ) ;
}
2012-01-23 09:36:49 +04:00
int main ( int argc , char * * argv ) {
2012-03-17 02:09:00 +04:00
_use_freetype = 1 ;
2012-04-12 00:24:24 +04:00
_login_shell = 0 ;
2012-03-17 02:09:00 +04:00
static struct option long_opts [ ] = {
2012-09-08 07:17:00 +04:00
{ " fullscreen " , no_argument , 0 , ' F ' } ,
{ " bitmap " , no_argument , 0 , ' b ' } ,
{ " login " , no_argument , 0 , ' l ' } ,
{ " help " , no_argument , 0 , ' h ' } ,
2012-09-13 09:10:10 +04:00
{ " kernel " , no_argument , 0 , ' k ' } ,
2012-09-08 07:17:00 +04:00
{ " scale " , required_argument , 0 , ' s ' } ,
{ " geometry " , required_argument , 0 , ' g ' } ,
2012-03-17 02:09:00 +04:00
{ 0 , 0 , 0 , 0 }
} ;
/* Read some arguments */
int index , c ;
2013-06-29 05:51:30 +04:00
while ( ( c = getopt_long ( argc , argv , " bhFlks:g: " , long_opts , & index ) ) ! = - 1 ) {
2012-03-17 02:09:00 +04:00
if ( ! c ) {
if ( long_opts [ index ] . flag = = 0 ) {
c = long_opts [ index ] . val ;
2012-01-23 09:36:49 +04:00
}
}
2012-03-17 02:09:00 +04:00
switch ( c ) {
2012-09-13 09:10:10 +04:00
case ' k ' :
_force_kernel = 1 ;
break ;
2012-04-12 00:24:24 +04:00
case ' l ' :
_login_shell = 1 ;
break ;
2012-03-17 02:09:00 +04:00
case ' F ' :
2013-06-28 11:42:40 +04:00
_fullscreen = 1 ;
2012-03-17 02:09:00 +04:00
break ;
case ' b ' :
_use_freetype = 0 ;
break ;
case ' h ' :
usage ( argv ) ;
return 0 ;
break ;
2012-09-08 07:17:00 +04:00
case ' s ' :
scale_fonts = 1 ;
font_scaling = atof ( optarg ) ;
break ;
case ' g ' :
{
char * c = strstr ( optarg , " x " ) ;
if ( c ) {
* c = ' \0 ' ;
c + + ;
2012-09-13 07:10:48 +04:00
window_width = atoi ( optarg ) ;
window_height = atoi ( c ) ;
2012-09-08 07:17:00 +04:00
}
}
break ;
2012-03-17 02:09:00 +04:00
case ' ? ' :
break ;
default :
break ;
2012-02-23 11:30:56 +04:00
}
}
2012-01-23 09:36:49 +04:00
2013-06-29 05:51:30 +04:00
putenv ( " TERM=toaru " ) ;
2012-02-23 11:30:56 +04:00
2013-06-29 05:51:30 +04:00
/* Initialize the windowing library */
2014-04-16 06:45:56 +04:00
yctx = yutani_init ( ) ;
2012-03-15 00:04:12 +04:00
2013-06-29 05:51:30 +04:00
if ( _fullscreen ) {
2014-04-16 06:45:56 +04:00
window_width = yctx - > display_width ;
window_height = yctx - > display_height ;
window = yutani_window_create ( yctx , window_width , window_height ) ;
yutani_set_stack ( yctx , window , YUTANI_ZORDER_BOTTOM ) ;
2013-06-29 05:51:30 +04:00
window - > focused = 1 ;
2012-02-23 10:36:49 +04:00
} else {
2013-06-29 05:51:30 +04:00
/* Create the window */
2014-04-16 06:45:56 +04:00
window = yutani_window_create ( yctx , window_width + decor_left_width + decor_right_width , window_height + decor_top_height + decor_bottom_height ) ;
2014-04-25 09:36:20 +04:00
window - > focused = 0 ;
2013-06-29 05:51:30 +04:00
/* Initialize the decoration library */
init_decorations ( ) ;
2012-02-23 10:36:49 +04:00
}
2013-06-29 05:51:30 +04:00
/* Initialize the graphics context */
2014-04-16 06:45:56 +04:00
ctx = init_graphics_yutani ( window ) ;
2013-06-29 05:51:30 +04:00
/* Clear to black */
2014-04-16 06:45:56 +04:00
draw_fill ( ctx , rgba ( 0 , 0 , 0 , 0 ) ) ;
yutani_window_move ( yctx , window , yctx - > display_width / 2 - window - > width / 2 , yctx - > display_height / 2 - window - > height / 2 ) ;
2013-06-29 05:51:30 +04:00
2012-01-23 22:36:59 +04:00
if ( _use_freetype ) {
int error ;
error = FT_Init_FreeType ( & library ) ;
if ( error ) return 1 ;
2012-01-26 02:12:56 +04:00
char * font = NULL ;
size_t s ;
2014-04-29 11:29:19 +04:00
/* XXX Use shmemfont library */
2014-04-16 08:16:46 +04:00
font = loadMemFont ( " /usr/share/fonts/DejaVuSansMono.ttf " , YUTANI_SERVER_IDENTIFIER " .fonts.monospace " , & s ) ;
2012-01-26 02:12:56 +04:00
error = FT_New_Memory_Face ( library , font , s , 0 , & face ) ; if ( error ) return 1 ;
2014-04-16 08:16:46 +04:00
font = loadMemFont ( " /usr/share/fonts/DejaVuSansMono-Bold.ttf " , YUTANI_SERVER_IDENTIFIER " .fonts.monospace.bold " , & s ) ;
2012-01-26 02:12:56 +04:00
error = FT_New_Memory_Face ( library , font , s , 0 , & face_bold ) ; if ( error ) return 1 ;
2014-04-16 08:16:46 +04:00
font = loadMemFont ( " /usr/share/fonts/DejaVuSansMono-Oblique.ttf " , YUTANI_SERVER_IDENTIFIER " .fonts.monospace.italic " , & s ) ;
2012-01-26 02:12:56 +04:00
error = FT_New_Memory_Face ( library , font , s , 0 , & face_italic ) ; if ( error ) return 1 ;
2014-04-16 08:16:46 +04:00
font = loadMemFont ( " /usr/share/fonts/DejaVuSansMono-BoldOblique.ttf " , YUTANI_SERVER_IDENTIFIER " .fonts.monospace.bolditalic " , & s ) ;
2012-01-26 02:12:56 +04:00
error = FT_New_Memory_Face ( library , font , s , 0 , & face_bold_italic ) ; if ( error ) return 1 ;
2012-01-24 08:26:13 +04:00
2012-01-29 08:27:37 +04:00
error = FT_New_Face ( library , " /usr/share/fonts/VLGothic.ttf " , 0 , & face_extra ) ;
2014-04-29 11:29:19 +04:00
error = FT_New_Face ( library , " /usr/share/fonts/Symbola.ttf " , 0 , & face_symbol ) ;
2012-01-23 22:36:59 +04:00
}
2012-01-23 10:13:50 +04:00
2013-03-18 03:34:23 +04:00
syscall_openpty ( & fd_master , & fd_slave , NULL , NULL , NULL ) ;
2013-04-14 07:21:40 +04:00
terminal = fdopen ( fd_slave , " w " ) ;
2013-05-06 02:00:24 +04:00
reinit ( 0 ) ;
2012-01-25 10:19:52 +04:00
2013-04-14 07:59:36 +04:00
fflush ( stdin ) ;
2012-01-25 05:06:07 +04:00
int pid = getpid ( ) ;
uint32_t f = fork ( ) ;
2012-01-25 10:19:52 +04:00
if ( getpid ( ) ! = pid ) {
2014-05-12 04:40:16 +04:00
dup2 ( fd_slave , 0 ) ;
dup2 ( fd_slave , 1 ) ;
dup2 ( fd_slave , 2 ) ;
2012-09-02 13:24:25 +04:00
if ( argv [ optind ] ! = NULL ) {
char * tokens [ ] = { argv [ optind ] , NULL } ;
2012-10-08 11:17:50 +04:00
int i = execvp ( tokens [ 0 ] , tokens ) ;
2014-04-06 02:25:34 +04:00
fprintf ( stderr , " Failed to launch requested startup application. \n " ) ;
2012-04-12 00:24:24 +04:00
} else {
2012-09-02 13:24:25 +04:00
/*
* TODO : Check the public - readable passwd file to select which shell to run
*/
if ( _login_shell ) {
char * tokens [ ] = { " /bin/login " , NULL } ;
2012-10-08 11:17:50 +04:00
int i = execvp ( tokens [ 0 ] , tokens ) ;
2012-09-02 13:24:25 +04:00
} else {
2013-03-28 11:12:48 +04:00
char * tokens [ ] = { " /bin/sh " , NULL } ;
2012-10-08 11:17:50 +04:00
int i = execvp ( tokens [ 0 ] , tokens ) ;
2012-09-02 13:24:25 +04:00
}
2012-04-12 00:24:24 +04:00
}
2012-04-11 06:55:41 +04:00
exit_application = 1 ;
return 1 ;
2012-01-25 05:06:07 +04:00
} else {
2012-02-08 12:40:44 +04:00
2013-06-29 05:51:30 +04:00
if ( _force_kernel ) {
2012-09-04 09:35:11 +04:00
/* Request kernel output to this terminal */
2013-03-18 11:52:12 +04:00
//syscall_system_function(4, (char **)fd_slave);
2012-09-04 09:35:11 +04:00
}
2012-02-08 12:40:44 +04:00
child_pid = f ;
2012-04-11 06:55:41 +04:00
pthread_t wait_for_exit_thread ;
pthread_create ( & wait_for_exit_thread , NULL , wait_for_exit , NULL ) ;
2012-12-10 11:07:04 +04:00
pthread_t handle_incoming_thread ;
pthread_create ( & handle_incoming_thread , NULL , handle_incoming , NULL ) ;
2013-07-20 13:04:58 +04:00
pthread_t cursor_blink_thread ;
pthread_create ( & cursor_blink_thread , NULL , blink_cursor , NULL ) ;
2012-10-15 06:53:16 +04:00
unsigned char buf [ 1024 ] ;
2012-12-10 11:07:04 +04:00
while ( ! exit_application ) {
2013-07-20 13:04:58 +04:00
int r = read ( fd_master , buf , 1024 ) ;
2014-04-19 06:46:05 +04:00
spin_lock ( & display_lock ) ;
2013-07-20 13:04:58 +04:00
for ( uint32_t i = 0 ; i < r ; + + i ) {
2014-03-25 08:32:19 +04:00
ansi_put ( ansi_state , buf [ i ] ) ;
2012-01-25 05:06:07 +04:00
}
2014-04-16 11:59:58 +04:00
display_flip ( ) ;
2014-04-19 06:46:05 +04:00
spin_unlock ( & display_lock ) ;
2012-01-25 05:06:07 +04:00
}
2012-04-11 06:55:41 +04:00
}
2014-04-16 06:45:56 +04:00
yutani_close ( yctx , window ) ;
2012-01-25 05:06:07 +04:00
2012-01-23 09:36:49 +04:00
return 0 ;
}