2020-01-18 08:38:21 +00:00
/*
* Copyright ( c ) 2018 - 2020 , Andreas Kling < kling @ serenityos . org >
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions are met :
*
* 1. Redistributions of source code must retain the above copyright notice , this
* list of conditions and the following disclaimer .
*
* 2. Redistributions in binary form must reproduce the above copyright notice ,
* this list of conditions and the following disclaimer in the documentation
* and / or other materials provided with the distribution .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS " AS IS "
* AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL
* DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY ,
* OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*/
2020-02-06 14:04:03 +00:00
# include <LibCore/ArgsParser.h>
2020-02-06 19:33:02 +00:00
# include <LibGUI/AboutDialog.h>
# include <LibGUI/Action.h>
# include <LibGUI/ActionGroup.h>
# include <LibGUI/Application.h>
# include <LibGUI/BoxLayout.h>
2020-12-28 09:30:19 +00:00
# include <LibGUI/Button.h>
# include <LibGUI/CheckBox.h>
# include <LibGUI/Event.h>
2020-02-06 19:33:02 +00:00
# include <LibGUI/GroupBox.h>
2020-11-02 19:30:17 +00:00
# include <LibGUI/Icon.h>
2020-02-15 00:56:30 +00:00
# include <LibGUI/Menu.h>
2020-02-06 19:33:02 +00:00
# include <LibGUI/MenuBar.h>
# include <LibGUI/RadioButton.h>
# include <LibGUI/Slider.h>
2020-06-29 20:28:18 +00:00
# include <LibGUI/SpinBox.h>
2020-12-28 09:30:19 +00:00
# include <LibGUI/TextBox.h>
2020-02-06 19:33:02 +00:00
# include <LibGUI/Widget.h>
# include <LibGUI/Window.h>
2020-02-14 22:53:11 +00:00
# include <LibGfx/Font.h>
2020-10-31 18:54:23 +00:00
# include <LibGfx/FontDatabase.h>
2020-02-15 00:18:12 +00:00
# include <LibGfx/Palette.h>
2019-10-21 17:50:07 +00:00
# include <LibVT/TerminalWidget.h>
2019-06-07 09:48:03 +00:00
# include <assert.h>
# include <errno.h>
# include <fcntl.h>
# include <pwd.h>
2020-08-04 12:23:28 +00:00
# include <serenity.h>
2019-12-02 15:50:10 +00:00
# include <signal.h>
2020-06-28 17:40:10 +00:00
# include <spawn.h>
2019-06-07 09:48:03 +00:00
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <sys/ioctl.h>
# include <sys/select.h>
2020-11-30 04:03:42 +00:00
# include <sys/wait.h>
2019-06-07 09:48:03 +00:00
# include <unistd.h>
2019-01-15 03:30:55 +00:00
2020-09-06 14:14:46 +00:00
static void utmp_update ( const char * tty , pid_t pid , bool create )
{
if ( ! tty )
return ;
int utmpupdate_pid = fork ( ) ;
if ( utmpupdate_pid < 0 ) {
perror ( " fork " ) ;
return ;
}
2020-11-30 04:03:42 +00:00
if ( utmpupdate_pid = = 0 ) {
// Be careful here! Because fork() only clones one thread it's
// possible that we deadlock on anything involving a mutex,
// including the heap! So resort to low-level APIs
char pid_str [ 32 ] ;
snprintf ( pid_str , sizeof ( pid_str ) , " %d " , pid ) ;
execl ( " /bin/utmpupdate " , " /bin/utmpupdate " , " -f " , " Terminal " , " -p " , pid_str , ( create ? " -c " : " -d " ) , tty , nullptr ) ;
} else {
wait_again :
int status = 0 ;
if ( waitpid ( utmpupdate_pid , & status , 0 ) < 0 ) {
int err = errno ;
if ( err = = EINTR )
goto wait_again ;
perror ( " waitpid " ) ;
return ;
}
if ( WIFEXITED ( status ) & & WEXITSTATUS ( status ) ! = 0 )
dbgln ( " Terminal: utmpupdate exited with status {} " , WEXITSTATUS ( status ) ) ;
else if ( WIFSIGNALED ( status ) )
dbgln ( " Terminal: utmpupdate exited due to unhandled signal {} " , WTERMSIG ( status ) ) ;
}
2020-09-06 14:14:46 +00:00
}
static pid_t run_command ( int ptm_fd , String command )
2019-01-15 05:30:19 +00:00
{
pid_t pid = fork ( ) ;
2020-05-25 12:34:57 +00:00
if ( pid < 0 ) {
perror ( " fork " ) ;
2020-10-06 17:12:42 +00:00
dbgln ( " run_command: could not fork to run '{}' " , command ) ;
2020-09-06 14:14:46 +00:00
return pid ;
2020-05-25 12:34:57 +00:00
}
2019-01-15 05:30:19 +00:00
if ( pid = = 0 ) {
const char * tty_name = ptsname ( ptm_fd ) ;
if ( ! tty_name ) {
perror ( " ptsname " ) ;
exit ( 1 ) ;
}
close ( ptm_fd ) ;
int pts_fd = open ( tty_name , O_RDWR ) ;
2019-02-12 10:25:25 +00:00
if ( pts_fd < 0 ) {
perror ( " open " ) ;
2019-01-15 07:49:24 +00:00
exit ( 1 ) ;
}
2019-02-12 10:25:25 +00:00
2020-01-25 13:52:06 +00:00
if ( setsid ( ) < 0 ) {
perror ( " setsid " ) ;
}
2019-02-12 10:25:25 +00:00
2019-01-15 05:30:19 +00:00
close ( 0 ) ;
close ( 1 ) ;
close ( 2 ) ;
2019-02-12 10:25:25 +00:00
int rc = dup2 ( pts_fd , 0 ) ;
if ( rc < 0 ) {
perror ( " dup2 " ) ;
exit ( 1 ) ;
}
rc = dup2 ( pts_fd , 1 ) ;
if ( rc < 0 ) {
perror ( " dup2 " ) ;
exit ( 1 ) ;
}
rc = dup2 ( pts_fd , 2 ) ;
if ( rc < 0 ) {
perror ( " dup2 " ) ;
exit ( 1 ) ;
}
rc = close ( pts_fd ) ;
if ( rc < 0 ) {
perror ( " close " ) ;
exit ( 1 ) ;
}
2019-01-15 07:49:24 +00:00
rc = ioctl ( 0 , TIOCSCTTY ) ;
if ( rc < 0 ) {
perror ( " ioctl(TIOCSCTTY) " ) ;
exit ( 1 ) ;
}
2020-01-25 11:25:43 +00:00
String shell = " /bin/Shell " ;
auto * pw = getpwuid ( getuid ( ) ) ;
if ( pw & & pw - > pw_shell ) {
shell = pw - > pw_shell ;
}
endpwent ( ) ;
const char * args [ 4 ] = { shell . characters ( ) , nullptr , nullptr , nullptr } ;
2019-09-02 05:28:52 +00:00
if ( ! command . is_empty ( ) ) {
args [ 1 ] = " -c " ;
args [ 2 ] = command . characters ( ) ;
}
2020-04-30 20:38:40 +00:00
const char * envs [ ] = { " PROMPT= \\ X \\ u@ \\ h: \\ w \\ a \\ e[33;1m \\ h \\ e[0m \\ e[34;1m \\ w \\ e[0m \\ p " , " TERM=xterm " , " PAGER=more " , " PATH=/bin:/usr/bin:/usr/local/bin " , nullptr } ;
2020-01-25 11:25:43 +00:00
rc = execve ( shell . characters ( ) , const_cast < char * * > ( args ) , const_cast < char * * > ( envs ) ) ;
2019-01-15 05:30:19 +00:00
if ( rc < 0 ) {
perror ( " execve " ) ;
exit ( 1 ) ;
}
ASSERT_NOT_REACHED ( ) ;
}
2020-09-06 14:14:46 +00:00
return pid ;
2019-01-15 05:30:19 +00:00
}
2019-01-15 03:30:55 +00:00
2020-08-10 21:51:31 +00:00
static RefPtr < GUI : : Window > create_settings_window ( TerminalWidget & terminal )
2019-05-31 20:28:47 +00:00
{
2020-02-02 14:07:41 +00:00
auto window = GUI : : Window : : construct ( ) ;
2019-05-31 21:51:06 +00:00
window - > set_title ( " Terminal Settings " ) ;
2020-03-10 21:08:25 +00:00
window - > set_resizable ( false ) ;
2020-12-30 02:14:21 +00:00
window - > resize ( 200 , 254 ) ;
2020-02-25 18:39:57 +00:00
window - > set_modal ( true ) ;
2019-05-31 21:51:06 +00:00
2020-03-04 08:46:23 +00:00
auto & settings = window - > set_main_widget < GUI : : Widget > ( ) ;
settings . set_fill_with_background_color ( true ) ;
settings . set_background_role ( ColorRole : : Button ) ;
settings . set_layout < GUI : : VerticalBoxLayout > ( ) ;
settings . layout ( ) - > set_margins ( { 4 , 4 , 4 , 4 } ) ;
2020-12-30 02:14:21 +00:00
auto & radio_container = settings . add < GUI : : GroupBox > ( " Bell mode " ) ;
2020-03-04 18:07:55 +00:00
radio_container . set_layout < GUI : : VerticalBoxLayout > ( ) ;
radio_container . layout ( ) - > set_margins ( { 6 , 16 , 6 , 6 } ) ;
2020-12-30 02:14:21 +00:00
radio_container . set_fixed_height ( 94 ) ;
2020-03-04 18:07:55 +00:00
2020-12-30 02:14:21 +00:00
auto & sysbell_radio = radio_container . add < GUI : : RadioButton > ( " Use (audible) system Bell " ) ;
auto & visbell_radio = radio_container . add < GUI : : RadioButton > ( " Use (visual) bell " ) ;
auto & nobell_radio = radio_container . add < GUI : : RadioButton > ( " Disable bell " ) ;
2020-12-19 21:47:45 +00:00
switch ( terminal . bell_mode ( ) ) {
case TerminalWidget : : BellMode : : Visible :
sysbell_radio . set_checked ( true ) ;
break ;
case TerminalWidget : : BellMode : : AudibleBeep :
visbell_radio . set_checked ( true ) ;
break ;
case TerminalWidget : : BellMode : : Disabled :
nobell_radio . set_checked ( true ) ;
break ;
}
sysbell_radio . on_checked = [ & terminal ] ( const bool ) {
terminal . set_bell_mode ( TerminalWidget : : BellMode : : AudibleBeep ) ;
} ;
visbell_radio . on_checked = [ & terminal ] ( const bool ) {
terminal . set_bell_mode ( TerminalWidget : : BellMode : : Visible ) ;
} ;
nobell_radio . on_checked = [ & terminal ] ( const bool ) {
terminal . set_bell_mode ( TerminalWidget : : BellMode : : Disabled ) ;
2019-06-07 09:48:03 +00:00
} ;
2019-05-31 20:28:47 +00:00
2020-12-30 02:14:21 +00:00
auto & slider_container = settings . add < GUI : : GroupBox > ( " Background opacity " ) ;
2020-03-04 18:07:55 +00:00
slider_container . set_layout < GUI : : VerticalBoxLayout > ( ) ;
slider_container . layout ( ) - > set_margins ( { 6 , 16 , 6 , 6 } ) ;
2020-12-30 00:23:32 +00:00
slider_container . set_fixed_height ( 50 ) ;
2020-03-04 18:07:55 +00:00
auto & slider = slider_container . add < GUI : : HorizontalSlider > ( ) ;
2019-05-31 20:28:47 +00:00
2020-03-04 18:07:55 +00:00
slider . on_value_changed = [ & terminal ] ( int value ) {
2019-06-28 19:41:13 +00:00
terminal . set_opacity ( value ) ;
2019-06-07 09:48:03 +00:00
} ;
2019-05-31 20:28:47 +00:00
2020-03-04 18:07:55 +00:00
slider . set_range ( 0 , 255 ) ;
slider . set_value ( terminal . opacity ( ) ) ;
2019-05-31 20:28:47 +00:00
2020-12-30 02:14:21 +00:00
auto & spinbox_container = settings . add < GUI : : GroupBox > ( " Scroll length " ) ;
2020-06-29 20:28:18 +00:00
spinbox_container . set_layout < GUI : : VerticalBoxLayout > ( ) ;
spinbox_container . layout ( ) - > set_margins ( { 6 , 16 , 6 , 6 } ) ;
2020-12-30 00:23:32 +00:00
spinbox_container . set_fixed_height ( 46 ) ;
2020-06-29 20:28:18 +00:00
auto & spinbox = spinbox_container . add < GUI : : SpinBox > ( ) ;
spinbox . set_min ( 1 ) ;
spinbox . set_value ( terminal . scroll_length ( ) ) ;
spinbox . on_change = [ & terminal ] ( int value ) {
terminal . set_scroll_length ( value ) ;
} ;
2020-11-29 14:49:45 +00:00
auto & history_size_spinbox_container = settings . add < GUI : : GroupBox > ( " Maximum scrollback history lines " ) ;
history_size_spinbox_container . set_layout < GUI : : VerticalBoxLayout > ( ) ;
history_size_spinbox_container . layout ( ) - > set_margins ( { 6 , 16 , 6 , 6 } ) ;
2020-12-30 00:23:32 +00:00
history_size_spinbox_container . set_fixed_height ( 46 ) ;
2020-11-29 14:49:45 +00:00
auto & history_size_spinbox = history_size_spinbox_container . add < GUI : : SpinBox > ( ) ;
history_size_spinbox . set_range ( 0 , 40960 ) ;
history_size_spinbox . set_value ( terminal . max_history_size ( ) ) ;
history_size_spinbox . on_change = [ & terminal ] ( int value ) {
terminal . set_max_history_size ( value ) ;
} ;
2019-05-31 21:51:06 +00:00
return window ;
2019-05-31 20:28:47 +00:00
}
2020-12-28 09:30:19 +00:00
static RefPtr < GUI : : Window > create_find_window ( TerminalWidget & terminal )
{
auto window = GUI : : Window : : construct ( ) ;
window - > set_title ( " Find in Terminal " ) ;
window - > set_resizable ( false ) ;
window - > resize ( 300 , 90 ) ;
window - > set_modal ( true ) ;
auto & search = window - > set_main_widget < GUI : : Widget > ( ) ;
search . set_fill_with_background_color ( true ) ;
search . set_background_role ( ColorRole : : Button ) ;
search . set_layout < GUI : : VerticalBoxLayout > ( ) ;
search . layout ( ) - > set_margins ( { 4 , 4 , 4 , 4 } ) ;
auto & find = search . add < GUI : : Widget > ( ) ;
find . set_layout < GUI : : HorizontalBoxLayout > ( ) ;
find . layout ( ) - > set_margins ( { 4 , 4 , 4 , 4 } ) ;
2020-12-30 00:23:32 +00:00
find . set_fixed_height ( 30 ) ;
2020-12-28 09:30:19 +00:00
auto & find_textbox = find . add < GUI : : TextBox > ( ) ;
2020-12-30 00:23:32 +00:00
find_textbox . set_fixed_width ( 230 ) ;
2020-12-28 09:30:19 +00:00
find_textbox . set_focus ( true ) ;
if ( terminal . has_selection ( ) ) {
String selected_text = terminal . selected_text ( ) ;
selected_text . replace ( " \n " , " " , true ) ;
find_textbox . set_text ( selected_text ) ;
}
auto & find_backwards = find . add < GUI : : Button > ( ) ;
2020-12-30 00:23:32 +00:00
find_backwards . set_fixed_width ( 25 ) ;
2020-12-28 09:30:19 +00:00
find_backwards . set_icon ( Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/upward-triangle.png " ) ) ;
auto & find_forwards = find . add < GUI : : Button > ( ) ;
2020-12-30 00:23:32 +00:00
find_forwards . set_fixed_width ( 25 ) ;
2020-12-28 09:30:19 +00:00
find_forwards . set_icon ( Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/downward-triangle.png " ) ) ;
find_textbox . on_return_pressed = [ & ] ( ) {
find_backwards . click ( ) ;
} ;
auto & match_case = search . add < GUI : : CheckBox > ( " Case sensitive " ) ;
auto & wrap_around = search . add < GUI : : CheckBox > ( " Wrap around " ) ;
find_backwards . on_click = [ & ] ( auto ) {
auto needle = find_textbox . text ( ) ;
if ( needle . is_empty ( ) ) {
return ;
}
auto found_range = terminal . find_previous ( needle , terminal . normalized_selection ( ) . start ( ) , match_case . is_checked ( ) , wrap_around . is_checked ( ) ) ;
if ( found_range . is_valid ( ) ) {
terminal . scroll_to_row ( found_range . start ( ) . row ( ) ) ;
terminal . set_selection ( found_range ) ;
}
} ;
find_forwards . on_click = [ & ] ( auto ) {
auto needle = find_textbox . text ( ) ;
if ( needle . is_empty ( ) ) {
return ;
}
auto found_range = terminal . find_next ( needle , terminal . normalized_selection ( ) . end ( ) , match_case . is_checked ( ) , wrap_around . is_checked ( ) ) ;
if ( found_range . is_valid ( ) ) {
terminal . scroll_to_row ( found_range . start ( ) . row ( ) ) ;
terminal . set_selection ( found_range ) ;
}
} ;
return window ;
}
2019-02-11 13:56:23 +00:00
int main ( int argc , char * * argv )
2019-01-16 11:57:07 +00:00
{
2020-05-26 10:52:42 +00:00
if ( pledge ( " stdio tty rpath accept cpath wpath shared_buffer proc exec unix fattr sigaction " , nullptr ) < 0 ) {
2020-01-11 20:07:18 +00:00
perror ( " pledge " ) ;
return 1 ;
}
2019-12-02 15:50:10 +00:00
struct sigaction act ;
memset ( & act , 0 , sizeof ( act ) ) ;
act . sa_flags = SA_NOCLDWAIT ;
act . sa_handler = SIG_IGN ;
int rc = sigaction ( SIGCHLD , & act , nullptr ) ;
if ( rc < 0 ) {
perror ( " sigaction " ) ;
return 1 ;
}
2020-07-04 12:05:19 +00:00
auto app = GUI : : Application : : construct ( argc , argv ) ;
2019-02-11 13:56:23 +00:00
2020-05-09 14:16:16 +00:00
if ( pledge ( " stdio tty rpath accept cpath wpath shared_buffer proc exec unix " , nullptr ) < 0 ) {
2020-01-11 20:07:18 +00:00
perror ( " pledge " ) ;
return 1 ;
}
2020-02-04 02:42:46 +00:00
const char * command_to_execute = nullptr ;
2019-09-02 05:28:52 +00:00
2020-02-02 11:34:39 +00:00
Core : : ArgsParser args_parser ;
2020-01-27 17:25:36 +00:00
args_parser . add_option ( command_to_execute , " Execute this command inside the terminal " , nullptr , ' e ' , " command " ) ;
2020-03-15 16:20:19 +00:00
2020-01-27 17:25:36 +00:00
args_parser . parse ( argc , argv ) ;
2019-09-02 05:28:52 +00:00
2020-02-05 20:17:41 +00:00
int ptm_fd = posix_openpt ( O_RDWR | O_CLOEXEC ) ;
2019-01-16 12:36:10 +00:00
if ( ptm_fd < 0 ) {
2020-02-05 20:17:41 +00:00
perror ( " posix_openpt " ) ;
return 1 ;
}
if ( grantpt ( ptm_fd ) < 0 ) {
perror ( " grantpt " ) ;
return 1 ;
}
if ( unlockpt ( ptm_fd ) < 0 ) {
perror ( " unlockpt " ) ;
2019-01-16 12:36:10 +00:00
return 1 ;
}
2019-01-15 05:30:19 +00:00
2020-05-02 09:28:26 +00:00
RefPtr < Core : : ConfigFile > config = Core : : ConfigFile : : get_for_app ( " Terminal " ) ;
2020-09-06 14:14:46 +00:00
pid_t shell_pid = 0 ;
2020-05-02 09:28:26 +00:00
if ( command_to_execute )
2020-09-06 14:14:46 +00:00
shell_pid = run_command ( ptm_fd , command_to_execute ) ;
2020-05-02 09:28:26 +00:00
else
2020-09-06 14:14:46 +00:00
shell_pid = run_command ( ptm_fd , config - > read_entry ( " Startup " , " Command " , " " ) ) ;
auto * pts_name = ptsname ( ptm_fd ) ;
utmp_update ( pts_name , shell_pid , true ) ;
2019-01-15 05:30:19 +00:00
2020-11-02 19:30:17 +00:00
auto app_icon = GUI : : Icon : : default_icon ( " app-terminal " ) ;
2020-02-02 14:07:41 +00:00
auto window = GUI : : Window : : construct ( ) ;
2019-05-16 18:13:41 +00:00
window - > set_title ( " Terminal " ) ;
2019-04-10 12:29:47 +00:00
window - > set_background_color ( Color : : Black ) ;
2019-03-17 03:23:54 +00:00
window - > set_double_buffering_enabled ( false ) ;
2019-01-15 03:30:55 +00:00
2020-03-04 08:46:23 +00:00
auto & terminal = window - > set_main_widget < TerminalWidget > ( ptm_fd , true , config ) ;
terminal . on_command_exit = [ & ] {
2020-07-04 12:05:19 +00:00
app - > quit ( 0 ) ;
2019-10-22 19:57:53 +00:00
} ;
2020-03-04 08:46:23 +00:00
terminal . on_title_change = [ & ] ( auto & title ) {
2019-10-21 20:07:59 +00:00
window - > set_title ( title ) ;
} ;
2020-03-04 08:46:23 +00:00
terminal . apply_size_increments_to_window ( * window ) ;
2019-02-10 13:28:39 +00:00
window - > show ( ) ;
2020-11-02 19:30:17 +00:00
window - > set_icon ( app_icon . bitmap_for_size ( 16 ) ) ;
2020-12-19 21:47:45 +00:00
auto bell = config - > read_entry ( " Window " , " Bell " , " Visible " ) ;
if ( bell = = " AudibleBeep " ) {
terminal . set_bell_mode ( TerminalWidget : : BellMode : : AudibleBeep ) ;
} else if ( bell = = " Disabled " ) {
terminal . set_bell_mode ( TerminalWidget : : BellMode : : Disabled ) ;
} else {
terminal . set_bell_mode ( TerminalWidget : : BellMode : : Visible ) ;
}
2019-02-10 13:28:39 +00:00
2020-02-02 14:07:41 +00:00
RefPtr < GUI : : Window > settings_window ;
2020-12-28 09:30:19 +00:00
RefPtr < GUI : : Window > find_window ;
2019-05-31 19:43:58 +00:00
2019-05-25 23:43:15 +00:00
auto new_opacity = config - > read_num_entry ( " Window " , " Opacity " , 255 ) ;
2020-03-04 08:46:23 +00:00
terminal . set_opacity ( new_opacity ) ;
2019-08-15 13:19:54 +00:00
window - > set_has_alpha_channel ( new_opacity < 255 ) ;
2019-05-25 23:43:15 +00:00
2020-11-29 14:49:45 +00:00
auto new_scrollback_size = config - > read_num_entry ( " Terminal " , " MaxHistorySize " , terminal . max_history_size ( ) ) ;
terminal . set_max_history_size ( new_scrollback_size ) ;
2020-04-21 14:01:00 +00:00
auto menubar = GUI : : MenuBar : : construct ( ) ;
2019-02-11 14:37:12 +00:00
2020-04-04 10:18:40 +00:00
auto & app_menu = menubar - > add_menu ( " Terminal " ) ;
app_menu . add_action ( GUI : : Action : : create ( " Open new terminal " , { Mod_Ctrl | Mod_Shift , Key_N } , Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/app-terminal.png " ) , [ & ] ( auto & ) {
2020-06-28 17:40:10 +00:00
pid_t child ;
const char * argv [ ] = { " Terminal " , nullptr } ;
2020-08-04 12:23:28 +00:00
if ( ( errno = posix_spawn ( & child , " /bin/Terminal " , nullptr , nullptr , const_cast < char * * > ( argv ) , environ ) ) ) {
perror ( " posix_spawn " ) ;
} else {
if ( disown ( child ) < 0 )
perror ( " disown " ) ;
}
2019-11-20 20:19:17 +00:00
} ) ) ;
2020-08-24 11:02:03 +00:00
app_menu . add_action ( GUI : : Action : : create ( " Settings... " , Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/gear.png " ) ,
2020-02-02 14:07:41 +00:00
[ & ] ( const GUI : : Action & ) {
2019-09-21 16:58:03 +00:00
if ( ! settings_window ) {
2020-03-04 08:46:23 +00:00
settings_window = create_settings_window ( terminal ) ;
2019-09-21 16:58:03 +00:00
settings_window - > on_close_request = [ & ] {
settings_window = nullptr ;
2020-02-02 14:07:41 +00:00
return GUI : : Window : : CloseRequestDecision : : Close ;
2019-09-21 16:58:03 +00:00
} ;
}
2019-06-07 09:48:03 +00:00
settings_window - > show ( ) ;
settings_window - > move_to_front ( ) ;
} ) ) ;
2020-04-04 10:18:40 +00:00
app_menu . add_separator ( ) ;
app_menu . add_action ( GUI : : CommonActions : : make_quit_action ( [ ] ( auto & ) {
2020-10-06 17:12:42 +00:00
dbgln ( " Terminal: Quit menu activated! " ) ;
2020-07-04 14:52:01 +00:00
GUI : : Application : : the ( ) - > quit ( ) ;
2019-02-12 13:09:48 +00:00
} ) ) ;
2019-02-11 14:37:12 +00:00
2020-04-04 10:18:40 +00:00
auto & edit_menu = menubar - > add_menu ( " Edit " ) ;
edit_menu . add_action ( terminal . copy_action ( ) ) ;
edit_menu . add_action ( terminal . paste_action ( ) ) ;
2020-12-28 09:30:19 +00:00
edit_menu . add_separator ( ) ;
edit_menu . add_action ( GUI : : Action : : create ( " Find " , { Mod_Ctrl , Key_F } , Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/find.png " ) ,
[ & ] ( auto & ) {
if ( ! find_window ) {
find_window = create_find_window ( terminal ) ;
find_window - > on_close_request = [ & ] {
find_window = nullptr ;
return GUI : : Window : : CloseRequestDecision : : Close ;
} ;
}
find_window - > show ( ) ;
find_window - > move_to_front ( ) ;
} ) ) ;
2019-11-20 20:33:23 +00:00
2020-07-05 21:34:02 +00:00
auto & view_menu = menubar - > add_menu ( " View " ) ;
view_menu . add_action ( terminal . clear_including_history_action ( ) ) ;
2020-02-02 14:07:41 +00:00
GUI : : ActionGroup font_action_group ;
2020-01-08 19:56:11 +00:00
font_action_group . set_exclusive ( true ) ;
2020-04-04 10:18:40 +00:00
auto & font_menu = menubar - > add_menu ( " Font " ) ;
2020-10-31 09:18:49 +00:00
Gfx : : FontDatabase : : the ( ) . for_each_fixed_width_font ( [ & ] ( const Gfx : : Font & font ) {
2020-10-25 18:28:06 +00:00
auto action = GUI : : Action : : create_checkable ( font . qualified_name ( ) , [ & ] ( auto & ) {
terminal . set_font ( font ) ;
config - > write_entry ( " Text " , " Font " , font . qualified_name ( ) ) ;
2019-05-25 23:58:22 +00:00
config - > sync ( ) ;
2020-03-04 08:46:23 +00:00
terminal . force_repaint ( ) ;
2020-01-08 19:56:11 +00:00
} ) ;
font_action_group . add_action ( * action ) ;
2020-10-25 18:28:06 +00:00
if ( terminal . font ( ) . qualified_name ( ) = = font . qualified_name ( ) )
2020-01-08 19:56:11 +00:00
action - > set_checked ( true ) ;
2020-04-04 10:18:40 +00:00
font_menu . add_action ( * action ) ;
2019-02-12 13:35:33 +00:00
} ) ;
2019-02-12 07:39:19 +00:00
2020-04-04 10:18:40 +00:00
auto & help_menu = menubar - > add_menu ( " Help " ) ;
help_menu . add_action ( GUI : : Action : : create ( " About " , [ & ] ( auto & ) {
2020-11-02 19:30:17 +00:00
GUI : : AboutDialog : : show ( " Terminal " , app_icon . bitmap_for_size ( 32 ) , window ) ;
2019-02-12 13:09:48 +00:00
} ) ) ;
2019-02-11 14:37:12 +00:00
2020-07-04 12:05:19 +00:00
app - > set_menubar ( move ( menubar ) ) ;
2019-02-11 14:37:12 +00:00
2020-01-21 11:12:15 +00:00
if ( unveil ( " /res " , " r " ) < 0 ) {
perror ( " unveil " ) ;
return 1 ;
}
2020-12-24 15:10:33 +00:00
if ( unveil ( " /bin " , " r " ) < 0 ) {
perror ( " unveil " ) ;
return 1 ;
}
2020-01-21 11:12:15 +00:00
if ( unveil ( " /bin/Terminal " , " x " ) < 0 ) {
perror ( " unveil " ) ;
return 1 ;
}
2020-09-06 14:14:46 +00:00
if ( unveil ( " /bin/utmpupdate " , " x " ) < 0 ) {
perror ( " unveil " ) ;
return 1 ;
}
2020-12-27 18:13:57 +00:00
if ( unveil ( " /etc/FileIconProvider.ini " , " r " ) < 0 ) {
perror ( " unveil " ) ;
return 1 ;
}
2020-05-09 14:16:16 +00:00
if ( unveil ( " /tmp/portal/launch " , " rw " ) < 0 ) {
perror ( " unveil " ) ;
return 1 ;
}
2020-01-21 12:08:22 +00:00
if ( unveil ( config - > file_name ( ) . characters ( ) , " rwc " ) ) {
perror ( " unveil " ) ;
return 1 ;
}
2020-01-21 11:12:15 +00:00
unveil ( nullptr , nullptr ) ;
2019-05-25 23:43:15 +00:00
config - > sync ( ) ;
2020-09-06 14:14:46 +00:00
int result = app - > exec ( ) ;
2020-10-06 17:12:42 +00:00
dbgln ( " Exiting terminal, updating utmp " ) ;
2020-09-06 14:14:46 +00:00
utmp_update ( pts_name , 0 , false ) ;
return result ;
2019-01-15 03:30:55 +00:00
}