2020-01-18 09:38:21 +01:00
/*
2021-08-25 19:39:57 +02:00
* Copyright ( c ) 2018 - 2021 , Andreas Kling < kling @ serenityos . org >
2020-01-18 09:38:21 +01:00
*
2021-04-22 01:24:48 -07:00
* SPDX - License - Identifier : BSD - 2 - Clause
2020-01-18 09:38:21 +01:00
*/
2021-05-28 21:26:39 +02:00
# include <AK/QuickSort.h>
2021-01-03 08:39:46 +00:00
# include <AK/URL.h>
2020-12-31 23:38:39 +01:00
# include <Applications/Terminal/TerminalSettingsWindowGML.h>
2021-08-25 19:39:57 +02:00
# include <LibConfig/Client.h>
2020-02-06 15:04:03 +01:00
# include <LibCore/ArgsParser.h>
2021-05-28 21:26:39 +02:00
# include <LibCore/DirIterator.h>
2021-08-06 01:05:32 +02:00
# include <LibCore/Process.h>
2021-11-23 10:59:50 +01:00
# include <LibCore/System.h>
2021-01-03 08:39:46 +00:00
# include <LibDesktop/Launcher.h>
2020-02-06 20:33:02 +01:00
# include <LibGUI/Action.h>
# include <LibGUI/ActionGroup.h>
# include <LibGUI/Application.h>
# include <LibGUI/BoxLayout.h>
2020-12-28 11:30:19 +02:00
# include <LibGUI/Button.h>
# include <LibGUI/CheckBox.h>
2021-05-28 21:26:39 +02:00
# include <LibGUI/ComboBox.h>
2020-12-28 11:30:19 +02:00
# include <LibGUI/Event.h>
2020-12-30 17:52:19 +01:00
# include <LibGUI/FontPicker.h>
2020-11-02 19:30:17 +00:00
# include <LibGUI/Icon.h>
2021-05-28 21:26:39 +02:00
# include <LibGUI/ItemListModel.h>
2020-02-15 01:56:30 +01:00
# include <LibGUI/Menu.h>
2021-04-13 16:18:20 +02:00
# include <LibGUI/Menubar.h>
2020-12-31 11:57:58 +01:00
# include <LibGUI/OpacitySlider.h>
2020-02-06 20:33:02 +01:00
# include <LibGUI/RadioButton.h>
2020-06-29 22:28:18 +02:00
# include <LibGUI/SpinBox.h>
2020-12-28 11:30:19 +02:00
# include <LibGUI/TextBox.h>
2020-02-06 20:33:02 +01:00
# include <LibGUI/Widget.h>
# include <LibGUI/Window.h>
2020-02-15 01:18:12 +01:00
# include <LibGfx/Palette.h>
2021-11-22 16:12:40 +01:00
# include <LibMain/Main.h>
2019-10-21 19:50:07 +02:00
# include <LibVT/TerminalWidget.h>
2019-06-07 11:48:03 +02:00
# include <assert.h>
# include <errno.h>
2021-04-30 03:40:38 +02:00
# include <pty.h>
2019-06-07 11:48:03 +02:00
# include <pwd.h>
2019-12-02 16:50:10 +01:00
# include <signal.h>
2019-06-07 11:48:03 +02:00
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <sys/ioctl.h>
2020-11-29 21:03:42 -07:00
# include <sys/wait.h>
2019-06-07 11:48:03 +02:00
# include <unistd.h>
2019-01-15 04:30:55 +01:00
2020-09-06 16:14:46 +02: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-29 21:03:42 -07: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 16:14:46 +02:00
}
2021-07-04 10:59:40 +02:00
static void run_command ( String command , bool keep_open )
2019-01-15 06:30:19 +01:00
{
2021-04-30 03:40:38 +02:00
String shell = " /bin/Shell " ;
auto * pw = getpwuid ( getuid ( ) ) ;
if ( pw & & pw - > pw_shell ) {
shell = pw - > pw_shell ;
2020-05-25 17:04:57 +04:30
}
2021-04-30 03:40:38 +02:00
endpwent ( ) ;
2020-05-25 17:04:57 +04:30
2021-07-04 10:59:40 +02:00
const char * args [ 5 ] = { shell . characters ( ) , nullptr , nullptr , nullptr , nullptr } ;
2021-04-30 03:40:38 +02:00
if ( ! command . is_empty ( ) ) {
2021-07-04 10:59:40 +02:00
int arg_index = 1 ;
if ( keep_open )
args [ arg_index + + ] = " --keep-open " ;
args [ arg_index + + ] = " -c " ;
args [ arg_index + + ] = command . characters ( ) ;
2019-01-15 06:30:19 +01:00
}
2021-08-07 14:05:33 +02:00
const char * envs [ ] = { " TERM=xterm " , " PAGER=more " , " PATH=/usr/local/bin:/usr/bin:/bin " , nullptr } ;
2021-04-30 03:40:38 +02:00
int rc = execve ( shell . characters ( ) , const_cast < char * * > ( args ) , const_cast < char * * > ( envs ) ) ;
if ( rc < 0 ) {
perror ( " execve " ) ;
exit ( 1 ) ;
}
VERIFY_NOT_REACHED ( ) ;
2019-01-15 06:30:19 +01:00
}
2019-01-15 04:30:55 +01:00
2021-02-27 17:49:08 +01:00
static RefPtr < GUI : : Window > create_settings_window ( VT : : TerminalWidget & terminal )
2019-05-31 13:28:47 -07:00
{
2021-02-16 16:20:39 +01:00
auto window = GUI : : Window : : construct ( ) ;
window - > set_window_type ( GUI : : WindowType : : ToolWindow ) ;
2020-12-30 13:42:16 +01:00
window - > set_title ( " Terminal settings " ) ;
2020-03-10 22:08:25 +01:00
window - > set_resizable ( false ) ;
2021-05-28 21:26:39 +02:00
window - > resize ( 200 , 240 ) ;
2021-02-16 16:20:39 +01:00
window - > center_within ( * terminal . window ( ) ) ;
2019-05-31 14:51:06 -07:00
2020-03-04 09:46:23 +01:00
auto & settings = window - > set_main_widget < GUI : : Widget > ( ) ;
2020-12-31 23:38:39 +01:00
settings . load_from_gml ( terminal_settings_window_gml ) ;
2020-03-04 19:07:55 +01:00
2021-01-01 00:57:48 -07:00
auto & beep_bell_radio = * settings . find_descendant_of_type_named < GUI : : RadioButton > ( " beep_bell_radio " ) ;
auto & visual_bell_radio = * settings . find_descendant_of_type_named < GUI : : RadioButton > ( " visual_bell_radio " ) ;
auto & no_bell_radio = * settings . find_descendant_of_type_named < GUI : : RadioButton > ( " no_bell_radio " ) ;
2020-12-19 21:47:45 +00:00
switch ( terminal . bell_mode ( ) ) {
2021-02-27 17:49:08 +01:00
case VT : : TerminalWidget : : BellMode : : Visible :
2020-12-31 23:38:39 +01:00
visual_bell_radio . set_checked ( true ) ;
2020-12-19 21:47:45 +00:00
break ;
2021-02-27 17:49:08 +01:00
case VT : : TerminalWidget : : BellMode : : AudibleBeep :
2020-12-31 23:38:39 +01:00
beep_bell_radio . set_checked ( true ) ;
2020-12-19 21:47:45 +00:00
break ;
2021-02-27 17:49:08 +01:00
case VT : : TerminalWidget : : BellMode : : Disabled :
2020-12-31 23:38:39 +01:00
no_bell_radio . set_checked ( true ) ;
2020-12-19 21:47:45 +00:00
break ;
}
2020-12-31 23:38:39 +01:00
beep_bell_radio . on_checked = [ & terminal ] ( bool ) {
2021-02-27 17:49:08 +01:00
terminal . set_bell_mode ( VT : : TerminalWidget : : BellMode : : AudibleBeep ) ;
2020-12-19 21:47:45 +00:00
} ;
2020-12-31 23:38:39 +01:00
visual_bell_radio . on_checked = [ & terminal ] ( bool ) {
2021-02-27 17:49:08 +01:00
terminal . set_bell_mode ( VT : : TerminalWidget : : BellMode : : Visible ) ;
2020-12-19 21:47:45 +00:00
} ;
2020-12-31 23:38:39 +01:00
no_bell_radio . on_checked = [ & terminal ] ( bool ) {
2021-02-27 17:49:08 +01:00
terminal . set_bell_mode ( VT : : TerminalWidget : : BellMode : : Disabled ) ;
2019-06-07 11:48:03 +02:00
} ;
2019-05-31 13:28:47 -07:00
2021-01-01 00:57:48 -07:00
auto & slider = * settings . find_descendant_of_type_named < GUI : : OpacitySlider > ( " background_opacity_slider " ) ;
2020-12-30 15:11:46 +01:00
slider . on_change = [ & terminal ] ( int value ) {
2019-06-28 21:41:13 +02:00
terminal . set_opacity ( value ) ;
2019-06-07 11:48:03 +02:00
} ;
2020-03-04 19:07:55 +01:00
slider . set_value ( terminal . opacity ( ) ) ;
2019-05-31 13:28:47 -07:00
2021-01-01 00:57:48 -07:00
auto & history_size_spinbox = * settings . find_descendant_of_type_named < GUI : : SpinBox > ( " history_size_spinbox " ) ;
2020-11-29 18:19:45 +03:30
history_size_spinbox . set_value ( terminal . max_history_size ( ) ) ;
history_size_spinbox . on_change = [ & terminal ] ( int value ) {
terminal . set_max_history_size ( value ) ;
} ;
2021-05-28 21:26:39 +02:00
// The settings window takes a reference to this vector, so it needs to outlive this scope.
// As long as we ensure that only one settings window may be open at a time (which we do),
// this should cause no problems.
static Vector < String > color_scheme_names ;
color_scheme_names . clear ( ) ;
Core : : DirIterator iterator ( " /res/terminal-colors " , Core : : DirIterator : : SkipParentAndBaseDir ) ;
while ( iterator . has_next ( ) ) {
auto path = iterator . next_path ( ) ;
2021-09-11 02:15:44 +03:00
color_scheme_names . append ( path . replace ( " .ini " , " " ) ) ;
2021-05-28 21:26:39 +02:00
}
quick_sort ( color_scheme_names ) ;
auto & color_scheme_combo = * settings . find_descendant_of_type_named < GUI : : ComboBox > ( " color_scheme_combo " ) ;
color_scheme_combo . set_only_allow_values_from_model ( true ) ;
color_scheme_combo . set_model ( * GUI : : ItemListModel < String > : : create ( color_scheme_names ) ) ;
color_scheme_combo . set_selected_index ( color_scheme_names . find_first_index ( terminal . color_scheme_name ( ) ) . value ( ) ) ;
2021-07-04 10:57:46 +02:00
color_scheme_combo . set_enabled ( color_scheme_names . size ( ) > 1 ) ;
2021-05-28 21:26:39 +02:00
color_scheme_combo . on_change = [ & ] ( auto & , const GUI : : ModelIndex & index ) {
terminal . set_color_scheme ( index . data ( ) . as_string ( ) ) ;
} ;
2019-05-31 14:51:06 -07:00
return window ;
2019-05-31 13:28:47 -07:00
}
2021-02-27 17:49:08 +01:00
static RefPtr < GUI : : Window > create_find_window ( VT : : TerminalWidget & terminal )
2020-12-28 11:30:19 +02:00
{
auto window = GUI : : Window : : construct ( ) ;
2021-02-16 16:20:39 +01:00
window - > set_window_type ( GUI : : WindowType : : ToolWindow ) ;
2020-12-28 11:30:19 +02:00
window - > set_title ( " Find in Terminal " ) ;
window - > set_resizable ( false ) ;
window - > resize ( 300 , 90 ) ;
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 > ( ) ;
Userland+LibGUI: Add shorthand versions of the Margins constructor
This allows for typing [8] instead of [8, 8, 8, 8] to specify the same
margin on all edges, for example. The constructors follow CSS' style of
specifying margins. The added constructors are:
- Margins(int all): Sets the same margin on all edges.
- Margins(int vertical, int horizontal): Sets the first argument to top
and bottom margins, and the second argument to left and right margins.
- Margins(int top, int vertical, int bottom): Sets the first argument to
the top margin, the second argument to the left and right margins,
and the third argument to the bottom margin.
2021-08-17 00:11:38 +00:00
search . layout ( ) - > set_margins ( 4 ) ;
2020-12-28 11:30:19 +02:00
auto & find = search . add < GUI : : Widget > ( ) ;
find . set_layout < GUI : : HorizontalBoxLayout > ( ) ;
Userland+LibGUI: Add shorthand versions of the Margins constructor
This allows for typing [8] instead of [8, 8, 8, 8] to specify the same
margin on all edges, for example. The constructors follow CSS' style of
specifying margins. The added constructors are:
- Margins(int all): Sets the same margin on all edges.
- Margins(int vertical, int horizontal): Sets the first argument to top
and bottom margins, and the second argument to left and right margins.
- Margins(int top, int vertical, int bottom): Sets the first argument to
the top margin, the second argument to the left and right margins,
and the third argument to the bottom margin.
2021-08-17 00:11:38 +00:00
find . layout ( ) - > set_margins ( 4 ) ;
2020-12-30 01:23:32 +01:00
find . set_fixed_height ( 30 ) ;
2020-12-28 11:30:19 +02:00
auto & find_textbox = find . add < GUI : : TextBox > ( ) ;
2020-12-30 01:23:32 +01:00
find_textbox . set_fixed_width ( 230 ) ;
2020-12-28 11:30:19 +02:00
find_textbox . set_focus ( true ) ;
2021-09-11 02:15:44 +03:00
if ( terminal . has_selection ( ) )
find_textbox . set_text ( terminal . selected_text ( ) . replace ( " \n " , " " , true ) ) ;
2020-12-28 11:30:19 +02:00
auto & find_backwards = find . add < GUI : : Button > ( ) ;
2020-12-30 01:23:32 +01:00
find_backwards . set_fixed_width ( 25 ) ;
2021-11-06 16:25:29 +01:00
find_backwards . set_icon ( Gfx : : Bitmap : : try_load_from_file ( " /res/icons/16x16/upward-triangle.png " ) . release_value_but_fixme_should_propagate_errors ( ) ) ;
2020-12-28 11:30:19 +02:00
auto & find_forwards = find . add < GUI : : Button > ( ) ;
2020-12-30 01:23:32 +01:00
find_forwards . set_fixed_width ( 25 ) ;
2021-11-06 16:25:29 +01:00
find_forwards . set_icon ( Gfx : : Bitmap : : try_load_from_file ( " /res/icons/16x16/downward-triangle.png " ) . release_value_but_fixme_should_propagate_errors ( ) ) ;
2020-12-28 11:30:19 +02:00
find_textbox . on_return_pressed = [ & ] ( ) {
find_backwards . click ( ) ;
} ;
2021-06-02 13:21:17 +02:00
find_textbox . on_shift_return_pressed = [ & ] ( ) {
find_forwards . click ( ) ;
} ;
2020-12-28 11:30:19 +02:00
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 ;
}
2021-11-22 16:12:40 +01:00
ErrorOr < int > serenity_main ( Main : : Arguments arguments )
2019-01-16 12:57:07 +01:00
{
2021-11-23 10:59:50 +01:00
TRY ( Core : : System : : pledge ( " stdio tty rpath cpath wpath recvfd sendfd proc exec unix sigaction " , nullptr ) ) ;
2020-01-11 21:07:18 +01:00
2019-12-02 16:50:10 +01:00
struct sigaction act ;
memset ( & act , 0 , sizeof ( act ) ) ;
act . sa_flags = SA_NOCLDWAIT ;
act . sa_handler = SIG_IGN ;
2021-11-23 10:59:50 +01:00
TRY ( Core : : System : : sigaction ( SIGCHLD , & act , nullptr ) ) ;
2019-02-11 14:56:23 +01:00
2021-11-22 19:11:16 -05:00
auto app = GUI : : Application : : construct ( arguments ) ;
2021-11-22 16:12:40 +01:00
2021-11-23 10:59:50 +01:00
TRY ( Core : : System : : pledge ( " stdio tty rpath cpath wpath recvfd sendfd proc exec unix " , nullptr ) ) ;
2020-01-11 21:07:18 +01:00
2021-08-25 20:06:54 +02:00
Config : : pledge_domains ( " Terminal " ) ;
2020-02-03 20:42:46 -06:00
const char * command_to_execute = nullptr ;
2021-07-04 10:59:40 +02:00
bool keep_open = false ;
2019-09-02 15:28:52 +10:00
2020-02-02 12:34:39 +01:00
Core : : ArgsParser args_parser ;
2020-01-27 20:25:36 +03:00
args_parser . add_option ( command_to_execute , " Execute this command inside the terminal " , nullptr , ' e ' , " command " ) ;
2021-07-04 10:59:40 +02:00
args_parser . add_option ( keep_open , " Keep the terminal open after the command has finished executing " , nullptr , ' k ' ) ;
2020-03-15 18:20:19 +02:00
2021-11-22 19:03:05 -05:00
args_parser . parse ( arguments ) ;
2019-09-02 15:28:52 +10:00
2021-07-04 10:59:40 +02:00
if ( keep_open & & ! command_to_execute ) {
warnln ( " Option -k can only be used in combination with -e. " ) ;
return 1 ;
}
2021-05-06 13:52:46 +01:00
int ptm_fd ;
pid_t shell_pid = forkpty ( & ptm_fd , nullptr , nullptr , nullptr ) ;
2021-04-30 03:40:38 +02:00
if ( shell_pid < 0 ) {
perror ( " forkpty " ) ;
return 1 ;
}
if ( shell_pid = = 0 ) {
close ( ptm_fd ) ;
if ( command_to_execute )
2021-07-04 10:59:40 +02:00
run_command ( command_to_execute , keep_open ) ;
2021-04-30 03:40:38 +02:00
else
2021-08-25 19:39:57 +02:00
run_command ( Config : : read_string ( " Terminal " , " Startup " , " Command " , " " ) , false ) ;
2021-04-30 03:40:38 +02:00
VERIFY_NOT_REACHED ( ) ;
}
2020-09-06 16:14:46 +02:00
auto * pts_name = ptsname ( ptm_fd ) ;
utmp_update ( pts_name , shell_pid , true ) ;
2019-01-15 06:30:19 +01:00
2020-11-02 19:30:17 +00:00
auto app_icon = GUI : : Icon : : default_icon ( " app-terminal " ) ;
2020-02-02 15:07:41 +01:00
auto window = GUI : : Window : : construct ( ) ;
2019-05-16 20:13:41 +02:00
window - > set_title ( " Terminal " ) ;
2019-04-10 14:29:47 +02:00
window - > set_background_color ( Color : : Black ) ;
2019-03-17 04:23:54 +01:00
window - > set_double_buffering_enabled ( false ) ;
2019-01-15 04:30:55 +01:00
2021-08-25 19:39:57 +02:00
auto & terminal = window - > set_main_widget < VT : : TerminalWidget > ( ptm_fd , true ) ;
2020-03-04 09:46:23 +01:00
terminal . on_command_exit = [ & ] {
2020-07-04 14:05:19 +02:00
app - > quit ( 0 ) ;
2019-10-22 21:57:53 +02:00
} ;
2021-11-11 00:55:02 +01:00
terminal . on_title_change = [ & ] ( auto title ) {
2019-10-21 22:07:59 +02:00
window - > set_title ( title ) ;
} ;
2021-02-20 16:57:31 +01:00
terminal . on_terminal_size_change = [ & ] ( auto & size ) {
window - > resize ( size ) ;
} ;
2020-03-04 09:46:23 +01:00
terminal . apply_size_increments_to_window ( * window ) ;
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
2021-08-25 19:39:57 +02:00
auto bell = Config : : read_string ( " Terminal " , " Window " , " Bell " , " Visible " ) ;
2020-12-19 21:47:45 +00:00
if ( bell = = " AudibleBeep " ) {
2021-02-27 17:49:08 +01:00
terminal . set_bell_mode ( VT : : TerminalWidget : : BellMode : : AudibleBeep ) ;
2020-12-19 21:47:45 +00:00
} else if ( bell = = " Disabled " ) {
2021-02-27 17:49:08 +01:00
terminal . set_bell_mode ( VT : : TerminalWidget : : BellMode : : Disabled ) ;
2020-12-19 21:47:45 +00:00
} else {
2021-02-27 17:49:08 +01:00
terminal . set_bell_mode ( VT : : TerminalWidget : : BellMode : : Visible ) ;
2020-12-19 21:47:45 +00:00
}
2019-02-10 14:28:39 +01:00
2020-02-02 15:07:41 +01:00
RefPtr < GUI : : Window > settings_window ;
2020-12-28 11:30:19 +02:00
RefPtr < GUI : : Window > find_window ;
2019-05-31 12:43:58 -07:00
2021-08-25 19:39:57 +02:00
auto new_opacity = Config : : read_i32 ( " Terminal " , " Window " , " Opacity " , 255 ) ;
2020-03-04 09:46:23 +01:00
terminal . set_opacity ( new_opacity ) ;
2019-08-15 15:19:54 +02:00
window - > set_has_alpha_channel ( new_opacity < 255 ) ;
2019-05-25 16:43:15 -07:00
2021-08-25 19:39:57 +02:00
auto new_scrollback_size = Config : : read_i32 ( " Terminal " , " Terminal " , " MaxHistorySize " , terminal . max_history_size ( ) ) ;
2020-11-29 18:19:45 +03:30
terminal . set_max_history_size ( new_scrollback_size ) ;
2021-11-06 16:25:29 +01:00
auto open_settings_action = GUI : : Action : : create ( " &Settings " , Gfx : : Bitmap : : try_load_from_file ( " /res/icons/16x16/settings.png " ) . release_value_but_fixme_should_propagate_errors ( ) ,
2020-12-30 13:42:16 +01:00
[ & ] ( const GUI : : Action & ) {
2021-02-16 16:20:39 +01:00
if ( ! settings_window )
2020-12-30 13:42:16 +01:00
settings_window = create_settings_window ( terminal ) ;
2021-02-16 16:20:39 +01:00
settings_window - > show ( ) ;
2020-12-30 13:42:16 +01:00
settings_window - > move_to_front ( ) ;
2021-08-08 02:06:18 +02:00
settings_window - > on_close = [ & ] ( ) {
2021-08-25 19:39:57 +02:00
Config : : write_i32 ( " Terminal " , " Window " , " Opacity " , terminal . opacity ( ) ) ;
Config : : write_i32 ( " Terminal " , " Terminal " , " MaxHistorySize " , terminal . max_history_size ( ) ) ;
2021-08-08 02:06:18 +02:00
auto bell = terminal . bell_mode ( ) ;
auto bell_setting = String : : empty ( ) ;
if ( bell = = VT : : TerminalWidget : : BellMode : : AudibleBeep ) {
bell_setting = " AudibleBeep " ;
} else if ( bell = = VT : : TerminalWidget : : BellMode : : Disabled ) {
bell_setting = " Disabled " ;
} else {
bell_setting = " Visible " ;
}
2021-08-25 19:39:57 +02:00
Config : : write_string ( " Terminal " , " Window " , " Bell " , bell_setting ) ;
2021-08-08 02:06:18 +02:00
} ;
2020-12-30 13:42:16 +01:00
} ) ;
2020-12-30 17:52:19 +01:00
terminal . context_menu ( ) . add_separator ( ) ;
2021-11-06 16:25:29 +01:00
auto pick_font_action = GUI : : Action : : create ( " &Terminal Font... " , Gfx : : Bitmap : : try_load_from_file ( " /res/icons/16x16/app-font-editor.png " ) . release_value_but_fixme_should_propagate_errors ( ) ,
2020-12-30 17:52:19 +01:00
[ & ] ( auto & ) {
auto picker = GUI : : FontPicker : : construct ( window , & terminal . font ( ) , true ) ;
if ( picker - > exec ( ) = = GUI : : Dialog : : ExecOK ) {
2021-01-09 13:46:22 +01:00
terminal . set_font_and_resize_to_fit ( * picker - > font ( ) ) ;
window - > resize ( terminal . size ( ) ) ;
2021-08-25 19:39:57 +02:00
Config : : write_string ( " Terminal " , " Text " , " Font " , picker - > font ( ) - > qualified_name ( ) ) ;
2020-12-30 17:52:19 +01:00
}
} ) ;
terminal . context_menu ( ) . add_action ( pick_font_action ) ;
2020-12-30 13:42:16 +01:00
terminal . context_menu ( ) . add_separator ( ) ;
terminal . context_menu ( ) . add_action ( open_settings_action ) ;
2021-07-21 21:21:03 +02:00
auto & file_menu = window - > add_menu ( " &File " ) ;
2021-11-06 16:25:29 +01:00
file_menu . add_action ( GUI : : Action : : create ( " Open New &Terminal " , { Mod_Ctrl | Mod_Shift , Key_N } , Gfx : : Bitmap : : try_load_from_file ( " /res/icons/16x16/app-terminal.png " ) . release_value_but_fixme_should_propagate_errors ( ) , [ & ] ( auto & ) {
2021-08-06 01:05:32 +02:00
Core : : Process : : spawn ( " /bin/Terminal " ) ;
2019-11-20 21:19:17 +01:00
} ) ) ;
2020-12-30 13:42:16 +01:00
2021-05-01 10:45:39 +02:00
file_menu . add_action ( open_settings_action ) ;
file_menu . add_separator ( ) ;
file_menu . add_action ( GUI : : CommonActions : : make_quit_action ( [ ] ( auto & ) {
2020-10-06 19:12:42 +02:00
dbgln ( " Terminal: Quit menu activated! " ) ;
2020-07-04 16:52:01 +02:00
GUI : : Application : : the ( ) - > quit ( ) ;
2019-02-12 14:09:48 +01:00
} ) ) ;
2019-02-11 15:37:12 +01:00
2021-07-21 21:21:03 +02:00
auto & edit_menu = window - > add_menu ( " &Edit " ) ;
2020-04-04 12:18:40 +02:00
edit_menu . add_action ( terminal . copy_action ( ) ) ;
edit_menu . add_action ( terminal . paste_action ( ) ) ;
2020-12-28 11:30:19 +02:00
edit_menu . add_separator ( ) ;
2021-11-06 16:25:29 +01:00
edit_menu . add_action ( GUI : : Action : : create ( " &Find... " , { Mod_Ctrl | Mod_Shift , Key_F } , Gfx : : Bitmap : : try_load_from_file ( " /res/icons/16x16/find.png " ) . release_value_but_fixme_should_propagate_errors ( ) ,
2020-12-28 11:30:19 +02:00
[ & ] ( auto & ) {
2021-02-16 16:20:39 +01:00
if ( ! find_window )
2020-12-28 11:30:19 +02:00
find_window = create_find_window ( terminal ) ;
find_window - > show ( ) ;
find_window - > move_to_front ( ) ;
} ) ) ;
2019-11-20 21:33:23 +01:00
2021-07-21 21:21:03 +02:00
auto & view_menu = window - > add_menu ( " &View " ) ;
2021-03-13 03:49:39 +00:00
view_menu . add_action ( GUI : : CommonActions : : make_fullscreen_action ( [ & ] ( auto & ) {
window - > set_fullscreen ( ! window - > is_fullscreen ( ) ) ;
} ) ) ;
2020-07-05 23:34:02 +02:00
view_menu . add_action ( terminal . clear_including_history_action ( ) ) ;
2020-12-30 17:52:19 +01:00
view_menu . add_separator ( ) ;
view_menu . add_action ( pick_font_action ) ;
2019-02-12 08:39:19 +01:00
2021-07-21 21:21:03 +02:00
auto & help_menu = window - > add_menu ( " &Help " ) ;
2021-01-03 08:39:46 +00:00
help_menu . add_action ( GUI : : CommonActions : : make_help_action ( [ ] ( auto & ) {
Desktop : : Launcher : : open ( URL : : create_with_file_protocol ( " /usr/share/man/man1/Terminal.md " ) , " /bin/Help " ) ;
} ) ) ;
2021-01-04 23:51:49 +01:00
help_menu . add_action ( GUI : : CommonActions : : make_about_action ( " Terminal " , app_icon , window ) ) ;
2019-02-11 15:37:12 +01:00
2021-06-02 12:28:52 +02:00
window - > on_close = [ & ] ( ) {
if ( find_window )
find_window - > close ( ) ;
if ( settings_window )
settings_window - > close ( ) ;
} ;
2021-11-23 10:59:50 +01:00
TRY ( Core : : System : : unveil ( " /res " , " r " ) ) ;
TRY ( Core : : System : : unveil ( " /bin " , " r " ) ) ;
TRY ( Core : : System : : unveil ( " /bin/Terminal " , " x " ) ) ;
TRY ( Core : : System : : unveil ( " /bin/utmpupdate " , " x " ) ) ;
TRY ( Core : : System : : unveil ( " /etc/FileIconProvider.ini " , " r " ) ) ;
TRY ( Core : : System : : unveil ( " /tmp/portal/launch " , " rw " ) ) ;
TRY ( Core : : System : : unveil ( " /tmp/portal/config " , " rw " ) ) ;
TRY ( Core : : System : : unveil ( nullptr , nullptr ) ) ;
2020-01-21 12:12:15 +01:00
2021-06-12 11:59:00 +02:00
window - > show ( ) ;
2020-09-06 16:14:46 +02:00
int result = app - > exec ( ) ;
2020-10-06 19:12:42 +02:00
dbgln ( " Exiting terminal, updating utmp " ) ;
2020-09-06 16:14:46 +02:00
utmp_update ( pts_name , 0 , false ) ;
return result ;
2019-01-15 04:30:55 +01:00
}